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