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