last.c revision 74588
1155192Srwatson/* 2155192Srwatson * Copyright (c) 1987, 1993, 1994 3170407Srwatson * The Regents of the University of California. All rights reserved. 4155192Srwatson * 5155192Srwatson * Redistribution and use in source and binary forms, with or without 6155192Srwatson * modification, are permitted provided that the following conditions 7155192Srwatson * are met: 8155192Srwatson * 1. Redistributions of source code must retain the above copyright 9155192Srwatson * notice, this list of conditions and the following disclaimer. 10155192Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11155192Srwatson * notice, this list of conditions and the following disclaimer in the 12155192Srwatson * documentation and/or other materials provided with the distribution. 13155192Srwatson * 3. All advertising materials mentioning features or use of this software 14155192Srwatson * must display the following acknowledgement: 15155192Srwatson * This product includes software developed by the University of 16155192Srwatson * California, Berkeley and its contributors. 17155192Srwatson * 4. Neither the name of the University nor the names of its contributors 18155192Srwatson * may be used to endorse or promote products derived from this software 19155192Srwatson * without specific prior written permission. 20155192Srwatson * 21155192Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22155192Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23155192Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24155192Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25155192Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26155192Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27155192Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28155192Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29155192Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30155192Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31155192Srwatson * SUCH DAMAGE. 32155192Srwatson * 33155192Srwatson * $FreeBSD: head/usr.bin/last/last.c 74588 2001-03-21 19:08:01Z ache $ 34155192Srwatson */ 35155192Srwatson 36155192Srwatson#ifndef lint 37155192Srwatsonstatic char copyright[] = 38155192Srwatson"@(#) Copyright (c) 1987, 1993, 1994\n\ 39155192Srwatson The Regents of the University of California. All rights reserved.\n"; 40155192Srwatson#endif /* not lint */ 41155192Srwatson 42155192Srwatson#ifndef lint 43155192Srwatsonstatic char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 44155192Srwatson#endif /* not lint */ 45164033Srwatson 46155192Srwatson#include <sys/param.h> 47155192Srwatson#include <sys/stat.h> 48155192Srwatson 49155192Srwatson#include <err.h> 50155192Srwatson#include <fcntl.h> 51155192Srwatson#include <langinfo.h> 52171144Srwatson#include <locale.h> 53155192Srwatson#include <paths.h> 54155192Srwatson#include <signal.h> 55155192Srwatson#include <stdio.h> 56155192Srwatson#include <stdlib.h> 57155192Srwatson#include <string.h> 58155192Srwatson#include <time.h> 59155192Srwatson#include <unistd.h> 60155192Srwatson#include <utmp.h> 61155192Srwatson#include <sys/queue.h> 62155406Srwatson 63156291Srwatson#define NO 0 /* false/no */ 64155406Srwatson#define YES 1 /* true/yes */ 65155406Srwatson 66155192Srwatsonstatic struct utmp buf[1024]; /* utmp read buffer */ 67155192Srwatson 68155192Srwatsontypedef struct arg { 69155192Srwatson char *name; /* argument */ 70155192Srwatson#define HOST_TYPE -2 71155192Srwatson#define TTY_TYPE -3 72155406Srwatson#define USER_TYPE -4 73155406Srwatson int type; /* type of arg */ 74156888Srwatson struct arg *next; /* linked list pointer */ 75170407Srwatson} ARG; 76155192SrwatsonARG *arglist; /* head of linked list */ 77155192Srwatson 78155192SrwatsonLIST_HEAD(ttylisthead, ttytab) ttylist; 79155192Srwatson 80171144Srwatsonstruct ttytab { 81171144Srwatson time_t logout; /* log out time */ 82171144Srwatson char tty[UT_LINESIZE + 1]; /* terminal name */ 83155192Srwatson LIST_ENTRY(ttytab) list; 84170196Srwatson}; 85170196Srwatson 86170196Srwatsonstatic long currentout, /* current logout value */ 87155192Srwatson maxrec; /* records to display */ 88155192Srwatsonstatic char *file = _PATH_WTMP; /* wtmp file */ 89156889Srwatsonstatic int sflag = 0; /* show delta in seconds */ 90156889Srwatsonstatic int width = 5; /* show seconds in delta */ 91155192Srwatsonstatic int d_first; 92155192Srwatson 93162176Srwatsonvoid addarg __P((int, char *)); 94162176Srwatsonvoid hostconv __P((char *)); 95155192Srwatsonvoid onintr __P((int)); 96156889Srwatsonchar *ttyconv __P((char *)); 97156889Srwatsonint want __P((struct utmp *)); 98161813Swsalamonvoid wtmp __P((void)); 99161813Swsalamon 100155192Srwatsonvoid 101155192Srwatsonusage(void) 102155192Srwatson{ 103155192Srwatson (void)fprintf(stderr, 104156889Srwatson "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n"); 105155192Srwatson exit(1); 106155192Srwatson} 107170691Srwatson 108155192Srwatsonint 109156889Srwatsonmain(argc, argv) 110155192Srwatson int argc; 111155192Srwatson char *argv[]; 112155192Srwatson{ 113155192Srwatson int ch; 114156889Srwatson char *p; 115155192Srwatson 116155192Srwatson (void) setlocale(LC_TIME, ""); 117155192Srwatson d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 118155192Srwatson 119155192Srwatson maxrec = -1; 120156889Srwatson while ((ch = getopt(argc, argv, "0123456789f:h:st:w")) != -1) 121155192Srwatson switch (ch) { 122155192Srwatson case '0': case '1': case '2': case '3': case '4': 123170196Srwatson case '5': case '6': case '7': case '8': case '9': 124170196Srwatson /* 125170196Srwatson * kludge: last was originally designed to take 126170196Srwatson * a number after a dash. 127170196Srwatson */ 128170196Srwatson if (maxrec == -1) { 129155192Srwatson p = argv[optind - 1]; 130156889Srwatson if (p[0] == '-' && p[1] == ch && !p[2]) 131156889Srwatson maxrec = atol(++p); 132156889Srwatson else 133155192Srwatson maxrec = atol(argv[optind] + 1); 134155192Srwatson if (!maxrec) 135155192Srwatson exit(0); 136155192Srwatson } 137156889Srwatson break; 138155192Srwatson case 'f': 139155192Srwatson file = optarg; 140170196Srwatson break; 141170196Srwatson case 'h': 142155192Srwatson hostconv(optarg); 143159261Srwatson addarg(HOST_TYPE, optarg); 144155192Srwatson break; 145155192Srwatson case 's': 146159261Srwatson sflag++; /* Show delta as seconds */ 147159261Srwatson break; 148159261Srwatson case 't': 149155192Srwatson addarg(TTY_TYPE, ttyconv(optarg)); 150159261Srwatson break; 151155192Srwatson case 'w': 152156889Srwatson width = 8; 153156889Srwatson break; 154170196Srwatson case '?': 155170196Srwatson default: 156155192Srwatson usage(); 157156889Srwatson } 158155192Srwatson 159155192Srwatson if (sflag && width == 8) usage(); 160155406Srwatson 161155192Srwatson if (argc) { 162155406Srwatson setlinebuf(stdout); 163155406Srwatson for (argv += optind; *argv; ++argv) { 164155406Srwatson#define COMPATIBILITY 165155406Srwatson#ifdef COMPATIBILITY 166155406Srwatson /* code to allow "last p5" to work */ 167155406Srwatson addarg(TTY_TYPE, ttyconv(*argv)); 168155406Srwatson#endif 169155406Srwatson addarg(USER_TYPE, *argv); 170155406Srwatson } 171155406Srwatson } 172155406Srwatson wtmp(); 173155406Srwatson exit(0); 174155406Srwatson} 175155406Srwatson 176155406Srwatson/* 177155406Srwatson * wtmp -- 178155406Srwatson * read through the wtmp file 179155406Srwatson */ 180155406Srwatsonvoid 181155406Srwatsonwtmp() 182155406Srwatson{ 183170407Srwatson struct utmp *bp; /* current structure */ 184170407Srwatson struct ttytab *tt, *ttx; /* ttylist entry */ 185155406Srwatson struct stat stb; /* stat of file for size */ 186170407Srwatson long bl; 187170407Srwatson time_t delta; /* time difference */ 188155406Srwatson int bytes, wfd; 189155406Srwatson char *crmsg; 190155406Srwatson char ct[80]; 191155192Srwatson struct tm *tm; 192155406Srwatson 193155192Srwatson LIST_INIT(&ttylist); 194155406Srwatson 195155192Srwatson if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 196155406Srwatson err(1, "%s", file); 197155406Srwatson bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 198155406Srwatson 199155406Srwatson (void)time(&buf[0].ut_time); 200155192Srwatson (void)signal(SIGINT, onintr); 201155406Srwatson (void)signal(SIGQUIT, onintr); 202155192Srwatson 203155406Srwatson while (--bl >= 0) { 204155192Srwatson if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 205155406Srwatson (bytes = read(wfd, buf, sizeof(buf))) == -1) 206155192Srwatson err(1, "%s", file); 207161813Swsalamon for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 208161813Swsalamon /* 209161813Swsalamon * if the terminal line is '~', the machine stopped. 210161813Swsalamon * see utmp(5) for more info. 211155192Srwatson */ 212155192Srwatson if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 213155192Srwatson /* everybody just logged out */ 214155192Srwatson for (tt = LIST_FIRST(&ttylist); tt;) { 215155192Srwatson LIST_REMOVE(tt, list); 216155192Srwatson ttx = tt; 217155192Srwatson tt = LIST_NEXT(tt, list); 218155192Srwatson free(ttx); 219155192Srwatson } 220155192Srwatson currentout = -bp->ut_time; 221155192Srwatson crmsg = strncmp(bp->ut_name, "shutdown", 222155192Srwatson UT_NAMESIZE) ? "crash" : "shutdown"; 223155192Srwatson if (want(bp)) { 224155192Srwatson tm = localtime(&bp->ut_time); 225155192Srwatson (void) strftime(ct, sizeof(ct), 226155192Srwatson d_first ? "%a %e %b %R" : 227161813Swsalamon "%a %b %e %R", 228161813Swsalamon tm); 229155192Srwatson printf("%-*.*s %-*.*s %-*.*s %s\n", 230170196Srwatson UT_NAMESIZE, UT_NAMESIZE, 231156889Srwatson bp->ut_name, UT_LINESIZE, 232173142Srwatson UT_LINESIZE, bp->ut_line, 233173142Srwatson UT_HOSTSIZE, UT_HOSTSIZE, 234155192Srwatson bp->ut_host, ct); 235155192Srwatson if (maxrec != -1 && !--maxrec) 236155192Srwatson return; 237155192Srwatson } 238155192Srwatson continue; 239155192Srwatson } 240155192Srwatson /* 241155192Srwatson * if the line is '{' or '|', date got set; see 242155192Srwatson * utmp(5) for more info. 243155192Srwatson */ 244159261Srwatson if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 245159261Srwatson && !bp->ut_line[1]) { 246155192Srwatson if (want(bp)) { 247155192Srwatson tm = localtime(&bp->ut_time); 248159266Srwatson (void) strftime(ct, sizeof(ct), 249155406Srwatson d_first ? "%a %e %b %R" : 250155406Srwatson "%a %b %e %R", 251155406Srwatson tm); 252155192Srwatson printf("%-*.*s %-*.*s %-*.*s %s\n", 253155192Srwatson UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 254155192Srwatson UT_LINESIZE, UT_LINESIZE, bp->ut_line, 255155192Srwatson UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 256155192Srwatson ct); 257155192Srwatson if (maxrec && !--maxrec) 258155192Srwatson return; 259155192Srwatson } 260155192Srwatson continue; 261156888Srwatson } 262156888Srwatson if (bp->ut_name[0] == '\0' || want(bp)) { 263155192Srwatson /* find associated tty */ 264155192Srwatson LIST_FOREACH(tt, &ttylist, list) 265155192Srwatson if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 266155192Srwatson break; 267155192Srwatson 268155192Srwatson if (tt == NULL) { 269155192Srwatson /* add new one */ 270155192Srwatson tt = malloc(sizeof(struct ttytab)); 271155192Srwatson if (tt == NULL) 272155192Srwatson err(1, "malloc failure"); 273155192Srwatson tt->logout = currentout; 274155192Srwatson strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 275155192Srwatson LIST_INSERT_HEAD(&ttylist, tt, list); 276155192Srwatson } 277155192Srwatson 278155192Srwatson if (bp->ut_name[0]) { 279155192Srwatson /* 280155192Srwatson * when uucp and ftp log in over a network, the entry in 281155192Srwatson * the utmp file is the name plus their process id. See 282169896Srwatson * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 283155192Srwatson */ 284155192Srwatson if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 285155192Srwatson bp->ut_line[3] = '\0'; 286155192Srwatson else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 287155192Srwatson bp->ut_line[4] = '\0'; 288155192Srwatson tm = localtime(&bp->ut_time); 289155192Srwatson (void) strftime(ct, sizeof(ct), 290155192Srwatson d_first ? "%a %e %b %R" : 291155192Srwatson "%a %b %e %R", 292155192Srwatson tm); 293155192Srwatson printf("%-*.*s %-*.*s %-*.*s %s ", 294155192Srwatson UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 295155192Srwatson UT_LINESIZE, UT_LINESIZE, bp->ut_line, 296155192Srwatson UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 297155192Srwatson ct); 298155192Srwatson if (!tt->logout) 299155192Srwatson puts(" still logged in"); 300155192Srwatson else { 301155192Srwatson if (tt->logout < 0) { 302155192Srwatson tt->logout = -tt->logout; 303155406Srwatson printf("- %s", crmsg); 304155406Srwatson } 305155406Srwatson else { 306155406Srwatson tm = localtime(&tt->logout); 307155406Srwatson (void) strftime(ct, sizeof(ct), "%R", tm); 308155192Srwatson printf("- %s", ct); 309155192Srwatson } 310165604Srwatson delta = tt->logout - bp->ut_time; 311165604Srwatson if ( sflag ) { 312165604Srwatson printf(" (%8lu)\n", 313155192Srwatson delta); 314155406Srwatson } else { 315155406Srwatson tm = gmtime(&delta); 316155192Srwatson (void) strftime(ct, sizeof(ct), 317155192Srwatson width >= 8 ? "%T" : "%R", 318155192Srwatson tm); 319155192Srwatson if (delta < 86400) 320155192Srwatson printf(" (%s)\n", ct); 321155192Srwatson else 322155192Srwatson printf(" (%ld+%s)\n", 323155192Srwatson delta / 86400, ct); 324156888Srwatson } 325156888Srwatson } 326156888Srwatson LIST_REMOVE(tt, list); 327156888Srwatson free(tt); 328156888Srwatson if (maxrec != -1 && !--maxrec) 329156888Srwatson return; 330156888Srwatson } else { 331155192Srwatson tt->logout = bp->ut_time; 332155192Srwatson } 333155192Srwatson } 334159269Srwatson } 335159269Srwatson } 336159269Srwatson tm = localtime(&buf[0].ut_time); 337155192Srwatson (void) strftime(ct, sizeof(ct), "\nwtmp begins %c\n", tm); 338155192Srwatson printf("%s", ct); 339155192Srwatson} 340155192Srwatson 341155192Srwatson/* 342155192Srwatson * want -- 343155192Srwatson * see if want this entry 344170196Srwatson */ 345170196Srwatsonint 346155192Srwatsonwant(bp) 347155192Srwatson struct utmp *bp; 348155192Srwatson{ 349155192Srwatson ARG *step; 350155192Srwatson 351156889Srwatson if (!arglist) 352155192Srwatson return (YES); 353155192Srwatson 354155192Srwatson for (step = arglist; step; step = step->next) 355155192Srwatson switch(step->type) { 356155192Srwatson case HOST_TYPE: 357155192Srwatson if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 358155192Srwatson return (YES); 359170196Srwatson break; 360170196Srwatson case TTY_TYPE: 361170196Srwatson if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 362170196Srwatson return (YES); 363155192Srwatson break; 364155192Srwatson case USER_TYPE: 365155192Srwatson if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 366155192Srwatson return (YES); 367155192Srwatson break; 368155192Srwatson } 369155192Srwatson return (NO); 370155192Srwatson} 371155192Srwatson 372155192Srwatson/* 373155192Srwatson * addarg -- 374170585Srwatson * add an entry to a linked list of arguments 375155192Srwatson */ 376155192Srwatsonvoid 377155192Srwatsonaddarg(type, arg) 378155192Srwatson int type; 379159269Srwatson char *arg; 380159269Srwatson{ 381159269Srwatson ARG *cur; 382155192Srwatson 383159269Srwatson if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 384159269Srwatson err(1, "malloc failure"); 385159269Srwatson cur->next = arglist; 386159269Srwatson cur->type = type; 387159269Srwatson cur->name = arg; 388159269Srwatson arglist = cur; 389162380Scsjp} 390162380Scsjp 391155192Srwatson/* 392155192Srwatson * hostconv -- 393155192Srwatson * convert the hostname to search pattern; if the supplied host name 394159275Srwatson * has a domain attached that is the same as the current domain, rip 395155192Srwatson * off the domain suffix since that's what login(1) does. 396155192Srwatson */ 397155192Srwatsonvoid 398155192Srwatsonhostconv(arg) 399155192Srwatson char *arg; 400155192Srwatson{ 401155192Srwatson static int first = 1; 402155192Srwatson static char *hostdot, name[MAXHOSTNAMELEN]; 403155192Srwatson char *argdot; 404155192Srwatson 405155192Srwatson if (!(argdot = strchr(arg, '.'))) 406170196Srwatson return; 407155192Srwatson if (first) { 408155192Srwatson first = 0; 409155192Srwatson if (gethostname(name, sizeof(name))) 410159275Srwatson err(1, "gethostname"); 411155192Srwatson hostdot = strchr(name, '.'); 412155192Srwatson } 413156889Srwatson if (hostdot && !strcasecmp(hostdot, argdot)) 414155192Srwatson *argdot = '\0'; 415170182Srwatson} 416170182Srwatson 417155192Srwatson/* 418170182Srwatson * ttyconv -- 419159261Srwatson * convert tty to correct name. 420155192Srwatson */ 421155192Srwatsonchar * 422155192Srwatsonttyconv(arg) 423155192Srwatson char *arg; 424159261Srwatson{ 425155192Srwatson char *mval; 426155192Srwatson 427155192Srwatson /* 428155192Srwatson * kludge -- we assume that all tty's end with 429155192Srwatson * a two character suffix. 430155192Srwatson */ 431155192Srwatson if (strlen(arg) == 2) { 432155192Srwatson /* either 6 for "ttyxx" or 8 for "console" */ 433155192Srwatson if (!(mval = malloc((u_int)8))) 434155192Srwatson err(1, "malloc failure"); 435155192Srwatson if (!strcmp(arg, "co")) 436155192Srwatson (void)strcpy(mval, "console"); 437155192Srwatson else { 438159269Srwatson (void)strcpy(mval, "tty"); 439159269Srwatson (void)strcpy(mval + 3, arg); 440159269Srwatson } 441155192Srwatson return (mval); 442155192Srwatson } 443155192Srwatson if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 444155192Srwatson return (arg + 5); 445155192Srwatson return (arg); 446155192Srwatson} 447155192Srwatson 448155192Srwatson/* 449162950Srwatson * onintr -- 450162950Srwatson * on interrupt, we inform the user how far we've gotten 451155192Srwatson */ 452155192Srwatsonvoid 453155192Srwatsononintr(signo) 454155192Srwatson int signo; 455159269Srwatson{ 456159269Srwatson char ct[80]; 457155192Srwatson struct tm *tm; 458155192Srwatson 459155192Srwatson tm = localtime(&buf[0].ut_time); 460155192Srwatson (void) strftime(ct, sizeof(ct), 461155192Srwatson d_first ? "%a %e %b %R" : "%a %b %e %R", 462155192Srwatson tm); 463170407Srwatson printf("\ninterrupted %s\n", ct); 464159269Srwatson if (signo == SIGINT) 465155192Srwatson exit(1); 466155192Srwatson (void)fflush(stdout); /* fix required for rsh */ 467170407Srwatson} 468156889Srwatson