source: MondoRescue/branches/3.3/mindi-busybox/miscutils/last_fancy.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 6.3 KB
Line 
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 GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10#include "libbb.h"
11
12/* NB: ut_name and ut_user are the same field, use only one name (ut_user)
13 * to reduce confusion */
14
15#ifndef SHUTDOWN_TIME
16# define SHUTDOWN_TIME 254
17#endif
18
19#define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n"
20#define HEADER_LINE "USER", "TTY", \
21 INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
22#define HEADER_LINE_WIDE "USER", "TTY", \
23 INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
24
25#if !defined __UT_LINESIZE && defined UT_LINESIZE
26# define __UT_LINESIZE UT_LINESIZE
27#endif
28
29enum {
30 NORMAL,
31 LOGGED,
32 DOWN,
33 REBOOT,
34 CRASH,
35 GONE
36};
37
38enum {
39 LAST_OPT_W = (1 << 0), /* -W wide */
40 LAST_OPT_f = (1 << 1), /* -f input file */
41 LAST_OPT_H = (1 << 2), /* -H header */
42};
43
44#define show_wide (option_mask32 & LAST_OPT_W)
45
46static void show_entry(struct utmpx *ut, int state, time_t dur_secs)
47{
48 unsigned days, hours, mins;
49 char duration[sizeof("(%u+02:02)") + sizeof(int)*3];
50 char login_time[17];
51 char logout_time[8];
52 const char *logout_str;
53 const char *duration_str;
54 time_t tmp;
55
56 /* manpages say ut_tv.tv_sec *is* time_t,
57 * but some systems have it wrong */
58 tmp = ut->ut_tv.tv_sec;
59 safe_strncpy(login_time, ctime(&tmp), 17);
60 tmp = dur_secs;
61 snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11);
62
63 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
64 /* unsigned int is easier to divide than time_t (which may be signed long) */
65 mins = dur_secs / 60;
66 days = mins / (24*60);
67 mins = mins % (24*60);
68 hours = mins / 60;
69 mins = mins % 60;
70
71// if (days) {
72 sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
73// } else {
74// sprintf(duration, " (%02u:%02u)", hours, mins);
75// }
76
77 logout_str = logout_time;
78 duration_str = duration;
79 switch (state) {
80 case NORMAL:
81 break;
82 case LOGGED:
83 logout_str = " still";
84 duration_str = "logged in";
85 break;
86 case DOWN:
87 logout_str = "- down ";
88 break;
89 case REBOOT:
90 break;
91 case CRASH:
92 logout_str = "- crash";
93 break;
94 case GONE:
95 logout_str = " gone";
96 duration_str = "- no logout";
97 break;
98 }
99
100 printf(HEADER_FORMAT,
101 ut->ut_user,
102 ut->ut_line,
103 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
104 show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
105 ut->ut_host,
106 login_time,
107 logout_str,
108 duration_str);
109}
110
111static int get_ut_type(struct utmpx *ut)
112{
113 if (ut->ut_line[0] == '~') {
114 if (strcmp(ut->ut_user, "shutdown") == 0) {
115 return SHUTDOWN_TIME;
116 }
117 if (strcmp(ut->ut_user, "reboot") == 0) {
118 return BOOT_TIME;
119 }
120 if (strcmp(ut->ut_user, "runlevel") == 0) {
121 return RUN_LVL;
122 }
123 return ut->ut_type;
124 }
125
126 if (ut->ut_user[0] == 0) {
127 return DEAD_PROCESS;
128 }
129
130 if ((ut->ut_type != DEAD_PROCESS)
131 && (strcmp(ut->ut_user, "LOGIN") != 0)
132 && ut->ut_user[0]
133 && ut->ut_line[0]
134 ) {
135 ut->ut_type = USER_PROCESS;
136 }
137
138 if (strcmp(ut->ut_user, "date") == 0) {
139 if (ut->ut_line[0] == '|') {
140 return OLD_TIME;
141 }
142 if (ut->ut_line[0] == '{') {
143 return NEW_TIME;
144 }
145 }
146 return ut->ut_type;
147}
148
149static int is_runlevel_shutdown(struct utmpx *ut)
150{
151 if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
152 return 1;
153 }
154
155 return 0;
156}
157
158int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
159int last_main(int argc UNUSED_PARAM, char **argv)
160{
161 struct utmpx ut;
162 const char *filename = _PATH_WTMP;
163 llist_t *zlist;
164 off_t pos;
165 time_t start_time;
166 time_t boot_time;
167 time_t down_time;
168 int file;
169 smallint going_down;
170 smallint boot_down;
171
172 /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename);
173#ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
174 if (opt & LAST_OPT_H) {
175 /* Print header line */
176 if (opt & LAST_OPT_W) {
177 printf(HEADER_FORMAT, HEADER_LINE_WIDE);
178 } else {
179 printf(HEADER_FORMAT, HEADER_LINE);
180 }
181 }
182#endif
183
184 file = xopen(filename, O_RDONLY);
185 {
186 /* in case the file is empty... */
187 struct stat st;
188 fstat(file, &st);
189 start_time = st.st_ctime;
190 }
191
192 time(&down_time);
193 going_down = 0;
194 boot_down = NORMAL; /* 0 */
195 zlist = NULL;
196 boot_time = 0;
197 /* get file size, rounding down to last full record */
198 pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut);
199 for (;;) {
200 pos -= (off_t)sizeof(ut);
201 if (pos < 0) {
202 /* Beyond the beginning of the file boundary =>
203 * the whole file has been read. */
204 break;
205 }
206 xlseek(file, pos, SEEK_SET);
207 xread(file, &ut, sizeof(ut));
208 /* rewritten by each record, eventially will have
209 * first record's ut_tv.tv_sec: */
210 start_time = ut.ut_tv.tv_sec;
211
212 switch (get_ut_type(&ut)) {
213 case SHUTDOWN_TIME:
214 down_time = ut.ut_tv.tv_sec;
215 boot_down = DOWN;
216 going_down = 1;
217 break;
218 case RUN_LVL:
219 if (is_runlevel_shutdown(&ut)) {
220 down_time = ut.ut_tv.tv_sec;
221 going_down = 1;
222 boot_down = DOWN;
223 }
224 break;
225 case BOOT_TIME:
226 strcpy(ut.ut_line, "system boot");
227 show_entry(&ut, REBOOT, down_time);
228 boot_down = CRASH;
229 going_down = 1;
230 break;
231 case DEAD_PROCESS:
232 if (!ut.ut_line[0]) {
233 break;
234 }
235 /* add_entry */
236 llist_add_to(&zlist, xmemdup(&ut, sizeof(ut)));
237 break;
238 case USER_PROCESS: {
239 int show;
240
241 if (!ut.ut_line[0]) {
242 break;
243 }
244 /* find_entry */
245 show = 1;
246 {
247 llist_t *el, *next;
248 for (el = zlist; el; el = next) {
249 struct utmpx *up = (struct utmpx *)el->data;
250 next = el->link;
251 if (strncmp(up->ut_line, ut.ut_line, __UT_LINESIZE) == 0) {
252 if (show) {
253 show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
254 show = 0;
255 }
256 llist_unlink(&zlist, el);
257 free(el->data);
258 free(el);
259 }
260 }
261 }
262
263 if (show) {
264 int state = boot_down;
265
266 if (boot_time == 0) {
267 state = LOGGED;
268 /* Check if the process is alive */
269 if ((ut.ut_pid > 0)
270 && (kill(ut.ut_pid, 0) != 0)
271 && (errno == ESRCH)) {
272 state = GONE;
273 }
274 }
275 show_entry(&ut, state, boot_time);
276 }
277 /* add_entry */
278 llist_add_to(&zlist, xmemdup(&ut, sizeof(ut)));
279 break;
280 }
281 }
282
283 if (going_down) {
284 boot_time = ut.ut_tv.tv_sec;
285 llist_free(zlist, free);
286 zlist = NULL;
287 going_down = 0;
288 }
289 }
290
291 if (ENABLE_FEATURE_CLEAN_UP) {
292 llist_free(zlist, free);
293 }
294
295 printf("\nwtmp begins %s", ctime(&start_time));
296
297 if (ENABLE_FEATURE_CLEAN_UP)
298 close(file);
299 fflush_stdout_and_exit(EXIT_SUCCESS);
300}
Note: See TracBrowser for help on using the repository browser.