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