]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/last.c
2 * Berkeley last for Linux. Currently maintained by poe@daimi.aau.dk at
3 * ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
5 * Copyright (c) 1987 Regents of the University of California.
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.
21 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
22 * - added Native Language Support
25 /* 2001-02-14 Marek Zelem <marek@fornax.sk>
26 * - using mmap() on Linux - great speed improvement
30 * This command is deprecated. The utility is in maintenance mode,
31 * meaning we keep them in source tree for backward compatibility
32 * only. Do not waste time making this command better, unless the
33 * fix is about security or other very critical issue.
35 * See Documentation/deprecated.txt for more information.
41 #include <sys/param.h>
44 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
59 #include "closestream.h"
60 #include "pathnames.h"
65 #define SECDAY (24*60*60) /* seconds in a day */
66 #define NO 0 /* false/no */
67 #define YES 1 /* true/yes */
69 static struct utmp utmpbuf
;
71 #define HMAX (int)sizeof(utmpbuf.ut_host) /* size of utmp host field */
72 #define LMAX (int)sizeof(utmpbuf.ut_line) /* size of utmp tty field */
73 #define NMAX (int)sizeof(utmpbuf.ut_name) /* size of utmp name field */
76 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
79 /* maximum sizes used for printing */
80 /* probably we want a two-pass version that computes the right length */
81 int hmax
= MIN(HMAX
, 16);
82 int lmax
= MIN(LMAX
, 8);
83 int nmax
= MIN(NMAX
, 16);
86 char *name
; /* argument */
91 int type
; /* type of arg */
92 struct arg
*next
; /* linked list pointer */
94 ARG
*arglist
; /* head of linked list */
96 typedef struct ttytab
{
97 long logout
; /* log out time */
98 char tty
[LMAX
+ 1]; /* terminal name */
99 struct ttytab
*next
; /* linked list pointer */
101 TTY
*ttylist
; /* head of linked list */
103 static long currentout
, /* current logout value */
104 maxrec
; /* records to display */
105 static char *file
= _PATH_WTMP
; /* wtmp file */
107 static int doyear
= 0; /* output year in dates */
108 static int dolong
= 0; /* print also ip-addr */
110 static void wtmp(void);
111 static void addarg(int, char *);
112 static void hostconv(char *);
113 static void onintr(int);
114 static int want(struct utmp
*, int);
116 static char *ttyconv(char *);
119 main(int argc
, char **argv
) {
122 setlocale(LC_ALL
, "");
123 bindtextdomain(PACKAGE
, LOCALEDIR
);
125 atexit(close_stdout
);
127 while ((ch
= getopt(argc
, argv
, "0123456789yli:f:h:t:")) != -1)
129 case '0': case '1': case '2': case '3': case '4':
130 case '5': case '6': case '7': case '8': case '9':
132 * kludge: last was originally designed to take
133 * a number after a dash.
136 maxrec
= atol(argv
[optind
- 1] + 1);
143 addarg(HOST_TYPE
, optarg
);
146 addarg(TTY_TYPE
, ttyconv(optarg
));
155 addarg(INET_TYPE
, optarg
);
159 fputs(_("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n"), stderr
);
162 for (argv
+= optind
; *argv
; ++argv
) {
163 #define COMPATIBILITY
165 /* code to allow "last p5" to work */
166 addarg(TTY_TYPE
, ttyconv(*argv
));
168 addarg(USER_TYPE
, *argv
);
175 static char *utmp_ctime(struct utmp
*u
)
177 time_t t
= (time_t) u
->ut_time
;
182 * print_partial_line --
183 * print the first part of each output line according to specified format
186 print_partial_line(struct utmp
*bp
) {
190 printf("%-*.*s %-*.*s ", nmax
, nmax
, bp
->ut_name
,
191 lmax
, lmax
, bp
->ut_line
);
196 foo
.s_addr
= bp
->ut_addr
;
197 printf("%-*.*s ", hmax
, hmax
, inet_ntoa(foo
));
199 printf("%-*.*s ", hmax
, hmax
, "");
202 printf("%-*.*s ", hmax
, hmax
, bp
->ut_host
);
206 printf("%10.10s %4.4s %5.5s ", ct
, ct
+ 20, ct
+ 11);
208 printf("%10.10s %5.5s ", ct
, ct
+ 11);
214 * read through the wtmp file
218 register struct utmp
*bp
; /* current structure */
219 register TTY
*T
; /* tty list entry */
220 long delta
; /* time difference */
233 #if defined(_HAVE_UT_TV)
235 gettimeofday(&tv
, NULL
);
236 utmpbuf
.ut_tv
.tv_sec
= tv
.tv_sec
;
237 utmpbuf
.ut_tv
.tv_usec
= tv
.tv_usec
;
245 (void)signal(SIGINT
, onintr
);
246 (void)signal(SIGQUIT
, onintr
);
248 if ((fd
= open(file
,O_RDONLY
)) < 0)
249 err(EXIT_FAILURE
, _("%s: open failed"), file
);
252 utl_len
= st
.st_size
;
253 utl
= mmap(NULL
, utl_len
, PROT_READ
|PROT_WRITE
,
254 MAP_PRIVATE
|MAP_FILE
, fd
, 0);
256 err(EXIT_FAILURE
, _("%s: mmap failed"), file
);
258 listnr
= utl_len
/sizeof(struct utmp
);
261 ct
= utmp_ctime(&utl
[0]);
263 for(i
= listnr
- 1; i
>= 0; i
--) {
266 * if the terminal line is '~', the machine stopped.
267 * see utmp(5) for more info.
269 if (!strncmp(bp
->ut_line
, "~", LMAX
)) {
271 * utmp(5) also mentions that the user
272 * name should be 'shutdown' or 'reboot'.
273 * Not checking the name causes e.g. runlevel
274 * changes to be displayed as 'crash'. -thaele
276 if (!strncmp(bp
->ut_user
, "reboot", NMAX
) ||
277 !strncmp(bp
->ut_user
, "shutdown", NMAX
)) {
278 /* everybody just logged out */
279 for (T
= ttylist
; T
; T
= T
->next
)
280 T
->logout
= -bp
->ut_time
;
283 currentout
= -bp
->ut_time
;
284 crmsg
= (strncmp(bp
->ut_name
, "shutdown", NMAX
)
285 ? "crash" : "down ");
287 (void)strcpy(bp
->ut_name
, "reboot");
290 if(bp
->ut_type
!= LOGIN_PROCESS
) {
291 print_partial_line(bp
);
294 if (maxrec
&& !--maxrec
)
299 /* find associated tty */
300 for (T
= ttylist
;; T
= T
->next
) {
303 T
= addtty(bp
->ut_line
);
306 if (!strncmp(T
->tty
, bp
->ut_line
, LMAX
))
309 if (bp
->ut_name
[0] && bp
->ut_type
!= LOGIN_PROCESS
310 && bp
->ut_type
!= DEAD_PROCESS
313 print_partial_line(bp
);
316 puts(_(" still logged in"));
319 T
->logout
= -T
->logout
;
320 printf("- %s", crmsg
);
323 printf("- %5.5s", ctime(&T
->logout
)+11);
324 delta
= T
->logout
- bp
->ut_time
;
326 printf(" (%5.5s)\n", asctime(gmtime(&delta
))+11);
328 printf(" (%ld+%5.5s)\n", delta
/ SECDAY
, asctime(gmtime(&delta
))+11);
330 if (maxrec
!= -1 && !--maxrec
)
333 T
->logout
= bp
->ut_time
;
334 utmpbuf
.ut_time
= bp
->ut_time
;
338 if(ct
) printf(_("\nwtmp begins %s"), ct
); /* ct already ends in \n */
343 * see if want this entry
346 want(struct utmp
*bp
, int check
) {
351 * when uucp and ftp log in over a network, the entry in
352 * the utmp file is the name plus their process id. See
353 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
355 if (!strncmp(bp
->ut_line
, "ftp", sizeof("ftp") - 1))
356 bp
->ut_line
[3] = '\0';
357 else if (!strncmp(bp
->ut_line
, "uucp", sizeof("uucp") - 1))
358 bp
->ut_line
[4] = '\0';
363 for (step
= arglist
; step
; step
= step
->next
)
366 if (!strncmp(step
->name
, bp
->ut_host
, HMAX
))
370 if (!strncmp(step
->name
, bp
->ut_line
, LMAX
))
374 if (!strncmp(step
->name
, bp
->ut_name
, NMAX
))
378 if ((in_addr_t
) bp
->ut_addr
== inet_addr(step
->name
))
389 * add an entry to a linked list of arguments
392 addarg(int type
, char *arg
) {
395 cur
= xmalloc(sizeof(ARG
));
404 * add an entry to a linked list of ttys
407 addtty(char *ttyname
) {
410 cur
= xmalloc(sizeof(TTY
));
412 cur
->logout
= currentout
;
413 memcpy(cur
->tty
, ttyname
, LMAX
);
414 return(ttylist
= cur
);
419 * convert the hostname to search pattern; if the supplied host name
420 * has a domain attached that is the same as the current domain, rip
421 * off the domain suffix since that's what login(1) does.
424 hostconv(char *arg
) {
425 static int first
= 1;
426 static char *hostdot
,
427 name
[MAXHOSTNAMELEN
];
430 if (!(argdot
= strchr(arg
, '.')))
434 if (gethostname(name
, sizeof(name
)))
435 err(EXIT_FAILURE
, _("gethostname failed"));
437 hostdot
= strchr(name
, '.');
439 if (hostdot
&& !strcmp(hostdot
, argdot
))
445 * convert tty to correct name.
452 * kludge -- we assume that all tty's end with
453 * a two character suffix.
455 if (strlen(arg
) == 2) {
456 /* either 6 for "ttyxx" or 8 for "console" */
458 if (!strncmp(arg
, "co", 2))
459 (void)strcpy(mval
, "console");
461 (void)strcpy(mval
, "tty");
462 (void)strncpy(mval
+ 3, arg
, 4);
466 if (!strncmp(arg
, "/dev/", sizeof("/dev/") - 1))
474 * on interrupt, we inform the user how far we've gotten
480 ct
= utmp_ctime(&utmpbuf
);
481 printf(_("\ninterrupted %10.10s %5.5s \n"), ct
, ct
+ 11);
484 fflush(stdout
); /* fix required for rsh */