source: MondoRescue/branches/3.2/mindi-busybox/miscutils/last_fancy.c@ 3232

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