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