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