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