1 /* vi: set sw=4 ts=4: */
3 * last implementation for busybox
5 * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
7 * Licensed under GPLv2, see file LICENSE in this source tree.
10 //usage:#define last_trivial_usage
11 //usage: ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]")
12 //usage:#define last_full_usage "\n\n"
13 //usage: "Show listing of the last users that logged into the system"
14 //usage: IF_FEATURE_LAST_FANCY( "\n"
15 /* //usage: "\n -H Show header line" */
16 //usage: "\n -W Display with no host column truncation"
17 //usage: "\n -f FILE Read from FILE instead of /var/log/wtmp"
22 /* NB: ut_name and ut_user are the same field, use only one name (ut_user)
23 * to reduce confusion */
26 # define SHUTDOWN_TIME 254
29 /* Grr... utmp char[] members do not have to be nul-terminated.
30 * Do what we can while still keeping this reasonably small.
31 * Note: We are assuming the ut_id[] size is fixed at 4. */
33 #if defined UT_LINESIZE \
34 && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256))
35 #error struct utmp member char[] size(s) have changed!
36 #elif defined __UT_LINESIZE \
37 && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256))
38 #error struct utmp member char[] size(s) have changed!
41 #if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \
43 #error Values for the ut_type field of struct utmp changed
46 int last_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
47 int last_main(int argc UNUSED_PARAM
, char **argv UNUSED_PARAM
)
50 int n
, file
= STDIN_FILENO
;
53 static const char _ut_usr
[] ALIGN1
=
54 "runlevel\0" "reboot\0" "shutdown\0";
55 static const char _ut_lin
[] ALIGN1
=
56 "~\0" "{\0" "|\0" /* "LOGIN\0" "date\0" */;
58 TYPE_RUN_LVL
= RUN_LVL
, /* 1 */
59 TYPE_BOOT_TIME
= BOOT_TIME
, /* 2 */
60 TYPE_SHUTDOWN_TIME
= SHUTDOWN_TIME
63 _TILDE
= EMPTY
, /* 0 */
64 TYPE_NEW_TIME
, /* NEW_TIME, 3 */
65 TYPE_OLD_TIME
/* OLD_TIME, 4 */
71 file
= xopen(bb_path_wtmp_file
, O_RDONLY
);
73 printf("%-10s %-14s %-18s %-12.12s %s\n",
74 "USER", "TTY", "HOST", "LOGIN", "TIME");
75 /* yikes. We reverse over the file and that is a not too elegant way */
76 pos
= xlseek(file
, 0, SEEK_END
);
77 pos
= lseek(file
, pos
- sizeof(ut
), SEEK_SET
);
78 while ((n
= full_read(file
, &ut
, sizeof(ut
))) > 0) {
79 if (n
!= sizeof(ut
)) {
80 bb_perror_msg_and_die("short read");
82 n
= index_in_strings(_ut_lin
, ut
.ut_line
);
83 if (n
== _TILDE
) { /* '~' */
85 /* do we really need to be cautious here? */
86 n
= index_in_strings(_ut_usr
, ut
.ut_user
);
88 ut
.ut_type
= n
!= 3 ? n
: SHUTDOWN_TIME
;
90 if (strncmp(ut
.ut_user
, "shutdown", 8) == 0)
91 ut
.ut_type
= SHUTDOWN_TIME
;
92 else if (strncmp(ut
.ut_user
, "reboot", 6) == 0)
93 ut
.ut_type
= BOOT_TIME
;
94 else if (strncmp(ut
.ut_user
, "runlevel", 8) == 0)
98 if (ut
.ut_user
[0] == '\0' || strcmp(ut
.ut_user
, "LOGIN") == 0) {
99 /* Don't bother. This means we can't find how long
100 * someone was logged in for. Oh well. */
103 if (ut
.ut_type
!= DEAD_PROCESS
107 ut
.ut_type
= USER_PROCESS
;
109 if (strcmp(ut
.ut_user
, "date") == 0) {
110 if (n
== TYPE_OLD_TIME
) { /* '|' */
111 ut
.ut_type
= OLD_TIME
;
113 if (n
== TYPE_NEW_TIME
) { /* '{' */
114 ut
.ut_type
= NEW_TIME
;
119 if (ut
.ut_type
!= USER_PROCESS
) {
120 switch (ut
.ut_type
) {
127 strcpy(ut
.ut_line
, "system boot");
130 /* manpages say ut_tv.tv_sec *is* time_t,
131 * but some systems have it wrong */
132 t_tmp
= (time_t)ut
.ut_tv
.tv_sec
;
133 printf("%-10s %-14s %-18s %-12.12s\n",
134 ut
.ut_user
, ut
.ut_line
, ut
.ut_host
, ctime(&t_tmp
) + 4);
139 xlseek(file
, pos
, SEEK_SET
);
142 fflush_stdout_and_exit(EXIT_SUCCESS
);