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