Tomato 1.28
[tomato.git] / release / src / router / busybox / miscutils / last_fancy.c
blobf3ea0375d3006fdec45a84d650a284e06b1300a8
1 /* vi: set sw=4 ts=4: */
2 /*
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.
8 */
10 #include "libbb.h"
11 #include <utmp.h>
13 /* NB: ut_name and ut_user are the same field, use only one name (ut_user)
14 * to reduce confusion */
16 #ifndef SHUTDOWN_TIME
17 # define SHUTDOWN_TIME 254
18 #endif
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", ""
26 enum {
27 NORMAL,
28 LOGGED,
29 DOWN,
30 REBOOT,
31 CRASH,
32 GONE
35 enum {
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;
46 char duration[32];
47 char login_time[17];
48 char logout_time[8];
49 const char *logout_str;
50 const char *duration_str;
51 time_t tmp;
53 /* manpages say ut_tv.tv_sec *is* time_t,
54 * but some systems have it wrong */
55 tmp = ut->ut_tv.tv_sec;
56 safe_strncpy(login_time, ctime(&tmp), 17);
57 snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
59 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
60 /* unsigned int is easier to divide than time_t (which may be signed long) */
61 mins = dur_secs / 60;
62 days = mins / (24*60);
63 mins = mins % (24*60);
64 hours = mins / 60;
65 mins = mins % 60;
67 // if (days) {
68 sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
69 // } else {
70 // sprintf(duration, " (%02u:%02u)", hours, mins);
71 // }
73 logout_str = logout_time;
74 duration_str = duration;
75 switch (state) {
76 case NORMAL:
77 break;
78 case LOGGED:
79 logout_str = " still";
80 duration_str = "logged in";
81 break;
82 case DOWN:
83 logout_str = "- down ";
84 break;
85 case REBOOT:
86 break;
87 case CRASH:
88 logout_str = "- crash";
89 break;
90 case GONE:
91 logout_str = " gone";
92 duration_str = "- no logout";
93 break;
96 printf(HEADER_FORMAT,
97 ut->ut_user,
98 ut->ut_line,
99 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
100 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
101 ut->ut_host,
102 login_time,
103 logout_str,
104 duration_str);
107 static int get_ut_type(struct utmp *ut)
109 if (ut->ut_line[0] == '~') {
110 if (strcmp(ut->ut_user, "shutdown") == 0) {
111 return SHUTDOWN_TIME;
113 if (strcmp(ut->ut_user, "reboot") == 0) {
114 return BOOT_TIME;
116 if (strcmp(ut->ut_user, "runlevel") == 0) {
117 return RUN_LVL;
119 return ut->ut_type;
122 if (ut->ut_user[0] == 0) {
123 return DEAD_PROCESS;
126 if ((ut->ut_type != DEAD_PROCESS)
127 && (strcmp(ut->ut_user, "LOGIN") != 0)
128 && ut->ut_user[0]
129 && ut->ut_line[0]
131 ut->ut_type = USER_PROCESS;
134 if (strcmp(ut->ut_user, "date") == 0) {
135 if (ut->ut_line[0] == '|') {
136 return OLD_TIME;
138 if (ut->ut_line[0] == '{') {
139 return NEW_TIME;
142 return ut->ut_type;
145 static int is_runlevel_shutdown(struct utmp *ut)
147 if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
148 return 1;
151 return 0;
154 int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
155 int last_main(int argc UNUSED_PARAM, char **argv)
157 struct utmp ut;
158 const char *filename = _PATH_WTMP;
159 llist_t *zlist;
160 off_t pos;
161 time_t start_time;
162 time_t boot_time;
163 time_t down_time;
164 int file;
165 unsigned opt;
166 smallint going_down;
167 smallint boot_down;
169 opt = getopt32(argv, "Wf:" /* "H" */, &filename);
170 #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
171 if (opt & LAST_OPT_H) {
172 /* Print header line */
173 if (opt & LAST_OPT_W) {
174 printf(HEADER_FORMAT, HEADER_LINE_WIDE);
175 } else {
176 printf(HEADER_FORMAT, HEADER_LINE);
179 #endif
181 file = xopen(filename, O_RDONLY);
183 /* in case the file is empty... */
184 struct stat st;
185 fstat(file, &st);
186 start_time = st.st_ctime;
189 time(&down_time);
190 going_down = 0;
191 boot_down = NORMAL; /* 0 */
192 zlist = NULL;
193 boot_time = 0;
194 /* get file size, rounding down to last full record */
195 pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut);
196 for (;;) {
197 pos -= (off_t)sizeof(ut);
198 if (pos < 0) {
199 /* Beyond the beginning of the file boundary =>
200 * the whole file has been read. */
201 break;
203 xlseek(file, pos, SEEK_SET);
204 xread(file, &ut, sizeof(ut));
205 /* rewritten by each record, eventially will have
206 * first record's ut_tv.tv_sec: */
207 start_time = ut.ut_tv.tv_sec;
209 switch (get_ut_type(&ut)) {
210 case SHUTDOWN_TIME:
211 down_time = ut.ut_tv.tv_sec;
212 boot_down = DOWN;
213 going_down = 1;
214 break;
215 case RUN_LVL:
216 if (is_runlevel_shutdown(&ut)) {
217 down_time = ut.ut_tv.tv_sec;
218 going_down = 1;
219 boot_down = DOWN;
221 break;
222 case BOOT_TIME:
223 strcpy(ut.ut_line, "system boot");
224 show_entry(&ut, REBOOT, down_time);
225 boot_down = CRASH;
226 going_down = 1;
227 break;
228 case DEAD_PROCESS:
229 if (!ut.ut_line[0]) {
230 break;
232 /* add_entry */
233 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
234 break;
235 case USER_PROCESS: {
236 int show;
238 if (!ut.ut_line[0]) {
239 break;
241 /* find_entry */
242 show = 1;
244 llist_t *el, *next;
245 for (el = zlist; el; el = next) {
246 struct utmp *up = (struct utmp *)el->data;
247 next = el->link;
248 if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
249 if (show) {
250 show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
251 show = 0;
253 llist_unlink(&zlist, el);
254 free(el->data);
255 free(el);
260 if (show) {
261 int state = boot_down;
263 if (boot_time == 0) {
264 state = LOGGED;
265 /* Check if the process is alive */
266 if ((ut.ut_pid > 0)
267 && (kill(ut.ut_pid, 0) != 0)
268 && (errno == ESRCH)) {
269 state = GONE;
272 show_entry(&ut, state, boot_time);
274 /* add_entry */
275 llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
276 break;
280 if (going_down) {
281 boot_time = ut.ut_tv.tv_sec;
282 llist_free(zlist, free);
283 zlist = NULL;
284 going_down = 0;
288 if (ENABLE_FEATURE_CLEAN_UP) {
289 llist_free(zlist, free);
292 printf("\nwtmp begins %s", ctime(&start_time));
294 if (ENABLE_FEATURE_CLEAN_UP)
295 close(file);
296 fflush_stdout_and_exit(EXIT_SUCCESS);