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