last.c revision 1590
1203288Srnoland/* 2203288Srnoland * Copyright (c) 1987, 1993, 1994 3203288Srnoland * The Regents of the University of California. All rights reserved. 4203288Srnoland * 5203288Srnoland * Redistribution and use in source and binary forms, with or without 6203288Srnoland * modification, are permitted provided that the following conditions 7203288Srnoland * are met: 8203288Srnoland * 1. Redistributions of source code must retain the above copyright 9203288Srnoland * notice, this list of conditions and the following disclaimer. 10203288Srnoland * 2. Redistributions in binary form must reproduce the above copyright 11203288Srnoland * notice, this list of conditions and the following disclaimer in the 12203288Srnoland * documentation and/or other materials provided with the distribution. 13203288Srnoland * 3. All advertising materials mentioning features or use of this software 14203288Srnoland * must display the following acknowledgement: 15203288Srnoland * This product includes software developed by the University of 16203288Srnoland * California, Berkeley and its contributors. 17203288Srnoland * 4. Neither the name of the University nor the names of its contributors 18203288Srnoland * may be used to endorse or promote products derived from this software 19203288Srnoland * without specific prior written permission. 20203288Srnoland * 21203288Srnoland * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22203288Srnoland * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23203288Srnoland * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24203288Srnoland * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25203288Srnoland * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26203288Srnoland * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27203288Srnoland * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28203288Srnoland * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29203288Srnoland * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30203288Srnoland * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31203288Srnoland * SUCH DAMAGE. 32203288Srnoland */ 33203288Srnoland 34203288Srnoland#ifndef lint 35203288Srnolandstatic char copyright[] = 36203288Srnoland"@(#) Copyright (c) 1987, 1993, 1994\n\ 37203288Srnoland The Regents of the University of California. All rights reserved.\n"; 38203288Srnoland#endif /* not lint */ 39203288Srnoland 40203288Srnoland#ifndef lint 41203288Srnolandstatic char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 42203288Srnoland#endif /* not lint */ 43203288Srnoland 44203288Srnoland#include <sys/param.h> 45203288Srnoland#include <sys/stat.h> 46203288Srnoland 47203288Srnoland#include <err.h> 48203288Srnoland#include <fcntl.h> 49203288Srnoland#include <paths.h> 50203288Srnoland#include <signal.h> 51203288Srnoland#include <stdio.h> 52203288Srnoland#include <stdlib.h> 53203288Srnoland#include <string.h> 54203288Srnoland#include <time.h> 55203288Srnoland#include <tzfile.h> 56203288Srnoland#include <unistd.h> 57203288Srnoland#include <utmp.h> 58203288Srnoland 59203288Srnoland#define NO 0 /* false/no */ 60203288Srnoland#define YES 1 /* true/yes */ 61203288Srnoland 62203288Srnolandstatic struct utmp buf[1024]; /* utmp read buffer */ 63203288Srnoland 64203288Srnolandtypedef struct arg { 65203288Srnoland char *name; /* argument */ 66203288Srnoland#define HOST_TYPE -2 67203288Srnoland#define TTY_TYPE -3 68203288Srnoland#define USER_TYPE -4 69203288Srnoland int type; /* type of arg */ 70203288Srnoland struct arg *next; /* linked list pointer */ 71203288Srnoland} ARG; 72203288SrnolandARG *arglist; /* head of linked list */ 73203288Srnoland 74203288Srnolandtypedef struct ttytab { 75203288Srnoland long logout; /* log out time */ 76203288Srnoland char tty[UT_LINESIZE + 1]; /* terminal name */ 77203288Srnoland struct ttytab *next; /* linked list pointer */ 78203288Srnoland} TTY; 79203288SrnolandTTY *ttylist; /* head of linked list */ 80203288Srnoland 81203288Srnolandstatic long currentout, /* current logout value */ 82203288Srnoland maxrec; /* records to display */ 83203288Srnolandstatic char *file = _PATH_WTMP; /* wtmp file */ 84203288Srnoland 85203288Srnolandvoid addarg __P((int, char *)); 86203288SrnolandTTY *addtty __P((char *)); 87203288Srnolandvoid hostconv __P((char *)); 88203288Srnolandvoid onintr __P((int)); 89203288Srnolandchar *ttyconv __P((char *)); 90203288Srnolandint want __P((struct utmp *, int)); 91203288Srnolandvoid wtmp __P((void)); 92203288Srnoland 93203288Srnolandint 94203288Srnolandmain(argc, argv) 95203288Srnoland int argc; 96203288Srnoland char *argv[]; 97203288Srnoland{ 98203288Srnoland extern int optind; 99203288Srnoland extern char *optarg; 100203288Srnoland int ch; 101203288Srnoland char *p; 102203288Srnoland 103203288Srnoland maxrec = -1; 104203288Srnoland while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF) 105203288Srnoland switch (ch) { 106203288Srnoland case '0': case '1': case '2': case '3': case '4': 107203288Srnoland case '5': case '6': case '7': case '8': case '9': 108203288Srnoland /* 109203288Srnoland * kludge: last was originally designed to take 110203288Srnoland * a number after a dash. 111203288Srnoland */ 112203288Srnoland if (maxrec == -1) { 113203288Srnoland p = argv[optind - 1]; 114203288Srnoland if (p[0] == '-' && p[1] == ch && !p[2]) 115203288Srnoland maxrec = atol(++p); 116203288Srnoland else 117203288Srnoland maxrec = atol(argv[optind] + 1); 118203288Srnoland if (!maxrec) 119203288Srnoland exit(0); 120203288Srnoland } 121203288Srnoland break; 122203288Srnoland case 'f': 123203288Srnoland file = optarg; 124203288Srnoland break; 125203288Srnoland case 'h': 126203288Srnoland hostconv(optarg); 127203288Srnoland addarg(HOST_TYPE, optarg); 128203288Srnoland break; 129203288Srnoland case 't': 130203288Srnoland addarg(TTY_TYPE, ttyconv(optarg)); 131203288Srnoland break; 132203288Srnoland case '?': 133203288Srnoland default: 134203288Srnoland (void)fprintf(stderr, 135203288Srnoland "usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n"); 136203288Srnoland exit(1); 137203288Srnoland } 138203288Srnoland 139203288Srnoland if (argc) { 140203288Srnoland setlinebuf(stdout); 141203288Srnoland for (argv += optind; *argv; ++argv) { 142203288Srnoland#define COMPATIBILITY 143203288Srnoland#ifdef COMPATIBILITY 144203288Srnoland /* code to allow "last p5" to work */ 145203288Srnoland addarg(TTY_TYPE, ttyconv(*argv)); 146203288Srnoland#endif 147203288Srnoland addarg(USER_TYPE, *argv); 148203288Srnoland } 149203288Srnoland } 150203288Srnoland wtmp(); 151203288Srnoland exit(0); 152203288Srnoland} 153203288Srnoland 154203288Srnoland/* 155203288Srnoland * wtmp -- 156203288Srnoland * read through the wtmp file 157203288Srnoland */ 158203288Srnolandvoid 159203288Srnolandwtmp() 160203288Srnoland{ 161203288Srnoland struct utmp *bp; /* current structure */ 162203288Srnoland TTY *T; /* tty list entry */ 163203288Srnoland struct stat stb; /* stat of file for size */ 164203288Srnoland long bl, delta; /* time difference */ 165203288Srnoland int bytes, wfd; 166203288Srnoland char *ct, *crmsg; 167203288Srnoland 168203288Srnoland if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 169203288Srnoland err(1, "%s", file); 170203288Srnoland bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 171203288Srnoland 172203288Srnoland (void)time(&buf[0].ut_time); 173203288Srnoland (void)signal(SIGINT, onintr); 174203288Srnoland (void)signal(SIGQUIT, onintr); 175203288Srnoland 176203288Srnoland while (--bl >= 0) { 177203288Srnoland if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 178203288Srnoland (bytes = read(wfd, buf, sizeof(buf))) == -1) 179203288Srnoland err(1, "%s", file); 180203288Srnoland for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 181203288Srnoland /* 182203288Srnoland * if the terminal line is '~', the machine stopped. 183203288Srnoland * see utmp(5) for more info. 184203288Srnoland */ 185203288Srnoland if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 186203288Srnoland /* everybody just logged out */ 187203288Srnoland for (T = ttylist; T; T = T->next) 188203288Srnoland T->logout = -bp->ut_time; 189203288Srnoland currentout = -bp->ut_time; 190203288Srnoland crmsg = strncmp(bp->ut_name, "shutdown", 191203288Srnoland UT_NAMESIZE) ? "crash" : "shutdown"; 192203288Srnoland if (want(bp, NO)) { 193203288Srnoland ct = ctime(&bp->ut_time); 194203288Srnoland printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", 195203288Srnoland UT_NAMESIZE, UT_NAMESIZE, 196203288Srnoland bp->ut_name, UT_LINESIZE, 197203288Srnoland UT_LINESIZE, bp->ut_line, 198203288Srnoland UT_HOSTSIZE, UT_HOSTSIZE, 199203288Srnoland bp->ut_host, ct, ct + 11); 200203288Srnoland if (maxrec != -1 && !--maxrec) 201203288Srnoland return; 202203288Srnoland } 203203288Srnoland continue; 204203288Srnoland } 205203288Srnoland /* 206203288Srnoland * if the line is '{' or '|', date got set; see 207203288Srnoland * utmp(5) for more info. 208203288Srnoland */ 209203288Srnoland if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 210203288Srnoland && !bp->ut_line[1]) { 211203288Srnoland if (want(bp, NO)) { 212203288Srnoland ct = ctime(&bp->ut_time); 213203288Srnoland printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", 214203288Srnoland UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 215203288Srnoland UT_LINESIZE, UT_LINESIZE, bp->ut_line, 216203288Srnoland UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 217203288Srnoland ct, ct + 11); 218203288Srnoland if (maxrec && !--maxrec) 219203288Srnoland return; 220203288Srnoland } 221203288Srnoland continue; 222203288Srnoland } 223203288Srnoland /* find associated tty */ 224203288Srnoland for (T = ttylist;; T = T->next) { 225203288Srnoland if (!T) { 226203288Srnoland /* add new one */ 227203288Srnoland T = addtty(bp->ut_line); 228203288Srnoland break; 229203288Srnoland } 230203288Srnoland if (!strncmp(T->tty, bp->ut_line, UT_LINESIZE)) 231203288Srnoland break; 232203288Srnoland } 233203288Srnoland if (bp->ut_name[0] && want(bp, YES)) { 234203288Srnoland ct = ctime(&bp->ut_time); 235203288Srnoland printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ", 236203288Srnoland UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 237203288Srnoland UT_LINESIZE, UT_LINESIZE, bp->ut_line, 238203288Srnoland UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 239203288Srnoland ct, ct + 11); 240203288Srnoland if (!T->logout) 241203288Srnoland puts(" still logged in"); 242203288Srnoland else { 243203288Srnoland if (T->logout < 0) { 244203288Srnoland T->logout = -T->logout; 245203288Srnoland printf("- %s", crmsg); 246203288Srnoland } 247203288Srnoland else 248203288Srnoland printf("- %5.5s", 249203288Srnoland ctime(&T->logout)+11); 250203288Srnoland delta = T->logout - bp->ut_time; 251203288Srnoland if (delta < SECSPERDAY) 252203288Srnoland printf(" (%5.5s)\n", 253203288Srnoland asctime(gmtime(&delta))+11); 254203288Srnoland else 255203288Srnoland printf(" (%ld+%5.5s)\n", 256203288Srnoland delta / SECSPERDAY, 257203288Srnoland asctime(gmtime(&delta))+11); 258203288Srnoland } 259203288Srnoland if (maxrec != -1 && !--maxrec) 260203288Srnoland return; 261203288Srnoland } 262203288Srnoland T->logout = bp->ut_time; 263203288Srnoland } 264203288Srnoland } 265203288Srnoland ct = ctime(&buf[0].ut_time); 266203288Srnoland printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11); 267203288Srnoland} 268203288Srnoland 269203288Srnoland/* 270203288Srnoland * want -- 271203288Srnoland * see if want this entry 272203288Srnoland */ 273203288Srnolandint 274203288Srnolandwant(bp, check) 275203288Srnoland struct utmp *bp; 276203288Srnoland int check; 277203288Srnoland{ 278203288Srnoland ARG *step; 279203288Srnoland 280203288Srnoland if (check) 281203288Srnoland /* 282203288Srnoland * when uucp and ftp log in over a network, the entry in 283203288Srnoland * the utmp file is the name plus their process id. See 284203288Srnoland * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 285203288Srnoland */ 286203288Srnoland if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 287203288Srnoland bp->ut_line[3] = '\0'; 288203288Srnoland else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 289203288Srnoland bp->ut_line[4] = '\0'; 290203288Srnoland if (!arglist) 291203288Srnoland return (YES); 292203288Srnoland 293203288Srnoland for (step = arglist; step; step = step->next) 294203288Srnoland switch(step->type) { 295203288Srnoland case HOST_TYPE: 296203288Srnoland if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 297203288Srnoland return (YES); 298203288Srnoland break; 299203288Srnoland case TTY_TYPE: 300203288Srnoland if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 301203288Srnoland return (YES); 302203288Srnoland break; 303203288Srnoland case USER_TYPE: 304203288Srnoland if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 305203288Srnoland return (YES); 306203288Srnoland break; 307203288Srnoland } 308203288Srnoland return (NO); 309203288Srnoland} 310203288Srnoland 311203288Srnoland/* 312203288Srnoland * addarg -- 313203288Srnoland * add an entry to a linked list of arguments 314203288Srnoland */ 315203288Srnolandvoid 316203288Srnolandaddarg(type, arg) 317203288Srnoland int type; 318203288Srnoland char *arg; 319203288Srnoland{ 320203288Srnoland ARG *cur; 321203288Srnoland 322203288Srnoland if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 323203288Srnoland err(1, "malloc failure"); 324203288Srnoland cur->next = arglist; 325203288Srnoland cur->type = type; 326203288Srnoland cur->name = arg; 327203288Srnoland arglist = cur; 328203288Srnoland} 329203288Srnoland 330203288Srnoland/* 331203288Srnoland * addtty -- 332203288Srnoland * add an entry to a linked list of ttys 333203288Srnoland */ 334203288SrnolandTTY * 335203288Srnolandaddtty(ttyname) 336203288Srnoland char *ttyname; 337203288Srnoland{ 338203288Srnoland TTY *cur; 339203288Srnoland 340203288Srnoland if (!(cur = (TTY *)malloc((u_int)sizeof(TTY)))) 341203288Srnoland err(1, "malloc failure"); 342203288Srnoland cur->next = ttylist; 343203288Srnoland cur->logout = currentout; 344203288Srnoland memmove(cur->tty, ttyname, UT_LINESIZE); 345203288Srnoland return (ttylist = cur); 346203288Srnoland} 347203288Srnoland 348203288Srnoland/* 349203288Srnoland * hostconv -- 350203288Srnoland * convert the hostname to search pattern; if the supplied host name 351203288Srnoland * has a domain attached that is the same as the current domain, rip 352203288Srnoland * off the domain suffix since that's what login(1) does. 353203288Srnoland */ 354203288Srnolandvoid 355203288Srnolandhostconv(arg) 356203288Srnoland char *arg; 357203288Srnoland{ 358203288Srnoland static int first = 1; 359203288Srnoland static char *hostdot, name[MAXHOSTNAMELEN]; 360203288Srnoland char *argdot; 361203288Srnoland 362203288Srnoland if (!(argdot = strchr(arg, '.'))) 363203288Srnoland return; 364203288Srnoland if (first) { 365203288Srnoland first = 0; 366203288Srnoland if (gethostname(name, sizeof(name))) 367203288Srnoland err(1, "gethostname"); 368203288Srnoland hostdot = strchr(name, '.'); 369203288Srnoland } 370203288Srnoland if (hostdot && !strcasecmp(hostdot, argdot)) 371203288Srnoland *argdot = '\0'; 372203288Srnoland} 373203288Srnoland 374203288Srnoland/* 375203288Srnoland * ttyconv -- 376203288Srnoland * convert tty to correct name. 377203288Srnoland */ 378203288Srnolandchar * 379203288Srnolandttyconv(arg) 380203288Srnoland char *arg; 381203288Srnoland{ 382203288Srnoland char *mval; 383203288Srnoland 384203288Srnoland /* 385203288Srnoland * kludge -- we assume that all tty's end with 386203288Srnoland * a two character suffix. 387203288Srnoland */ 388203288Srnoland if (strlen(arg) == 2) { 389203288Srnoland /* either 6 for "ttyxx" or 8 for "console" */ 390203288Srnoland if (!(mval = malloc((u_int)8))) 391203288Srnoland err(1, "malloc failure"); 392203288Srnoland if (!strcmp(arg, "co")) 393203288Srnoland (void)strcpy(mval, "console"); 394203288Srnoland else { 395203288Srnoland (void)strcpy(mval, "tty"); 396203288Srnoland (void)strcpy(mval + 3, arg); 397203288Srnoland } 398203288Srnoland return (mval); 399203288Srnoland } 400203288Srnoland if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 401203288Srnoland return (arg + 5); 402203288Srnoland return (arg); 403203288Srnoland} 404203288Srnoland 405203288Srnoland/* 406203288Srnoland * onintr -- 407203288Srnoland * on interrupt, we inform the user how far we've gotten 408203288Srnoland */ 409203288Srnolandvoid 410203288Srnolandonintr(signo) 411203288Srnoland int signo; 412203288Srnoland{ 413203288Srnoland char *ct; 414203288Srnoland 415203288Srnoland ct = ctime(&buf[0].ut_time); 416203288Srnoland printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11); 417203288Srnoland if (signo == SIGINT) 418203288Srnoland exit(1); 419203288Srnoland (void)fflush(stdout); /* fix required for rsh */ 420203288Srnoland} 421203288Srnoland