]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/last.c
Imported from util-linux-2.7.1 tarball.
[thirdparty/util-linux.git] / login-utils / last.c
CommitLineData
6dbe3af9
KZ
1/*
2 * Berkeley last for Linux. Currently maintained by poe@daimi.aau.dk at
3 * ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
4 *
5 * Copyright (c) 1987 Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 */
20
21#ifndef lint
22char copyright[] =
23"@(#) Copyright (c) 1987 Regents of the University of California.\n\
24 All rights reserved.\n";
25#endif /* not lint */
26
27#ifndef lint
28static char sccsid[] = "@(#)last.c 5.11 (Berkeley) 6/29/88";
29#endif /* not lint */
30
31/*
32 * last
33 */
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/file.h>
37#include <signal.h>
38#include <string.h>
39#include <time.h>
40#include <utmp.h>
41#include <stdio.h>
42#include <getopt.h>
43#include <stdlib.h>
44#include <unistd.h>
45#include <arpa/inet.h>
46#include "pathnames.h"
47
48#define SECDAY (24*60*60) /* seconds in a day */
49#define NO 0 /* false/no */
50#define YES 1 /* true/yes */
51
52static struct utmp buf[1024]; /* utmp read buffer */
53
54#define HMAX (int)sizeof(buf[0].ut_host) /* size of utmp host field */
55#define LMAX (int)sizeof(buf[0].ut_line) /* size of utmp tty field */
56#define NMAX (int)sizeof(buf[0].ut_name) /* size of utmp name field */
57
58typedef struct arg {
59 char *name; /* argument */
60#define HOST_TYPE -2
61#define TTY_TYPE -3
62#define USER_TYPE -4
63#define INET_TYPE -5
64 int type; /* type of arg */
65 struct arg *next; /* linked list pointer */
66} ARG;
67ARG *arglist; /* head of linked list */
68
69typedef struct ttytab {
70 long logout; /* log out time */
71 char tty[LMAX + 1]; /* terminal name */
72 struct ttytab *next; /* linked list pointer */
73} TTY;
74TTY *ttylist; /* head of linked list */
75
76static long currentout, /* current logout value */
77 maxrec; /* records to display */
78static char *file = _PATH_WTMP; /* wtmp file */
79
80static int doyear = 0; /* output year in dates */
81static int dolong = 0; /* print also ip-addr */
82
83static void wtmp(), addarg(), hostconv();
84static int want();
85TTY *addtty();
86static char *ttyconv();
87
88int
89main(argc, argv)
90 int argc;
91 char **argv;
92{
93 extern int optind;
94 extern char *optarg;
95 int ch;
96
97 while ((ch = getopt(argc, argv, "0123456789yli:f:h:t:")) != EOF)
98 switch((char)ch) {
99 case '0': case '1': case '2': case '3': case '4':
100 case '5': case '6': case '7': case '8': case '9':
101 /*
102 * kludge: last was originally designed to take
103 * a number after a dash.
104 */
105 if (!maxrec)
106 maxrec = atol(argv[optind - 1] + 1);
107 break;
108 case 'f':
109 file = optarg;
110 break;
111 case 'h':
112 hostconv(optarg);
113 addarg(HOST_TYPE, optarg);
114 break;
115 case 't':
116 addarg(TTY_TYPE, ttyconv(optarg));
117 break;
118 case 'y':
119 doyear = 1;
120 break;
121 case 'l':
122 dolong = 1;
123 break;
124 case 'i':
125 addarg(INET_TYPE, optarg);
126 break;
127 case '?':
128 default:
129 fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr);
130 exit(1);
131 }
132 for (argv += optind; *argv; ++argv) {
133#define COMPATIBILITY
134#ifdef COMPATIBILITY
135 /* code to allow "last p5" to work */
136 addarg(TTY_TYPE, ttyconv(*argv));
137#endif
138 addarg(USER_TYPE, *argv);
139 }
140 wtmp();
141 exit(0);
142}
143
144/*
145 * print_partial_line --
146 * print the first part of each output line according to specified format
147 */
148void
149print_partial_line(bp)
150 struct utmp *bp;
151{
152 char *ct;
153
154 ct = ctime(&bp->ut_time);
155 printf("%-*.*s %-*.*s ", NMAX, NMAX, bp->ut_name,
156 LMAX, LMAX, bp->ut_line);
157
158 if (dolong) {
159 if (bp->ut_addr) {
160 struct in_addr foo;
161 foo.s_addr = bp->ut_addr;
162 printf("%-*.*s ", HMAX, HMAX, inet_ntoa(foo));
163 } else {
164 printf("%-*.*s ", HMAX, HMAX, "");
165 }
166 } else {
167 printf("%-*.*s ", HMAX, HMAX, bp->ut_host);
168 }
169
170 if (doyear) {
171 printf("%10.10s %4.4s %5.5s ", ct, ct + 20, ct + 11);
172 } else {
173 printf("%10.10s %5.5s ", ct, ct + 11);
174 }
175}
176
177/*
178 * wtmp --
179 * read through the wtmp file
180 */
181static void
182wtmp()
183{
184 register struct utmp *bp; /* current structure */
185 register TTY *T; /* tty list entry */
186 struct stat stb; /* stat of file for size */
187 long bl, delta, /* time difference */
188 lseek(), time();
189 int bytes, wfd;
190 void onintr();
191 char *ct, *crmsg;
192
193 if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) {
194 perror(file);
195 exit(1);
196 }
197 bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
198
199 (void)time(&buf[0].ut_time);
200 (void)signal(SIGINT, onintr);
201 (void)signal(SIGQUIT, onintr);
202
203 while (--bl >= 0) {
fd6b7a7f
KZ
204 if (lseek(wfd, (long)(bl * sizeof(buf)), L_SET) == -1 ||
205 (bytes = read(wfd, (char *)buf, sizeof(buf))) == -1) {
206 fprintf(stderr, "last: %s: ", file);
207 perror((char *)NULL);
208 exit(1);
209 }
210 for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
211 /*
212 * if the terminal line is '~', the machine stopped.
213 * see utmp(5) for more info.
214 */
215 if (!strncmp(bp->ut_line, "~", LMAX)) {
216 /*
217 * utmp(5) also mentions that the user
218 * name should be 'shutdown' or 'reboot'.
219 * Not checking the name causes i.e. runlevel
220 * changes to be displayed as 'crash'. -thaele
221 */
222 if (!strncmp(bp->ut_user, "reboot", NMAX) ||
223 !strncmp(bp->ut_user, "shutdown", NMAX)) {
224 /* everybody just logged out */
225 for (T = ttylist; T; T = T->next)
226 T->logout = -bp->ut_time;
227 }
228
229 currentout = -bp->ut_time;
230 crmsg = strncmp(bp->ut_name, "shutdown", NMAX) ? "crash" : "down ";
231 if (!bp->ut_name[0])
232 (void)strcpy(bp->ut_name, "reboot");
233 if (want(bp, NO)) {
234 ct = ctime(&bp->ut_time);
235 if(bp->ut_type != LOGIN_PROCESS) {
236 print_partial_line(bp);
237 putchar('\n');
6dbe3af9 238 }
fd6b7a7f
KZ
239 if (maxrec && !--maxrec)
240 return;
241 }
242 continue;
243 }
244 /* find associated tty */
245 for (T = ttylist;; T = T->next) {
246 if (!T) {
247 /* add new one */
248 T = addtty(bp->ut_line);
249 break;
250 }
251 if (!strncmp(T->tty, bp->ut_line, LMAX))
252 break;
253 }
254 if (bp->ut_name[0] && bp->ut_type != LOGIN_PROCESS
255 && bp->ut_type != DEAD_PROCESS
256 && want(bp, YES)) {
6dbe3af9 257
fd6b7a7f 258 print_partial_line(bp);
6dbe3af9 259
fd6b7a7f
KZ
260 if (!T->logout)
261 puts(" still logged in");
262 else {
263 if (T->logout < 0) {
264 T->logout = -T->logout;
265 printf("- %s", crmsg);
6dbe3af9 266 }
fd6b7a7f
KZ
267 else
268 printf("- %5.5s", ctime(&T->logout)+11);
269 delta = T->logout - bp->ut_time;
270 if (delta < SECDAY)
271 printf(" (%5.5s)\n", asctime(gmtime(&delta))+11);
272 else
273 printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11);
274 }
275 if (maxrec != -1 && !--maxrec)
276 return;
6dbe3af9 277 }
fd6b7a7f
KZ
278 T->logout = bp->ut_time;
279 }
6dbe3af9
KZ
280 }
281 ct = ctime(&buf[0].ut_time);
282 printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
283}
284
285/*
286 * want --
287 * see if want this entry
288 */
289static int
290want(bp, check)
291 register struct utmp *bp;
292 int check;
293{
294 register ARG *step;
295
296 if (check)
297 /*
298 * when uucp and ftp log in over a network, the entry in
299 * the utmp file is the name plus their process id. See
300 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
301 */
302 if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
303 bp->ut_line[3] = '\0';
304 else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
305 bp->ut_line[4] = '\0';
306 if (!arglist)
307 return(YES);
308
309 for (step = arglist; step; step = step->next)
310 switch(step->type) {
311 case HOST_TYPE:
312 if (!strncmp(step->name, bp->ut_host, HMAX))
313 return(YES);
314 break;
315 case TTY_TYPE:
316 if (!strncmp(step->name, bp->ut_line, LMAX))
317 return(YES);
318 break;
319 case USER_TYPE:
320 if (!strncmp(step->name, bp->ut_name, NMAX))
321 return(YES);
322 break;
323 case INET_TYPE:
324 if (bp->ut_addr == inet_addr(step->name))
325 return(YES);
326 break;
327 }
328 return(NO);
329}
330
331/*
332 * addarg --
333 * add an entry to a linked list of arguments
334 */
335static void
336addarg(type, arg)
337 int type;
338 char *arg;
339{
340 register ARG *cur;
341
342 if (!(cur = (ARG *)malloc((unsigned int)sizeof(ARG)))) {
343 fputs("last: malloc failure.\n", stderr);
344 exit(1);
345 }
346 cur->next = arglist;
347 cur->type = type;
348 cur->name = arg;
349 arglist = cur;
350}
351
352/*
353 * addtty --
354 * add an entry to a linked list of ttys
355 */
356TTY *
357addtty(ttyname)
358 char *ttyname;
359{
360 register TTY *cur;
361
362 if (!(cur = (TTY *)malloc((unsigned int)sizeof(TTY)))) {
363 fputs("last: malloc failure.\n", stderr);
364 exit(1);
365 }
366 cur->next = ttylist;
367 cur->logout = currentout;
368 memcpy(cur->tty, ttyname, LMAX);
369 return(ttylist = cur);
370}
371
372/*
373 * hostconv --
374 * convert the hostname to search pattern; if the supplied host name
375 * has a domain attached that is the same as the current domain, rip
376 * off the domain suffix since that's what login(1) does.
377 */
378static void
379hostconv(arg)
380 char *arg;
381{
382 static int first = 1;
383 static char *hostdot,
384 name[MAXHOSTNAMELEN];
385 char *argdot;
386
387 if (!(argdot = strchr(arg, '.')))
388 return;
389 if (first) {
390 first = 0;
391 if (gethostname(name, sizeof(name))) {
392 perror("last: gethostname");
393 exit(1);
394 }
395 hostdot = strchr(name, '.');
396 }
397 if (hostdot && !strcmp(hostdot, argdot))
398 *argdot = '\0';
399}
400
401/*
402 * ttyconv --
403 * convert tty to correct name.
404 */
405static char *
406ttyconv(arg)
407 char *arg;
408{
409 char *mval;
410
411 /*
412 * kludge -- we assume that all tty's end with
413 * a two character suffix.
414 */
415 if (strlen(arg) == 2) {
416 /* either 6 for "ttyxx" or 8 for "console" */
417 if (!(mval = malloc((unsigned int)8))) {
418 fputs("last: malloc failure.\n", stderr);
419 exit(1);
420 }
421 if (!strcmp(arg, "co"))
422 (void)strcpy(mval, "console");
423 else {
424 (void)strcpy(mval, "tty");
425 (void)strcpy(mval + 3, arg);
426 }
427 return(mval);
428 }
429 if (!strncmp(arg, "/dev/", sizeof("/dev/") - 1))
430 return(arg + 5);
431 return(arg);
432}
433
434/*
435 * onintr --
436 * on interrupt, we inform the user how far we've gotten
437 */
438void
439onintr(signo)
440 int signo;
441{
442 char *ct;
443
444 ct = ctime(&buf[0].ut_time);
445 printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
446 if (signo == SIGINT)
447 exit(1);
448 (void)fflush(stdout); /* fix required for rsh */
449}