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