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