1 /*- 2 * Copyright (c) 2002 Tim J. Robbins. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 #include <sys/stat.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <langinfo.h> 37 #include <limits.h> 38 #include <locale.h> 39 #include <paths.h> 40 #include <pwd.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <unistd.h> 46 #include <utmp.h> 47 48 static void heading(void); 49 static void process_utmp(FILE *); 50 static void quick(FILE *); 51 static void row(struct utmp *); 52 static int ttywidth(void); 53 static void usage(void); 54 static void whoami(FILE *); 55 56 static int Hflag; /* Write column headings */ 57 static int mflag; /* Show info about current terminal */ 58 static int qflag; /* "Quick" mode */ 59 static int sflag; /* Show name, line, time */ 60 static int Tflag; /* Show terminal state */ 61 static int uflag; /* Show idle time */ 62 63 int 64 main(int argc, char *argv[]) 65 { 66 int ch; 67 const char *file; 68 FILE *fp; 69 70 setlocale(LC_TIME, ""); 71 72 while ((ch = getopt(argc, argv, "HTabdlmpqrstu")) != -1) { 73 switch (ch) { 74 case 'H': /* Write column headings */ 75 Hflag = 1; 76 break; 77 case 'T': /* Show terminal state */ 78 Tflag = 1; 79 break; 80 case 'm': /* Show info about current terminal */ 81 mflag = 1; 82 break; 83 case 'q': /* "Quick" mode */ 84 qflag = 1; 85 break; 86 case 's': /* Show name, line, time */ 87 sflag = 1; 88 break; 89 case 'u': /* Show idle time */ 90 uflag = 1; 91 break; 92 default: 93 usage(); 94 /*NOTREACHED*/ 95 } 96 } 97 argc -= optind; 98 argv += optind; 99 100 if (argc >= 2 && strcmp(argv[0], "am") == 0 && 101 (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) { 102 /* "who am i" or "who am I", equivalent to -m */ 103 mflag = 1; 104 argc -= 2; 105 argv += 2; 106 } 107 if (argc > 1) 108 usage(); 109 110 if (*argv != NULL) 111 file = *argv; 112 else 113 file = _PATH_UTMP; 114 if ((fp = fopen(file, "r")) == NULL) 115 err(1, "%s", file); 116 117 if (qflag) 118 quick(fp); 119 else { 120 if (sflag) 121 Tflag = uflag = 0; 122 if (Hflag) 123 heading(); 124 if (mflag) 125 whoami(fp); 126 else 127 process_utmp(fp); 128 } 129 130 fclose(fp); 131 132 exit(0); 133 } 134 135 static void 136 usage(void) 137 { 138 139 fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n"); 140 exit(1); 141 } 142 143 static void 144 heading(void) 145 { 146 147 printf("%-*s ", UT_NAMESIZE, "NAME"); 148 if (Tflag) 149 printf("S "); 150 printf("%-*s ", UT_LINESIZE, "LINE"); 151 printf("%-*s ", 12, "TIME"); 152 if (uflag) 153 printf("IDLE "); 154 printf("%-*s", UT_HOSTSIZE, "FROM"); 155 putchar('\n'); 156 } 157 158 static void 159 row(struct utmp *ut) 160 { 161 char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE]; 162 struct stat sb; 163 time_t idle; 164 static int d_first = -1; 165 struct tm *tm; 166 char state; 167 168 if (d_first < 0) 169 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 170 171 if (Tflag || uflag) { 172 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, 173 UT_LINESIZE, ut->ut_line); 174 state = '?'; 175 idle = 0; 176 if (stat(tty, &sb) == 0) { 177 state = sb.st_mode & (S_IWOTH|S_IWGRP) ? 178 '+' : '-'; 179 idle = time(NULL) - sb.st_mtime; 180 } 181 } 182 183 printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name); 184 if (Tflag) 185 printf("%c ", state); 186 printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line); 187 tm = localtime(&ut->ut_time); 188 strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); 189 printf("%-*s ", 12, buf); 190 if (uflag) { 191 if (idle < 60) 192 printf(" . "); 193 else if (idle < 24 * 60 * 60) 194 printf("%02d:%02d ", (int)(idle / 60 / 60), 195 (int)(idle / 60 % 60)); 196 else 197 printf(" old "); 198 } 199 if (*ut->ut_host != '\0') 200 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host); 201 putchar('\n'); 202 } 203 204 static void 205 process_utmp(FILE *fp) 206 { 207 struct utmp ut; 208 209 while (fread(&ut, sizeof(ut), 1, fp) == 1) 210 if (*ut.ut_name != '\0') 211 row(&ut); 212 } 213 214 static void 215 quick(FILE *fp) 216 { 217 struct utmp ut; 218 int col, ncols, num; 219 220 ncols = ttywidth(); 221 col = num = 0; 222 while (fread(&ut, sizeof(ut), 1, fp) == 1) { 223 if (*ut.ut_name == '\0') 224 continue; 225 printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name); 226 if (++col < ncols / (UT_NAMESIZE + 1)) 227 putchar(' '); 228 else { 229 col = 0; 230 putchar('\n'); 231 } 232 num++; 233 } 234 if (col != 0) 235 putchar('\n'); 236 237 printf("# users = %d\n", num); 238 } 239 240 static void 241 whoami(FILE *fp) 242 { 243 struct utmp ut; 244 struct passwd *pwd; 245 const char *name, *p, *tty; 246 247 if ((tty = ttyname(STDIN_FILENO)) == NULL) 248 tty = "tty??"; 249 else if ((p = strrchr(tty, '/')) != NULL) 250 tty = p + 1; 251 252 /* Search utmp for our tty, dump first matching record. */ 253 while (fread(&ut, sizeof(ut), 1, fp) == 1) 254 if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty, 255 UT_LINESIZE) == 0) { 256 row(&ut); 257 return; 258 } 259 260 /* Not found; fill the utmp structure with the information we have. */ 261 memset(&ut, 0, sizeof(ut)); 262 if ((pwd = getpwuid(getuid())) != NULL) 263 name = pwd->pw_name; 264 else 265 name = "?"; 266 strncpy(ut.ut_name, name, UT_NAMESIZE); 267 strncpy(ut.ut_line, tty, UT_LINESIZE); 268 time(&ut.ut_time); 269 row(&ut); 270 } 271 272 static int 273 ttywidth(void) 274 { 275 struct winsize ws; 276 long width; 277 char *cols, *ep; 278 279 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { 280 errno = 0; 281 width = strtol(cols, &ep, 10); 282 if (errno || width <= 0 || width > INT_MAX || ep == cols || 283 *ep != '\0') 284 warnx("invalid COLUMNS environment variable ignored"); 285 else 286 return ((int)cols); 287 } 288 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 289 return (ws.ws_col); 290 291 return (80); 292 } 293