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.
13 /* NB: ut_name and ut_user are the same field, use only one name (ut_user)
14 * to reduce confusion */
17 # define SHUTDOWN_TIME 254
20 #define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n"
21 #define HEADER_LINE "USER", "TTY", \
22 INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
23 #define HEADER_LINE_WIDE "USER", "TTY", \
24 INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
36 LAST_OPT_W = (1 << 0), /* -W wide */
37 LAST_OPT_f = (1 << 1), /* -f input file */
38 LAST_OPT_H = (1 << 2), /* -H header */
41 #define show_wide (option_mask32 & LAST_OPT_W)
43 static void show_entry(struct utmp *ut, int state, time_t dur_secs)
45 unsigned days, hours, mins;
49 const char *logout_str;
50 const char *duration_str;
52 safe_strncpy(login_time, ctime(&(ut->ut_tv.tv_sec)), 17);
53 snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
55 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
56 /* unsigned int is easier to divide than time_t (which may be signed long) */
58 days = mins / (24*60);
59 mins = mins % (24*60);
64 sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
66 // sprintf(duration, " (%02u:%02u)", hours, mins);
69 logout_str = logout_time;
70 duration_str = duration;
75 logout_str = " still";
76 duration_str = "logged in";
79 logout_str = "- down ";
84 logout_str = "- crash";
88 duration_str = "- no logout";
95 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
96 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
103 static int get_ut_type(struct utmp *ut)
105 if (ut->ut_line[0] == '~') {
106 if (strcmp(ut->ut_user, "shutdown") == 0) {
107 return SHUTDOWN_TIME;
109 if (strcmp(ut->ut_user, "reboot") == 0) {
112 if (strcmp(ut->ut_user, "runlevel") == 0) {
118 if (ut->ut_user[0] == 0) {
122 if ((ut->ut_type != DEAD_PROCESS)
123 && (strcmp(ut->ut_user, "LOGIN") != 0)
127 ut->ut_type = USER_PROCESS;
130 if (strcmp(ut->ut_user, "date") == 0) {
131 if (ut->ut_line[0] == '|') {
134 if (ut->ut_line[0] == '{') {
141 static int is_runlevel_shutdown(struct utmp *ut)
143 if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
150 int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
151 int last_main(int argc ATTRIBUTE_UNUSED, char **argv)
154 const char *filename = _PATH_WTMP;
165 opt = getopt32(argv, "Wf:" /* "H" */, &filename);
166 #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
167 if (opt & LAST_OPT_H) {
168 /* Print header line */
169 if (opt & LAST_OPT_W) {
170 printf(HEADER_FORMAT, HEADER_LINE_WIDE);
172 printf(HEADER_FORMAT, HEADER_LINE);
177 file = xopen(filename, O_RDONLY);
179 /* in case the file is empty... */
182 start_time = st.st_ctime;
187 boot_down = NORMAL; /* 0 */
190 /* get file size, rounding down to last full record */
191 pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut);
193 pos -= (off_t)sizeof(ut);
195 /* Beyond the beginning of the file boundary =>
196 * the whole file has been read. */
199 xlseek(file, pos, SEEK_SET);
200 xread(file, &ut, sizeof(ut));
201 /* rewritten by each record, eventially will have
202 * first record's ut_tv.tv_sec: */
203 start_time = ut.ut_tv.tv_sec;
205 switch (get_ut_type(&ut)) {
207 down_time = ut.ut_tv.tv_sec;
212 if (is_runlevel_shutdown(&ut)) {
213 down_time = ut.ut_tv.tv_sec;
219 strcpy(ut.ut_line, "system boot");
220 show_entry(&ut, REBOOT, down_time);
225 if (!ut.ut_line[0]) {
229 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
234 if (!ut.ut_line[0]) {
241 for (el = zlist; el; el = next) {
242 struct utmp *up = (struct utmp *)el->data;
244 if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
246 show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
249 llist_unlink(&zlist, el);
257 int state = boot_down;
259 if (boot_time == 0) {
261 /* Check if the process is alive */
263 && (kill(ut.ut_pid, 0) != 0)
264 && (errno == ESRCH)) {
268 show_entry(&ut, state, boot_time);
271 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
277 boot_time = ut.ut_tv.tv_sec;
278 llist_free(zlist, free);
284 if (ENABLE_FEATURE_CLEAN_UP) {
285 llist_free(zlist, free);
288 printf("\nwtmp begins %s", ctime(&start_time));
290 if (ENABLE_FEATURE_CLEAN_UP)
292 fflush_stdout_and_exit(EXIT_SUCCESS);