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 <timeconv.h> 46 #include <unistd.h> 47 #include <utmp.h> 48 49 static void heading(void); 50 static void process_utmp(FILE *); 51 static void quick(FILE *); 52 static void row(struct utmp *); 53 static int ttywidth(void); 54 static void usage(void); 55 static void whoami(FILE *); 56 57 static int Hflag; /* Write column headings */ 58 static int mflag; /* Show info about current terminal */ 59 static int qflag; /* "Quick" mode */ 60 static int sflag; /* Show name, line, time */ 61 static int Tflag; /* Show terminal state */ 62 static int uflag; /* Show idle time */ 63 64 int 65 main(int argc, char *argv[]) 66 { 67 int ch; 68 const char *file; 69 FILE *fp; 70 71 setlocale(LC_TIME, ""); 72 73 while ((ch = getopt(argc, argv, "HTmqsu")) != -1) { 74 switch (ch) { 75 case 'H': /* Write column headings */ 76 Hflag = 1; 77 break; 78 case 'T': /* Show terminal state */ 79 Tflag = 1; 80 break; 81 case 'm': /* Show info about current terminal */ 82 mflag = 1; 83 break; 84 case 'q': /* "Quick" mode */ 85 qflag = 1; 86 break; 87 case 's': /* Show name, line, time */ 88 sflag = 1; 89 break; 90 case 'u': /* Show idle time */ 91 uflag = 1; 92 break; 93 default: 94 usage(); 95 /*NOTREACHED*/ 96 } 97 } 98 argc -= optind; 99 argv += optind; 100 101 if (argc >= 2 && strcmp(argv[0], "am") == 0 && 102 (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) { 103 /* "who am i" or "who am I", equivalent to -m */ 104 mflag = 1; 105 argc -= 2; 106 argv += 2; 107 } 108 if (argc > 1) 109 usage(); 110 111 if (*argv != NULL) 112 file = *argv; 113 else 114 file = _PATH_UTMP; 115 if ((fp = fopen(file, "r")) == NULL) 116 err(1, "%s", file); 117 118 if (qflag) 119 quick(fp); 120 else { 121 if (sflag) 122 Tflag = uflag = 0; 123 if (Hflag) 124 heading(); 125 if (mflag) 126 whoami(fp); 127 else 128 process_utmp(fp); 129 } 130 131 fclose(fp); 132 133 exit(0); 134 } 135 136 static void 137 usage(void) 138 { 139 140 fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n"); 141 exit(1); 142 } 143 144 static void 145 heading(void) 146 { 147 148 printf("%-*s ", UT_NAMESIZE, "NAME"); 149 if (Tflag) 150 printf("S "); 151 printf("%-*s ", UT_LINESIZE, "LINE"); 152 printf("%-*s ", 12, "TIME"); 153 if (uflag) 154 printf("IDLE "); 155 printf("%-*s", UT_HOSTSIZE, "FROM"); 156 putchar('\n'); 157 } 158 159 static void 160 row(struct utmp *ut) 161 { 162 char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE]; 163 struct stat sb; 164 time_t idle, t; 165 static int d_first = -1; 166 struct tm *tm; 167 char state; 168 169 if (d_first < 0) 170 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 171 172 state = '?'; 173 idle = 0; 174 if (Tflag || uflag) { 175 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, 176 UT_LINESIZE, ut->ut_line); 177 if (stat(tty, &sb) == 0) { 178 state = sb.st_mode & (S_IWOTH|S_IWGRP) ? 179 '+' : '-'; 180 idle = time(NULL) - sb.st_mtime; 181 } 182 } 183 184 printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name); 185 if (Tflag) 186 printf("%c ", state); 187 printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line); 188 t = _time32_to_time(ut->ut_time); 189 tm = localtime(&t); 190 strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); 191 printf("%-*s ", 12, buf); 192 if (uflag) { 193 if (idle < 60) 194 printf(" . "); 195 else if (idle < 24 * 60 * 60) 196 printf("%02d:%02d ", (int)(idle / 60 / 60), 197 (int)(idle / 60 % 60)); 198 else 199 printf(" old "); 200 } 201 if (*ut->ut_host != '\0') 202 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host); 203 putchar('\n'); 204 } 205 206 static void 207 process_utmp(FILE *fp) 208 { 209 struct utmp ut; 210 211 while (fread(&ut, sizeof(ut), 1, fp) == 1) 212 if (*ut.ut_name != '\0') 213 row(&ut); 214 } 215 216 static void 217 quick(FILE *fp) 218 { 219 struct utmp ut; 220 int col, ncols, num; 221 222 ncols = ttywidth(); 223 col = num = 0; 224 while (fread(&ut, sizeof(ut), 1, fp) == 1) { 225 if (*ut.ut_name == '\0') 226 continue; 227 printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name); 228 if (++col < ncols / (UT_NAMESIZE + 1)) 229 putchar(' '); 230 else { 231 col = 0; 232 putchar('\n'); 233 } 234 num++; 235 } 236 if (col != 0) 237 putchar('\n'); 238 239 printf("# users = %d\n", num); 240 } 241 242 static void 243 whoami(FILE *fp) 244 { 245 struct utmp ut; 246 struct passwd *pwd; 247 const char *name, *p, *tty; 248 249 if ((tty = ttyname(STDIN_FILENO)) == NULL) 250 tty = "tty??"; 251 else if ((p = strrchr(tty, '/')) != NULL) 252 tty = p + 1; 253 254 /* Search utmp for our tty, dump first matching record. */ 255 while (fread(&ut, sizeof(ut), 1, fp) == 1) 256 if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty, 257 UT_LINESIZE) == 0) { 258 row(&ut); 259 return; 260 } 261 262 /* Not found; fill the utmp structure with the information we have. */ 263 memset(&ut, 0, sizeof(ut)); 264 if ((pwd = getpwuid(getuid())) != NULL) 265 name = pwd->pw_name; 266 else 267 name = "?"; 268 strncpy(ut.ut_name, name, UT_NAMESIZE); 269 strncpy(ut.ut_line, tty, UT_LINESIZE); 270 ut.ut_time = _time_to_time32(time(NULL)); 271 row(&ut); 272 } 273 274 static int 275 ttywidth(void) 276 { 277 struct winsize ws; 278 long width; 279 char *cols, *ep; 280 281 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { 282 errno = 0; 283 width = strtol(cols, &ep, 10); 284 if (errno || width <= 0 || width > INT_MAX || ep == cols || 285 *ep != '\0') 286 warnx("invalid COLUMNS environment variable ignored"); 287 else 288 return (width); 289 } 290 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 291 return (ws.ws_col); 292 293 return (80); 294 } 295