Annotation of src/usr.bin/last/last.c, Revision 1.1
1.1 ! deraadt 1: /* $NetBSD: last.c,v 1.6 1994/12/24 16:49:02 cgd Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1987, 1993, 1994
! 5: * The Regents of the University of California. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by the University of
! 18: * California, Berkeley and its contributors.
! 19: * 4. Neither the name of the University nor the names of its contributors
! 20: * may be used to endorse or promote products derived from this software
! 21: * without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: */
! 35:
! 36: #ifndef lint
! 37: static char copyright[] =
! 38: "@(#) Copyright (c) 1987, 1993, 1994\n\
! 39: The Regents of the University of California. All rights reserved.\n";
! 40: #endif /* not lint */
! 41:
! 42: #ifndef lint
! 43: #if 0
! 44: static char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94";
! 45: #endif
! 46: static char rcsid[] = "$NetBSD: last.c,v 1.6 1994/12/24 16:49:02 cgd Exp $";
! 47: #endif /* not lint */
! 48:
! 49: #include <sys/param.h>
! 50: #include <sys/stat.h>
! 51:
! 52: #include <err.h>
! 53: #include <fcntl.h>
! 54: #include <paths.h>
! 55: #include <signal.h>
! 56: #include <stdio.h>
! 57: #include <stdlib.h>
! 58: #include <string.h>
! 59: #include <time.h>
! 60: #include <tzfile.h>
! 61: #include <unistd.h>
! 62: #include <utmp.h>
! 63:
! 64: #define NO 0 /* false/no */
! 65: #define YES 1 /* true/yes */
! 66:
! 67: static struct utmp buf[1024]; /* utmp read buffer */
! 68:
! 69: typedef struct arg {
! 70: char *name; /* argument */
! 71: #define HOST_TYPE -2
! 72: #define TTY_TYPE -3
! 73: #define USER_TYPE -4
! 74: int type; /* type of arg */
! 75: struct arg *next; /* linked list pointer */
! 76: } ARG;
! 77: ARG *arglist; /* head of linked list */
! 78:
! 79: typedef struct ttytab {
! 80: time_t logout; /* log out time */
! 81: char tty[UT_LINESIZE + 1]; /* terminal name */
! 82: struct ttytab *next; /* linked list pointer */
! 83: } TTY;
! 84: TTY *ttylist; /* head of linked list */
! 85:
! 86: static time_t currentout; /* current logout value */
! 87: static long maxrec; /* records to display */
! 88: static char *file = _PATH_WTMP; /* wtmp file */
! 89:
! 90: void addarg __P((int, char *));
! 91: TTY *addtty __P((char *));
! 92: void hostconv __P((char *));
! 93: void onintr __P((int));
! 94: char *ttyconv __P((char *));
! 95: int want __P((struct utmp *, int));
! 96: void wtmp __P((void));
! 97:
! 98: int
! 99: main(argc, argv)
! 100: int argc;
! 101: char *argv[];
! 102: {
! 103: extern int optind;
! 104: extern char *optarg;
! 105: int ch;
! 106: char *p;
! 107:
! 108: maxrec = -1;
! 109: while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF)
! 110: switch (ch) {
! 111: case '0': case '1': case '2': case '3': case '4':
! 112: case '5': case '6': case '7': case '8': case '9':
! 113: /*
! 114: * kludge: last was originally designed to take
! 115: * a number after a dash.
! 116: */
! 117: if (maxrec == -1) {
! 118: p = argv[optind - 1];
! 119: if (p[0] == '-' && p[1] == ch && !p[2])
! 120: maxrec = atol(++p);
! 121: else
! 122: maxrec = atol(argv[optind] + 1);
! 123: if (!maxrec)
! 124: exit(0);
! 125: }
! 126: break;
! 127: case 'f':
! 128: file = optarg;
! 129: break;
! 130: case 'h':
! 131: hostconv(optarg);
! 132: addarg(HOST_TYPE, optarg);
! 133: break;
! 134: case 't':
! 135: addarg(TTY_TYPE, ttyconv(optarg));
! 136: break;
! 137: case '?':
! 138: default:
! 139: (void)fprintf(stderr,
! 140: "usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n");
! 141: exit(1);
! 142: }
! 143:
! 144: if (argc) {
! 145: setlinebuf(stdout);
! 146: for (argv += optind; *argv; ++argv) {
! 147: #define COMPATIBILITY
! 148: #ifdef COMPATIBILITY
! 149: /* code to allow "last p5" to work */
! 150: addarg(TTY_TYPE, ttyconv(*argv));
! 151: #endif
! 152: addarg(USER_TYPE, *argv);
! 153: }
! 154: }
! 155: wtmp();
! 156: exit(0);
! 157: }
! 158:
! 159: /*
! 160: * wtmp --
! 161: * read through the wtmp file
! 162: */
! 163: void
! 164: wtmp()
! 165: {
! 166: struct utmp *bp; /* current structure */
! 167: TTY *T; /* tty list entry */
! 168: struct stat stb; /* stat of file for size */
! 169: time_t bl, delta; /* time difference */
! 170: int bytes, wfd;
! 171: char *ct, *crmsg;
! 172:
! 173: if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
! 174: err(1, "%s", file);
! 175: bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
! 176:
! 177: (void)time(&buf[0].ut_time);
! 178: (void)signal(SIGINT, onintr);
! 179: (void)signal(SIGQUIT, onintr);
! 180:
! 181: while (--bl >= 0) {
! 182: if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
! 183: (bytes = read(wfd, buf, sizeof(buf))) == -1)
! 184: err(1, "%s", file);
! 185: for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
! 186: /*
! 187: * if the terminal line is '~', the machine stopped.
! 188: * see utmp(5) for more info.
! 189: */
! 190: if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
! 191: /* everybody just logged out */
! 192: for (T = ttylist; T; T = T->next)
! 193: T->logout = -bp->ut_time;
! 194: currentout = -bp->ut_time;
! 195: crmsg = strncmp(bp->ut_name, "shutdown",
! 196: UT_NAMESIZE) ? "crash" : "shutdown";
! 197: if (want(bp, NO)) {
! 198: ct = ctime(&bp->ut_time);
! 199: printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
! 200: UT_NAMESIZE, UT_NAMESIZE,
! 201: bp->ut_name, UT_LINESIZE,
! 202: UT_LINESIZE, bp->ut_line,
! 203: UT_HOSTSIZE, UT_HOSTSIZE,
! 204: bp->ut_host, ct, ct + 11);
! 205: if (maxrec != -1 && !--maxrec)
! 206: return;
! 207: }
! 208: continue;
! 209: }
! 210: /*
! 211: * if the line is '{' or '|', date got set; see
! 212: * utmp(5) for more info.
! 213: */
! 214: if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
! 215: && !bp->ut_line[1]) {
! 216: if (want(bp, NO)) {
! 217: ct = ctime(&bp->ut_time);
! 218: printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
! 219: UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
! 220: UT_LINESIZE, UT_LINESIZE, bp->ut_line,
! 221: UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
! 222: ct, ct + 11);
! 223: if (maxrec && !--maxrec)
! 224: return;
! 225: }
! 226: continue;
! 227: }
! 228: /* find associated tty */
! 229: for (T = ttylist;; T = T->next) {
! 230: if (!T) {
! 231: /* add new one */
! 232: T = addtty(bp->ut_line);
! 233: break;
! 234: }
! 235: if (!strncmp(T->tty, bp->ut_line, UT_LINESIZE))
! 236: break;
! 237: }
! 238: if (bp->ut_name[0] && want(bp, YES)) {
! 239: ct = ctime(&bp->ut_time);
! 240: printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ",
! 241: UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
! 242: UT_LINESIZE, UT_LINESIZE, bp->ut_line,
! 243: UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
! 244: ct, ct + 11);
! 245: if (!T->logout)
! 246: puts(" still logged in");
! 247: else {
! 248: if (T->logout < 0) {
! 249: T->logout = -T->logout;
! 250: printf("- %s", crmsg);
! 251: }
! 252: else
! 253: printf("- %5.5s",
! 254: ctime(&T->logout)+11);
! 255: delta = T->logout - bp->ut_time;
! 256: if (delta < SECSPERDAY)
! 257: printf(" (%5.5s)\n",
! 258: asctime(gmtime(&delta))+11);
! 259: else
! 260: printf(" (%ld+%5.5s)\n",
! 261: delta / SECSPERDAY,
! 262: asctime(gmtime(&delta))+11);
! 263: }
! 264: if (maxrec != -1 && !--maxrec)
! 265: return;
! 266: }
! 267: T->logout = bp->ut_time;
! 268: }
! 269: }
! 270: ct = ctime(&buf[0].ut_time);
! 271: printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
! 272: }
! 273:
! 274: /*
! 275: * want --
! 276: * see if want this entry
! 277: */
! 278: int
! 279: want(bp, check)
! 280: struct utmp *bp;
! 281: int check;
! 282: {
! 283: ARG *step;
! 284:
! 285: if (check)
! 286: /*
! 287: * when uucp and ftp log in over a network, the entry in
! 288: * the utmp file is the name plus their process id. See
! 289: * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
! 290: */
! 291: if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
! 292: bp->ut_line[3] = '\0';
! 293: else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
! 294: bp->ut_line[4] = '\0';
! 295: if (!arglist)
! 296: return (YES);
! 297:
! 298: for (step = arglist; step; step = step->next)
! 299: switch(step->type) {
! 300: case HOST_TYPE:
! 301: if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
! 302: return (YES);
! 303: break;
! 304: case TTY_TYPE:
! 305: if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
! 306: return (YES);
! 307: break;
! 308: case USER_TYPE:
! 309: if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
! 310: return (YES);
! 311: break;
! 312: }
! 313: return (NO);
! 314: }
! 315:
! 316: /*
! 317: * addarg --
! 318: * add an entry to a linked list of arguments
! 319: */
! 320: void
! 321: addarg(type, arg)
! 322: int type;
! 323: char *arg;
! 324: {
! 325: ARG *cur;
! 326:
! 327: if (!(cur = (ARG *)malloc((u_int)sizeof(ARG))))
! 328: err(1, "malloc failure");
! 329: cur->next = arglist;
! 330: cur->type = type;
! 331: cur->name = arg;
! 332: arglist = cur;
! 333: }
! 334:
! 335: /*
! 336: * addtty --
! 337: * add an entry to a linked list of ttys
! 338: */
! 339: TTY *
! 340: addtty(ttyname)
! 341: char *ttyname;
! 342: {
! 343: TTY *cur;
! 344:
! 345: if (!(cur = (TTY *)malloc((u_int)sizeof(TTY))))
! 346: err(1, "malloc failure");
! 347: cur->next = ttylist;
! 348: cur->logout = currentout;
! 349: memmove(cur->tty, ttyname, UT_LINESIZE);
! 350: return (ttylist = cur);
! 351: }
! 352:
! 353: /*
! 354: * hostconv --
! 355: * convert the hostname to search pattern; if the supplied host name
! 356: * has a domain attached that is the same as the current domain, rip
! 357: * off the domain suffix since that's what login(1) does.
! 358: */
! 359: void
! 360: hostconv(arg)
! 361: char *arg;
! 362: {
! 363: static int first = 1;
! 364: static char *hostdot, name[MAXHOSTNAMELEN];
! 365: char *argdot;
! 366:
! 367: if (!(argdot = strchr(arg, '.')))
! 368: return;
! 369: if (first) {
! 370: first = 0;
! 371: if (gethostname(name, sizeof(name)))
! 372: err(1, "gethostname");
! 373: hostdot = strchr(name, '.');
! 374: }
! 375: if (hostdot && !strcasecmp(hostdot, argdot))
! 376: *argdot = '\0';
! 377: }
! 378:
! 379: /*
! 380: * ttyconv --
! 381: * convert tty to correct name.
! 382: */
! 383: char *
! 384: ttyconv(arg)
! 385: char *arg;
! 386: {
! 387: char *mval;
! 388:
! 389: /*
! 390: * kludge -- we assume that all tty's end with
! 391: * a two character suffix.
! 392: */
! 393: if (strlen(arg) == 2) {
! 394: /* either 6 for "ttyxx" or 8 for "console" */
! 395: if (!(mval = malloc((u_int)8)))
! 396: err(1, "malloc failure");
! 397: if (!strcmp(arg, "co"))
! 398: (void)strcpy(mval, "console");
! 399: else {
! 400: (void)strcpy(mval, "tty");
! 401: (void)strcpy(mval + 3, arg);
! 402: }
! 403: return (mval);
! 404: }
! 405: if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
! 406: return (arg + 5);
! 407: return (arg);
! 408: }
! 409:
! 410: /*
! 411: * onintr --
! 412: * on interrupt, we inform the user how far we've gotten
! 413: */
! 414: void
! 415: onintr(signo)
! 416: int signo;
! 417: {
! 418: char *ct;
! 419:
! 420: ct = ctime(&buf[0].ut_time);
! 421: printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
! 422: if (signo == SIGINT)
! 423: exit(1);
! 424: (void)fflush(stdout); /* fix required for rsh */
! 425: }