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