1 /* vi: set sw=4 ts=4: */
3 * (sysvinit like) last implementation
5 * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com>
7 * Licensed under the GPLv2 or later, see the file LICENSE in this tarball.
10 #define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n"
11 #define HEADER_LINE "USER", "TTY", \
12 INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
13 #define HEADER_LINE_WIDE "USER", "TTY", \
14 INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
26 LAST_OPT_W = (1 << 0), /* -W wide */
27 LAST_OPT_f = (1 << 1), /* -f input file */
28 LAST_OPT_H = (1 << 2), /* -H header */
31 #define show_wide (option_mask32 & LAST_OPT_W)
33 static void show_entry(struct utmp *ut, int state, time_t dur_secs)
35 unsigned days, hours, mins;
39 const char *logout_str;
40 const char *duration_str;
42 safe_strncpy(login_time, ctime(&(ut->ut_time)), 17);
43 snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
45 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
46 /* unsigned int is easier to divide than time_t (which may be signed long) */
48 days = mins / (24*60);
49 mins = mins % (24*60);
54 sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
56 sprintf(duration, " (%02u:%02u)", hours, mins);
59 logout_str = logout_time;
60 duration_str = duration;
65 logout_str = " still";
66 duration_str = "logged in";
69 logout_str = "- down ";
74 logout_str = "- crash";
78 duration_str = "- no logout";
85 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
86 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
93 static int get_ut_type(struct utmp *ut)
95 if (ut->ut_line[0] == '~') {
96 if (strncmp(ut->ut_user, "shutdown", sizeof("shutdown")-1) == 0) {
99 if (strncmp(ut->ut_user, "reboot", sizeof("reboot")-1) == 0) {
102 if (strncmp(ut->ut_user, "runlevel", sizeof("runlevel")-1) == 0) {
108 if (ut->ut_name[0] == 0) {
112 if ((ut->ut_type != DEAD_PROCESS)
113 && (strcmp(ut->ut_name, "LOGIN") != 0)
117 ut->ut_type = USER_PROCESS;
120 if (strcmp(ut->ut_name, "date") == 0) {
121 if (ut->ut_line[0] == '|') {
124 if (ut->ut_line[0] == '{') {
131 static int is_runlevel_shutdown(struct utmp *ut)
133 if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
140 int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
141 int last_main(int argc ATTRIBUTE_UNUSED, char **argv)
144 const char *filename = _PATH_WTMP;
155 opt = getopt32(argv, "Wf:" /* "H" */, &filename);
156 #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
157 if (opt & LAST_OPT_H) {
158 /* Print header line */
159 if (opt & LAST_OPT_W) {
160 printf(HEADER_FORMAT, HEADER_LINE_WIDE);
162 printf(HEADER_FORMAT, HEADER_LINE);
167 file = xopen(filename, O_RDONLY);
168 if (full_read(file, &ut, sizeof(ut)) != sizeof(ut)) {
171 start_time = st.st_ctime;
174 start_time = ut.ut_time;
178 boot_down = NORMAL; /* 0 */
183 pos -= (off_t)sizeof(ut);
184 /* Bug? What if file changes size?
185 * What if size is not a multiple of sizeof(ut)? */
186 if (lseek(file, pos, SEEK_END) < 0) {
187 /* Beyond the beginning of the file boundary =>
188 * the whole file has been read. */
191 if (full_read(file, &ut, sizeof(ut)) != sizeof(ut))
194 switch (get_ut_type(&ut)) {
196 down_time = ut.ut_time;
201 if (is_runlevel_shutdown(&ut)) {
202 down_time = ut.ut_time;
208 strcpy(ut.ut_line, "system boot");
209 show_entry(&ut, REBOOT, down_time);
214 if (!ut.ut_line[0]) {
218 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
223 if (!ut.ut_line[0]) {
230 for (el = zlist; el; el = next) {
231 struct utmp *up = (struct utmp *)el->data;
233 if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
235 show_entry(&ut, NORMAL, up->ut_time);
238 llist_unlink(&zlist, el);
246 int state = boot_down;
248 if (boot_time == 0) {
250 /* Check if the process is alive */
252 && (kill(ut.ut_pid, 0) != 0)
253 && (errno == ESRCH)) {
257 show_entry(&ut, state, boot_time);
260 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
266 boot_time = ut.ut_time;
267 llist_free(zlist, free);
273 if (ENABLE_FEATURE_CLEAN_UP) {
274 llist_free(zlist, free);
278 printf("\nwtmp begins %s", ctime(&start_time));
280 if (ENABLE_FEATURE_CLEAN_UP)
282 fflush_stdout_and_exit(EXIT_SUCCESS);