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