last.c revision 60938
150276Speter/* 2178866Srafan * Copyright (c) 1987, 1993, 1994 350276Speter * The Regents of the University of California. All rights reserved. 450276Speter * 550276Speter * Redistribution and use in source and binary forms, with or without 650276Speter * modification, are permitted provided that the following conditions 750276Speter * are met: 850276Speter * 1. Redistributions of source code must retain the above copyright 950276Speter * notice, this list of conditions and the following disclaimer. 1050276Speter * 2. Redistributions in binary form must reproduce the above copyright 1150276Speter * notice, this list of conditions and the following disclaimer in the 1250276Speter * documentation and/or other materials provided with the distribution. 1350276Speter * 3. All advertising materials mentioning features or use of this software 1450276Speter * must display the following acknowledgement: 1550276Speter * This product includes software developed by the University of 1650276Speter * California, Berkeley and its contributors. 1750276Speter * 4. Neither the name of the University nor the names of its contributors 1850276Speter * may be used to endorse or promote products derived from this software 1950276Speter * without specific prior written permission. 2050276Speter * 2150276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2250276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2350276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2450276Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2550276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2650276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2750276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2850276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2950276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3050276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3150276Speter * SUCH DAMAGE. 32166124Srafan * 3350276Speter * $FreeBSD: head/usr.bin/last/last.c 60938 2000-05-26 02:09:24Z jake $ 3450276Speter */ 3550276Speter 3650276Speter#ifndef lint 3750276Speterstatic char copyright[] = 3850276Speter"@(#) Copyright (c) 1987, 1993, 1994\n\ 3950276Speter The Regents of the University of California. All rights reserved.\n"; 4050276Speter#endif /* not lint */ 4150276Speter 4250276Speter#ifndef lint 4350276Speterstatic char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 4450276Speter#endif /* not lint */ 4566963Speter 4650276Speter#include <sys/param.h> 4750276Speter#include <sys/stat.h> 4850276Speter 49178866Srafan#include <err.h> 5050276Speter#include <fcntl.h> 5150276Speter#include <locale.h> 5250276Speter#include <paths.h> 5350276Speter#include <signal.h> 5450276Speter#include <stdio.h> 5550276Speter#include <stdlib.h> 5650276Speter#include <string.h> 5797049Speter#include <time.h> 5897049Speter#include <unistd.h> 5997049Speter#include <utmp.h> 6097049Speter#include <sys/queue.h> 6197049Speter 6297049Speter#define NO 0 /* false/no */ 6397049Speter#define YES 1 /* true/yes */ 6497049Speter 6597049Speterstatic struct utmp buf[1024]; /* utmp read buffer */ 6697049Speter 6797049Spetertypedef struct arg { 6897049Speter char *name; /* argument */ 6997049Speter#define HOST_TYPE -2 7097049Speter#define TTY_TYPE -3 7197049Speter#define USER_TYPE -4 7297049Speter int type; /* type of arg */ 7397049Speter struct arg *next; /* linked list pointer */ 7497049Speter} ARG; 7597049SpeterARG *arglist; /* head of linked list */ 7697049Speter 7797049SpeterLIST_HEAD(ttylisthead, ttytab) ttylist; 7897049Speter 7997049Speterstruct ttytab { 8097049Speter time_t logout; /* log out time */ 8197049Speter char tty[UT_LINESIZE + 1]; /* terminal name */ 8297049Speter LIST_ENTRY(ttytab) list; 8397049Speter}; 8497049Speter 8597049Speterstatic long currentout, /* current logout value */ 8697049Speter maxrec; /* records to display */ 8797049Speterstatic char *file = _PATH_WTMP; /* wtmp file */ 8897049Speterstatic int sflag = 0; /* show delta in seconds */ 8997049Speterstatic int width = 5; /* show seconds in delta */ 9097049Speter 9197049Spetervoid addarg __P((int, char *)); 9250276Spetervoid hostconv __P((char *)); 9350276Spetervoid onintr __P((int)); 9450276Speterchar *ttyconv __P((char *)); 9550276Speterint want __P((struct utmp *)); 9650276Spetervoid wtmp __P((void)); 9750276Speter 9850276Spetervoid 9950276Speterusage(void) 10050276Speter{ 10150276Speter (void)fprintf(stderr, 10250276Speter "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n"); 10350276Speter exit(1); 10450276Speter} 10550276Speter 10650276Speterint 10750276Spetermain(argc, argv) 10850276Speter int argc; 10950276Speter char *argv[]; 11050276Speter{ 11150276Speter extern int optind; 11250276Speter extern char *optarg; 11350276Speter int ch; 11450276Speter char *p; 11550276Speter 11650276Speter (void) setlocale(LC_TIME, ""); 11750276Speter 11850276Speter maxrec = -1; 11950276Speter while ((ch = getopt(argc, argv, "0123456789f:h:st:w")) != -1) 12050276Speter switch (ch) { 12150276Speter case '0': case '1': case '2': case '3': case '4': 12250276Speter case '5': case '6': case '7': case '8': case '9': 12350276Speter /* 12450276Speter * kludge: last was originally designed to take 12550276Speter * a number after a dash. 12650276Speter */ 12750276Speter if (maxrec == -1) { 12850276Speter p = argv[optind - 1]; 12950276Speter if (p[0] == '-' && p[1] == ch && !p[2]) 13050276Speter maxrec = atol(++p); 13150276Speter else 13250276Speter maxrec = atol(argv[optind] + 1); 13350276Speter if (!maxrec) 13450276Speter exit(0); 13550276Speter } 13666963Speter break; 13766963Speter case 'f': 13850276Speter file = optarg; 13966963Speter break; 14066963Speter case 'h': 14150276Speter hostconv(optarg); 14250276Speter addarg(HOST_TYPE, optarg); 14366963Speter break; 14450276Speter case 's': 14550276Speter sflag++; /* Show delta as seconds */ 14666963Speter break; 14750276Speter case 't': 14866963Speter addarg(TTY_TYPE, ttyconv(optarg)); 14966963Speter break; 15066963Speter case 'w': 15166963Speter width = 8; 15266963Speter break; 15366963Speter case '?': 15466963Speter default: 15566963Speter usage(); 15666963Speter } 15766963Speter 15850276Speter if (sflag && width == 8) usage(); 15950276Speter 16050276Speter if (argc) { 16150276Speter setlinebuf(stdout); 16250276Speter for (argv += optind; *argv; ++argv) { 16366963Speter#define COMPATIBILITY 16466963Speter#ifdef COMPATIBILITY 16566963Speter /* code to allow "last p5" to work */ 16666963Speter addarg(TTY_TYPE, ttyconv(*argv)); 16766963Speter#endif 16866963Speter addarg(USER_TYPE, *argv); 16966963Speter } 17050276Speter } 17166963Speter wtmp(); 17250276Speter exit(0); 17366963Speter} 17450276Speter 17550276Speter/* 17666963Speter * wtmp -- 17766963Speter * read through the wtmp file 17866963Speter */ 17966963Spetervoid 18066963Speterwtmp() 18166963Speter{ 18250276Speter struct utmp *bp; /* current structure */ 18350276Speter struct ttytab *tt, *ttx; /* ttylist entry */ 18466963Speter struct stat stb; /* stat of file for size */ 18566963Speter long bl; 18666963Speter time_t delta; /* time difference */ 18766963Speter int bytes, wfd; 18866963Speter char *crmsg; 18950276Speter char ct[80]; 19066963Speter struct tm *tm; 19166963Speter 19266963Speter LIST_INIT(&ttylist); 19350276Speter 19466963Speter if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 19566963Speter err(1, "%s", file); 19666963Speter bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 19766963Speter 19850276Speter (void)time(&buf[0].ut_time); 19966963Speter (void)signal(SIGINT, onintr); 20050276Speter (void)signal(SIGQUIT, onintr); 20166963Speter 20266963Speter while (--bl >= 0) { 20366963Speter if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 20466963Speter (bytes = read(wfd, buf, sizeof(buf))) == -1) 20550276Speter err(1, "%s", file); 20666963Speter for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 20750276Speter /* 20866963Speter * if the terminal line is '~', the machine stopped. 20966963Speter * see utmp(5) for more info. 21050276Speter */ 21166963Speter if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 21250276Speter /* everybody just logged out */ 21366963Speter for (tt = ttylist.lh_first; tt;) { 21466963Speter LIST_REMOVE(tt, list); 21566963Speter ttx = tt; 21650276Speter tt = tt->list.le_next; 21766963Speter free(ttx); 21866963Speter } 21966963Speter currentout = -bp->ut_time; 22066963Speter crmsg = strncmp(bp->ut_name, "shutdown", 22166963Speter UT_NAMESIZE) ? "crash" : "shutdown"; 22250276Speter if (want(bp)) { 22366963Speter tm = localtime(&bp->ut_time); 22466963Speter (void) strftime(ct, sizeof(ct), "%c", tm); 22566963Speter printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", 22666963Speter UT_NAMESIZE, UT_NAMESIZE, 22766963Speter bp->ut_name, UT_LINESIZE, 22850276Speter UT_LINESIZE, bp->ut_line, 22966963Speter UT_HOSTSIZE, UT_HOSTSIZE, 23066963Speter bp->ut_host, ct, ct + 11); 23150276Speter if (maxrec != -1 && !--maxrec) 23266963Speter return; 23350276Speter } 23466963Speter continue; 23566963Speter } 23650276Speter /* 23766963Speter * if the line is '{' or '|', date got set; see 23866963Speter * utmp(5) for more info. 23966963Speter */ 24066963Speter if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 24166963Speter && !bp->ut_line[1]) { 242174993Srafan if (want(bp)) { 24366963Speter tm = localtime(&bp->ut_time); 24466963Speter (void) strftime(ct, sizeof(ct), "%c", tm); 24550276Speter printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", 24666963Speter UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 24766963Speter UT_LINESIZE, UT_LINESIZE, bp->ut_line, 24866963Speter UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 24966963Speter ct, ct + 11); 25097049Speter if (maxrec && !--maxrec) 25150276Speter return; 25266963Speter } 25350276Speter continue; 25466963Speter } 255178866Srafan if (bp->ut_name[0] == '\0' || want(bp)) { 256178866Srafan /* find associated tty */ 257178866Srafan for (tt = ttylist.lh_first; ; tt = tt->list.le_next) { 258178866Srafan if (tt == NULL) { 259178866Srafan /* add new one */ 260178866Srafan tt = malloc(sizeof(struct ttytab)); 26150276Speter if (tt == NULL) 26266963Speter err(1, "malloc failure"); 26366963Speter tt->logout = currentout; 26466963Speter strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 26566963Speter LIST_INSERT_HEAD(&ttylist, tt, list); 26666963Speter break; 26750276Speter } 26866963Speter if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 26966963Speter break; 27050276Speter } 27150276Speter if (bp->ut_name[0]) { 27250276Speter /* 27366963Speter * when uucp and ftp log in over a network, the entry in 27466963Speter * the utmp file is the name plus their process id. See 27550276Speter * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 276174993Srafan */ 27750276Speter if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 27850276Speter bp->ut_line[3] = '\0'; 27950276Speter else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 28050276Speter bp->ut_line[4] = '\0'; 28150276Speter tm = localtime(&bp->ut_time); 28250276Speter (void) strftime(ct, sizeof(ct), "%c", tm); 28350276Speter printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ", 28466963Speter UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 28597049Speter UT_LINESIZE, UT_LINESIZE, bp->ut_line, 28650276Speter UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 28797049Speter ct, ct + 11); 28897049Speter if (!tt->logout) 28966963Speter puts(" still logged in"); 29097049Speter else { 29150276Speter if (tt->logout < 0) { 29297049Speter tt->logout = -tt->logout; 29397049Speter printf("- %s", crmsg); 29497049Speter } 29597049Speter else { 29697049Speter tm = localtime(&tt->logout); 29797049Speter (void) strftime(ct, sizeof(ct), "%c", tm); 29897049Speter printf("- %5.5s", ct + 11); 29997049Speter } 30097049Speter delta = tt->logout - bp->ut_time; 30197049Speter if ( sflag ) { 30297049Speter printf(" (%8lu)\n", 30366963Speter delta); 30497049Speter } else { 30550276Speter tm = gmtime(&delta); 30650276Speter (void) strftime(ct, sizeof(ct), "%c", tm); 30750276Speter if (delta < 86400) 30866963Speter printf(" (%*.*s)\n", width, width, ct + 11); 30997049Speter else 31097049Speter printf(" (%ld+%*.*s)\n", 31197049Speter delta / 86400, width, width, ct + 11); 31297049Speter } 31366963Speter } 31497049Speter LIST_REMOVE(tt, list); 31597049Speter free(tt); 31650276Speter if (maxrec != -1 && !--maxrec) 31766963Speter return; 31866963Speter } else { 31997049Speter tt->logout = bp->ut_time; 32050276Speter } 32166963Speter } 32250276Speter } 32350276Speter } 32466963Speter tm = localtime(&buf[0].ut_time); 32597049Speter (void) strftime(ct, sizeof(ct), "\nwtmp begins %c\n", tm); 32666963Speter printf(ct); 32766963Speter} 32897049Speter 32966963Speter/* 33097049Speter * want -- 33197049Speter * see if want this entry 33297049Speter */ 33397049Speterint 33450276Speterwant(bp) 33550276Speter struct utmp *bp; 33650276Speter{ 33750276Speter ARG *step; 33850276Speter 33950276Speter if (!arglist) 34050276Speter return (YES); 34150276Speter 34250276Speter for (step = arglist; step; step = step->next) 34350276Speter switch(step->type) { 34450276Speter case HOST_TYPE: 34550276Speter if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 34650276Speter return (YES); 34776726Speter break; 34866963Speter case TTY_TYPE: 34950276Speter if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 35097049Speter return (YES); 35166963Speter break; 35297049Speter case USER_TYPE: 35397049Speter if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 35450276Speter return (YES); 35597049Speter break; 356174993Srafan } 35750276Speter return (NO); 35897049Speter} 359174993Srafan 360174993Srafan/* 361174993Srafan * addarg -- 362174993Srafan * add an entry to a linked list of arguments 363174993Srafan */ 364174993Srafanvoid 365174993Srafanaddarg(type, arg) 36650276Speter int type; 367174993Srafan char *arg; 36850276Speter{ 369174993Srafan ARG *cur; 370174993Srafan 37197049Speter if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 37297049Speter err(1, "malloc failure"); 37397049Speter cur->next = arglist; 37450276Speter cur->type = type; 37566963Speter cur->name = arg; 37697049Speter arglist = cur; 37750276Speter} 378174993Srafan 37997049Speter/* 38097049Speter * hostconv -- 38197049Speter * convert the hostname to search pattern; if the supplied host name 38250276Speter * has a domain attached that is the same as the current domain, rip 38397049Speter * off the domain suffix since that's what login(1) does. 38450276Speter */ 385174993Srafanvoid 38697049Speterhostconv(arg) 38766963Speter char *arg; 38897049Speter{ 38950276Speter static int first = 1; 390 static char *hostdot, name[MAXHOSTNAMELEN]; 391 char *argdot; 392 393 if (!(argdot = strchr(arg, '.'))) 394 return; 395 if (first) { 396 first = 0; 397 if (gethostname(name, sizeof(name))) 398 err(1, "gethostname"); 399 hostdot = strchr(name, '.'); 400 } 401 if (hostdot && !strcasecmp(hostdot, argdot)) 402 *argdot = '\0'; 403} 404 405/* 406 * ttyconv -- 407 * convert tty to correct name. 408 */ 409char * 410ttyconv(arg) 411 char *arg; 412{ 413 char *mval; 414 415 /* 416 * kludge -- we assume that all tty's end with 417 * a two character suffix. 418 */ 419 if (strlen(arg) == 2) { 420 /* either 6 for "ttyxx" or 8 for "console" */ 421 if (!(mval = malloc((u_int)8))) 422 err(1, "malloc failure"); 423 if (!strcmp(arg, "co")) 424 (void)strcpy(mval, "console"); 425 else { 426 (void)strcpy(mval, "tty"); 427 (void)strcpy(mval + 3, arg); 428 } 429 return (mval); 430 } 431 if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 432 return (arg + 5); 433 return (arg); 434} 435 436/* 437 * onintr -- 438 * on interrupt, we inform the user how far we've gotten 439 */ 440void 441onintr(signo) 442 int signo; 443{ 444 char ct[80]; 445 struct tm *tm; 446 447 tm = localtime(&buf[0].ut_time); 448 (void) strftime(ct, sizeof(ct), "%c", tm); 449 printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11); 450 if (signo == SIGINT) 451 exit(1); 452 (void)fflush(stdout); /* fix required for rsh */ 453} 454