1/* 2 * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved 3 * 4 * Copyright (c) 1987, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * The NEXTSTEP Software License Agreement specifies the terms 8 * and conditions for redistribution. 9 * 10 * @(#)last.c 8.2 (Berkeley) 4/2/94 11 */ 12 13 14#ifndef lint 15static const char copyright[] = 16"@(#) Copyright (c) 1987, 1993, 1994\n\ 17 The Regents of the University of California. All rights reserved.\n"; 18#endif /* not lint */ 19 20#include <sys/param.h> 21#include <sys/stat.h> 22 23#include <err.h> 24#include <fcntl.h> 25#include <paths.h> 26#include <signal.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <time.h> 31#include <tzfile.h> 32#include <unistd.h> 33#include <utmpx.h> 34#include <ctype.h> 35 36#define NO 0 /* false/no */ 37#define YES 1 /* true/yes */ 38 39static const struct utmpx *prev; /* previous utmpx structure */ 40 41/* values from utmp.h, used for print formatting */ 42#define UT_NAMESIZE 8 /* old utmp.h value */ 43#define UT_LINESIZE 8 44#define UT_HOSTSIZE 16 45 46typedef struct arg { 47 const char *name; /* argument */ 48#define HOST_TYPE -2 49#define TTY_TYPE -3 50#define USER_TYPE -4 51 int type; /* type of arg */ 52 struct arg *next; /* linked list pointer */ 53} ARG; 54ARG *arglist; /* head of linked list */ 55 56typedef struct ttytab { 57 long logout; /* log out time */ 58 char id[_UTX_IDSIZE]; /* terminal id */ 59 pid_t pid; 60 struct ttytab *next; /* linked list pointer */ 61} TTY; 62TTY *ttylist; /* head of linked list */ 63 64static const char *crmsg; /* cause of last reboot */ 65static long currentout, /* current logout value */ 66 maxrec; /* records to display */ 67time_t now; 68 69void addarg __P((int, const char *)); 70TTY *addtty __P((const char *, pid_t)); 71void hostconv __P((const char *)); 72void onintr __P((int)); 73const char *ttyconv __P((const char *)); 74int want __P((struct utmpx *, int)); 75void wtmp __P((void)); 76 77int 78main(argc, argv) 79 int argc; 80 char *argv[]; 81{ 82 extern int optind; 83 extern char *optarg; 84 int ch; 85 char *p; 86 char *myname = *argv; 87 88 if ((p = strrchr(myname, '/')) != NULL) 89 myname = p + 1; 90 maxrec = -1; 91 while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF) 92 switch (ch) { 93 case '0': case '1': case '2': case '3': case '4': 94 case '5': case '6': case '7': case '8': case '9': 95 /* 96 * kludge: last was originally designed to take 97 * a number after a dash. 98 */ 99 if (maxrec == -1) { 100 p = argv[optind - 1]; 101 if (p[0] == '-' && p[1] == ch && !p[2]) 102 maxrec = atol(++p); 103 else 104 maxrec = atol(argv[optind] + 1); 105 if (!maxrec) 106 exit(0); 107 } 108 break; 109 case 'h': 110 hostconv(optarg); 111 addarg(HOST_TYPE, optarg); 112 break; 113 case 't': 114 addarg(TTY_TYPE, ttyconv(optarg)); 115 break; 116 case 'f': 117 warnx("-f is unsupported"); 118 break; 119 case '?': 120 default: 121 (void)fprintf(stderr, 122 "usage: last [-#] [-t tty] [-h hostname] [user ...]\n"); 123 exit(1); 124 } 125 126 if (argc) { 127 setlinebuf(stdout); 128 for (argv += optind; *argv; ++argv) { 129#define COMPATIBILITY 130#ifdef COMPATIBILITY 131 /* code to allow "last p5" to work */ 132 addarg(TTY_TYPE, ttyconv(*argv)); 133#endif 134 addarg(USER_TYPE, *argv); 135 } 136 } 137 wtmp(); 138 exit(0); 139} 140 141/* 142 * wtmp -- 143 * read through the wtmp file 144 */ 145 146void 147wtmp() 148{ 149 150 struct utmpx *bp; /* current structure */ 151 TTY *T; /* tty list entry */ 152 long delta; /* time difference */ 153 char *ct; 154 155 (void)time(&now); 156 (void)signal(SIGINT, onintr); 157 (void)signal(SIGQUIT, onintr); 158 159 setutxent_wtmp(0); /* zero means reverse chronological order */ 160 while ((bp = getutxent_wtmp()) != NULL) { 161 prev = bp; 162 switch(bp->ut_type) { 163 case BOOT_TIME: 164 case SHUTDOWN_TIME: 165 /* everybody just logged out */ 166 for (T = ttylist; T; T = T->next) 167 T->logout = -bp->ut_tv.tv_sec; 168 currentout = -bp->ut_tv.tv_sec; 169 crmsg = bp->ut_type == BOOT_TIME ? "crash" : "shutdown"; 170 if (want(bp, NO)) { 171 ct = ctime(&bp->ut_tv.tv_sec); 172 printf("%-*s %-*s %-*.*s %10.10s %5.5s \n", 173 UT_NAMESIZE, bp->ut_type == BOOT_TIME ? "reboot" : "shutdown", 174 UT_LINESIZE, "~", 175 UT_HOSTSIZE, _UTX_HOSTSIZE, 176 bp->ut_host, ct, ct + 11); 177 if (maxrec != -1 && !--maxrec) 178 return; 179 } 180 continue; 181 case OLD_TIME: 182 case NEW_TIME: 183 if (want(bp, NO)) { 184 ct = ctime(&bp->ut_tv.tv_sec); 185 printf("%-*s %-*s %-*.*s %10.10s %5.5s \n", 186 UT_NAMESIZE, "date", 187 UT_LINESIZE, bp->ut_type == OLD_TIME ? "|" : "{", 188 UT_HOSTSIZE, _UTX_HOSTSIZE, bp->ut_host, 189 ct, ct + 11); 190 if (maxrec && !--maxrec) 191 return; 192 } 193 continue; 194 case USER_PROCESS: 195 case DEAD_PROCESS: 196 /* find associated tty */ 197 for (T = ttylist;; T = T->next) { 198 if (!T) { 199 /* add new one */ 200 T = addtty(bp->ut_id, bp->ut_pid); 201 break; 202 } 203 if (!memcmp(T->id, bp->ut_id, _UTX_IDSIZE) && T->pid == bp->ut_pid) 204 break; 205 } 206 if (bp->ut_type != DEAD_PROCESS && want(bp, YES)) { 207 ct = ctime(&bp->ut_tv.tv_sec); 208 printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ", 209 UT_NAMESIZE, _UTX_USERSIZE, bp->ut_user, 210 UT_LINESIZE, _UTX_LINESIZE, bp->ut_line, 211 UT_HOSTSIZE, _UTX_HOSTSIZE, bp->ut_host, 212 ct, ct + 11); 213 if (!T->logout) 214 puts(" still logged in"); 215 else { 216 if (T->logout < 0) { 217 T->logout = -T->logout; 218 printf("- %s", crmsg); 219 } 220 else 221 printf("- %5.5s", 222 ctime(&T->logout)+11); 223 delta = T->logout - bp->ut_tv.tv_sec; 224 if (delta < SECSPERDAY) 225 printf(" (%5.5s)\n", 226 asctime(gmtime(&delta))+11); 227 else 228 printf(" (%ld+%5.5s)\n", 229 delta / SECSPERDAY, 230 asctime(gmtime(&delta))+11); 231 } 232 if (maxrec != -1 && !--maxrec) 233 return; 234 } 235 T->logout = bp->ut_tv.tv_sec; 236 continue; 237 } 238 } 239 endutxent_wtmp(); 240 ct = ctime(prev ? &prev->ut_tv.tv_sec : &now); 241 printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11); 242} 243 244/* 245 * want -- 246 * see if want this entry 247 */ 248int 249want(bp, check) 250 struct utmpx *bp; 251 int check; 252{ 253 ARG *step; 254 255 if (!arglist) 256 return (YES); 257 258 for (step = arglist; step; step = step->next) 259 switch(step->type) { 260 case HOST_TYPE: 261 if (!strncasecmp(step->name, bp->ut_host, _UTX_HOSTSIZE)) 262 return (YES); 263 break; 264 case TTY_TYPE: 265 { 266 char *line = bp->ut_line; 267 if (check) { 268 /* 269 * when uucp and ftp log in over a network, the entry in 270 * the utmpx file is the name plus their process id. See 271 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 272 */ 273 if (!strncmp(line, "ftp", sizeof("ftp") - 1)) 274 line = "ftp"; 275 else if (!strncmp(line, "uucp", sizeof("uucp") - 1)) 276 line = "uucp"; 277 } 278 if (!strncmp(step->name, line, _UTX_LINESIZE)) 279 return (YES); 280 break; 281 } 282 case USER_TYPE: 283 if (bp->ut_type == BOOT_TIME && !strncmp(step->name, "reboot", _UTX_USERSIZE)) 284 return (YES); 285 if (bp->ut_type == SHUTDOWN_TIME && !strncmp(step->name, "shutdown", _UTX_USERSIZE)) 286 return (YES); 287 if (!strncmp(step->name, bp->ut_user, _UTX_USERSIZE)) 288 return (YES); 289 break; 290 } 291 return (NO); 292} 293 294/* 295 * addarg -- 296 * add an entry to a linked list of arguments 297 */ 298void 299addarg(type, arg) 300 int type; 301 const char *arg; 302{ 303 ARG *cur; 304 305 if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 306 err(1, "malloc failure"); 307 cur->next = arglist; 308 cur->type = type; 309 cur->name = arg; 310 arglist = cur; 311} 312 313/* 314 * addtty -- 315 * add an entry to a linked list of ttys 316 */ 317TTY * 318addtty(id, pid) 319 const char *id; 320 pid_t pid; 321{ 322 TTY *cur; 323 324 if (!(cur = (TTY *)malloc((u_int)sizeof(TTY)))) 325 err(1, "malloc failure"); 326 cur->next = ttylist; 327 cur->logout = currentout; 328 memmove(cur->id, id, _UTX_IDSIZE); 329 cur->pid = pid; 330 return (ttylist = cur); 331} 332 333/* 334 * hostconv -- 335 * convert the hostname to search pattern; if the supplied host name 336 * has a domain attached that is the same as the current domain, rip 337 * off the domain suffix since that's what login(1) does. 338 */ 339void 340hostconv(arg) 341 const char *arg; 342{ 343 static int first = 1; 344 static char *hostdot, name[MAXHOSTNAMELEN]; 345 char *argdot; 346 347 if (!(argdot = strchr(arg, '.'))) 348 return; 349 if (first) { 350 first = 0; 351 if (gethostname(name, sizeof(name))) 352 err(1, "gethostname"); 353 hostdot = strchr(name, '.'); 354 } 355 if (hostdot && !strcasecmp(hostdot, argdot)) 356 *argdot = '\0'; 357} 358 359/* 360 * ttyconv -- 361 * convert tty to correct name. 362 */ 363const char * 364ttyconv(arg) 365 const char *arg; 366{ 367 char *mval; 368 int kludge = 0; 369 int len = strlen(arg); 370 371 /* 372 * kludge -- we assume that most tty's end with 373 * a two character suffix. 374 */ 375 if (len == 2) 376 kludge = 8; /* either 6 for "ttyxx" or 8 for "console"; use largest */ 377 /* 378 * kludge -- we assume cloning ptys names are "ttys" followed by 379 * more than one digit 380 */ 381 else if (len > 2 && *arg == 's') { 382 const char *cp = arg + 1; 383 while(*cp && isdigit(*cp)) 384 cp++; 385 if (*cp == 0) 386 kludge = len + sizeof("tty"); 387 } 388 if (kludge) { 389 if (!(mval = malloc(kludge))) 390 err(1, "malloc failure"); 391 if (!strcmp(arg, "co")) 392 (void)strcpy(mval, "console"); 393 else { 394 (void)strcpy(mval, "tty"); 395 (void)strcpy(mval + sizeof("tty") - 1, arg); 396 } 397 return (mval); 398 } 399 if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 400 return (arg + sizeof(_PATH_DEV) - 1); 401 return (arg); 402} 403 404/* 405 * onintr -- 406 * on interrupt, we inform the user how far we've gotten 407 */ 408void 409onintr(signo) 410 int signo; 411{ 412 char *ct; 413 414 ct = ctime(prev ? &prev->ut_tv.tv_sec : &now); 415 printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11); 416 if (signo == SIGINT) 417 exit(1); 418 (void)fflush(stdout); /* fix required for rsh */ 419} 420