Contents of /tags/mkinitrd-6_3_0/busybox/miscutils/last_fancy.c
Parent Directory | Revision Log
Revision 1139 -
(show annotations)
(download)
Thu Aug 19 10:14:02 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 6391 byte(s)
Thu Aug 19 10:14:02 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 6391 byte(s)
tagged 'mkinitrd-6_3_0'
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * (sysvinit like) last implementation |
4 | * |
5 | * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com> |
6 | * |
7 | * Licensed under the GPLv2 or later, see the file LICENSE in this tarball. |
8 | */ |
9 | |
10 | #include "libbb.h" |
11 | #include <utmp.h> |
12 | |
13 | /* NB: ut_name and ut_user are the same field, use only one name (ut_user) |
14 | * to reduce confusion */ |
15 | |
16 | #ifndef SHUTDOWN_TIME |
17 | # define SHUTDOWN_TIME 254 |
18 | #endif |
19 | |
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", "" |
25 | |
26 | enum { |
27 | NORMAL, |
28 | LOGGED, |
29 | DOWN, |
30 | REBOOT, |
31 | CRASH, |
32 | GONE |
33 | }; |
34 | |
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 */ |
39 | }; |
40 | |
41 | #define show_wide (option_mask32 & LAST_OPT_W) |
42 | |
43 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) |
44 | { |
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; |
52 | |
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); |
58 | |
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; |
66 | |
67 | // if (days) { |
68 | sprintf(duration, "(%u+%02u:%02u)", days, hours, mins); |
69 | // } else { |
70 | // sprintf(duration, " (%02u:%02u)", hours, mins); |
71 | // } |
72 | |
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; |
94 | } |
95 | |
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); |
105 | } |
106 | |
107 | static int get_ut_type(struct utmp *ut) |
108 | { |
109 | if (ut->ut_line[0] == '~') { |
110 | if (strcmp(ut->ut_user, "shutdown") == 0) { |
111 | return SHUTDOWN_TIME; |
112 | } |
113 | if (strcmp(ut->ut_user, "reboot") == 0) { |
114 | return BOOT_TIME; |
115 | } |
116 | if (strcmp(ut->ut_user, "runlevel") == 0) { |
117 | return RUN_LVL; |
118 | } |
119 | return ut->ut_type; |
120 | } |
121 | |
122 | if (ut->ut_user[0] == 0) { |
123 | return DEAD_PROCESS; |
124 | } |
125 | |
126 | if ((ut->ut_type != DEAD_PROCESS) |
127 | && (strcmp(ut->ut_user, "LOGIN") != 0) |
128 | && ut->ut_user[0] |
129 | && ut->ut_line[0] |
130 | ) { |
131 | ut->ut_type = USER_PROCESS; |
132 | } |
133 | |
134 | if (strcmp(ut->ut_user, "date") == 0) { |
135 | if (ut->ut_line[0] == '|') { |
136 | return OLD_TIME; |
137 | } |
138 | if (ut->ut_line[0] == '{') { |
139 | return NEW_TIME; |
140 | } |
141 | } |
142 | return ut->ut_type; |
143 | } |
144 | |
145 | static int is_runlevel_shutdown(struct utmp *ut) |
146 | { |
147 | if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { |
148 | return 1; |
149 | } |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
155 | int last_main(int argc UNUSED_PARAM, char **argv) |
156 | { |
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; |
168 | |
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); |
177 | } |
178 | } |
179 | #endif |
180 | |
181 | file = xopen(filename, O_RDONLY); |
182 | { |
183 | /* in case the file is empty... */ |
184 | struct stat st; |
185 | fstat(file, &st); |
186 | start_time = st.st_ctime; |
187 | } |
188 | |
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; |
202 | } |
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; |
208 | |
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; |
220 | } |
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; |
231 | } |
232 | /* add_entry */ |
233 | llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); |
234 | break; |
235 | case USER_PROCESS: { |
236 | int show; |
237 | |
238 | if (!ut.ut_line[0]) { |
239 | break; |
240 | } |
241 | /* find_entry */ |
242 | show = 1; |
243 | { |
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; |
252 | } |
253 | llist_unlink(&zlist, el); |
254 | free(el->data); |
255 | free(el); |
256 | } |
257 | } |
258 | } |
259 | |
260 | if (show) { |
261 | int state = boot_down; |
262 | |
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; |
270 | } |
271 | } |
272 | show_entry(&ut, state, boot_time); |
273 | } |
274 | /* add_entry */ |
275 | llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); |
276 | break; |
277 | } |
278 | } |
279 | |
280 | if (going_down) { |
281 | boot_time = ut.ut_tv.tv_sec; |
282 | llist_free(zlist, free); |
283 | zlist = NULL; |
284 | going_down = 0; |
285 | } |
286 | } |
287 | |
288 | if (ENABLE_FEATURE_CLEAN_UP) { |
289 | llist_free(zlist, free); |
290 | } |
291 | |
292 | printf("\nwtmp begins %s", ctime(&start_time)); |
293 | |
294 | if (ENABLE_FEATURE_CLEAN_UP) |
295 | close(file); |
296 | fflush_stdout_and_exit(EXIT_SUCCESS); |
297 | } |