diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-05-22 02:07:58 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-05-22 02:07:58 +0000 |
commit | 869d3d318ff3c17d74499f1216abfe3afa40590c (patch) | |
tree | f76f5abd31348c95c73d9455fef5f3fb38291686 /miscutils/last_fancy.c | |
parent | 7bd8d8fd264a72f9c228da3ca238eae1b2ad8ce1 (diff) | |
download | busybox-w32-869d3d318ff3c17d74499f1216abfe3afa40590c.tar.gz busybox-w32-869d3d318ff3c17d74499f1216abfe3afa40590c.tar.bz2 busybox-w32-869d3d318ff3c17d74499f1216abfe3afa40590c.zip |
last: optional alternative sysv-like implementation
(by Patricia Muscalu <patricia.muscalu AT axis.com>)
function old new delta
last_main 448 917 +469
show_entry - 319 +319
packed_usage 24216 24268 +52
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/0 up/down: 840/0) Total: 840 bytes
Diffstat (limited to 'miscutils/last_fancy.c')
-rw-r--r-- | miscutils/last_fancy.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c new file mode 100644 index 000000000..0ad2b9245 --- /dev/null +++ b/miscutils/last_fancy.c | |||
@@ -0,0 +1,283 @@ | |||
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 | #define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %-12.12s\n" | ||
11 | #define HEADER_LINE "USER", "TTY", \ | ||
12 | INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" | ||
13 | #define HEADER_LINE_WIDE "USER", "TTY", \ | ||
14 | INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" | ||
15 | |||
16 | enum { | ||
17 | NORMAL, | ||
18 | LOGGED, | ||
19 | DOWN, | ||
20 | REBOOT, | ||
21 | CRASH, | ||
22 | GONE | ||
23 | }; | ||
24 | |||
25 | enum { | ||
26 | LAST_OPT_W = (1 << 0), /* -W wide */ | ||
27 | LAST_OPT_f = (1 << 1), /* -f input file */ | ||
28 | LAST_OPT_H = (1 << 2), /* -H header */ | ||
29 | }; | ||
30 | |||
31 | #define show_wide (option_mask32 & LAST_OPT_W) | ||
32 | |||
33 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) | ||
34 | { | ||
35 | unsigned days, hours, mins; | ||
36 | char duration[32]; | ||
37 | char login_time[17]; | ||
38 | char logout_time[8]; | ||
39 | const char *logout_str; | ||
40 | const char *duration_str; | ||
41 | |||
42 | safe_strncpy(login_time, ctime(&(ut->ut_time)), 17); | ||
43 | snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11); | ||
44 | |||
45 | dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); | ||
46 | /* unsigned int is easier to divide than time_t (which may be signed long) */ | ||
47 | mins = dur_secs / 60; | ||
48 | days = mins / (24*60); | ||
49 | mins = mins % (24*60); | ||
50 | hours = mins / 60; | ||
51 | mins = mins % 60; | ||
52 | |||
53 | if (days) { | ||
54 | sprintf(duration, "(%u+%02u:%02u)", days, hours, mins); | ||
55 | } else { | ||
56 | sprintf(duration, " (%02u:%02u)", hours, mins); | ||
57 | } | ||
58 | |||
59 | logout_str = logout_time; | ||
60 | duration_str = duration; | ||
61 | switch (state) { | ||
62 | case NORMAL: | ||
63 | break; | ||
64 | case LOGGED: | ||
65 | logout_str = " still"; | ||
66 | duration_str = "logged in"; | ||
67 | break; | ||
68 | case DOWN: | ||
69 | logout_str = "- down "; | ||
70 | break; | ||
71 | case REBOOT: | ||
72 | break; | ||
73 | case CRASH: | ||
74 | logout_str = "- crash"; | ||
75 | break; | ||
76 | case GONE: | ||
77 | logout_str = " gone"; | ||
78 | duration_str = "- no logout"; | ||
79 | break; | ||
80 | } | ||
81 | |||
82 | printf(HEADER_FORMAT, | ||
83 | ut->ut_name, | ||
84 | ut->ut_line, | ||
85 | show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, | ||
86 | show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, | ||
87 | ut->ut_host, | ||
88 | login_time, | ||
89 | logout_str, | ||
90 | duration_str); | ||
91 | } | ||
92 | |||
93 | static int get_ut_type(struct utmp *ut) | ||
94 | { | ||
95 | if (ut->ut_line[0] == '~') { | ||
96 | if (strncmp(ut->ut_user, "shutdown", sizeof("shutdown")-1) == 0) { | ||
97 | return SHUTDOWN_TIME; | ||
98 | } | ||
99 | if (strncmp(ut->ut_user, "reboot", sizeof("reboot")-1) == 0) { | ||
100 | return BOOT_TIME; | ||
101 | } | ||
102 | if (strncmp(ut->ut_user, "runlevel", sizeof("runlevel")-1) == 0) { | ||
103 | return RUN_LVL; | ||
104 | } | ||
105 | return ut->ut_type; | ||
106 | } | ||
107 | |||
108 | if (ut->ut_name[0] == 0) { | ||
109 | return DEAD_PROCESS; | ||
110 | } | ||
111 | |||
112 | if ((ut->ut_type != DEAD_PROCESS) | ||
113 | && (strcmp(ut->ut_name, "LOGIN") != 0) | ||
114 | && ut->ut_name[0] | ||
115 | && ut->ut_line[0] | ||
116 | ) { | ||
117 | ut->ut_type = USER_PROCESS; | ||
118 | } | ||
119 | |||
120 | if (strcmp(ut->ut_name, "date") == 0) { | ||
121 | if (ut->ut_line[0] == '|') { | ||
122 | return OLD_TIME; | ||
123 | } | ||
124 | if (ut->ut_line[0] == '{') { | ||
125 | return NEW_TIME; | ||
126 | } | ||
127 | } | ||
128 | return ut->ut_type; | ||
129 | } | ||
130 | |||
131 | static int is_runlevel_shutdown(struct utmp *ut) | ||
132 | { | ||
133 | if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { | ||
134 | return 1; | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
141 | int last_main(int argc ATTRIBUTE_UNUSED, char **argv) | ||
142 | { | ||
143 | struct utmp ut; | ||
144 | const char *filename = _PATH_WTMP; | ||
145 | llist_t *zlist; | ||
146 | off_t pos; | ||
147 | time_t start_time; | ||
148 | time_t boot_time; | ||
149 | time_t down_time; | ||
150 | int file; | ||
151 | unsigned opt; | ||
152 | smallint going_down; | ||
153 | smallint boot_down; | ||
154 | |||
155 | opt = getopt32(argv, "Wf:" /* "H" */, &filename); | ||
156 | #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT | ||
157 | if (opt & LAST_OPT_H) { | ||
158 | /* Print header line */ | ||
159 | if (opt & LAST_OPT_W) { | ||
160 | printf(HEADER_FORMAT, HEADER_LINE_WIDE); | ||
161 | } else { | ||
162 | printf(HEADER_FORMAT, HEADER_LINE); | ||
163 | } | ||
164 | } | ||
165 | #endif | ||
166 | |||
167 | file = xopen(filename, O_RDONLY); | ||
168 | if (full_read(file, &ut, sizeof(ut)) != sizeof(ut)) { | ||
169 | struct stat st; | ||
170 | fstat(file, &st); | ||
171 | start_time = st.st_ctime; | ||
172 | goto exit; | ||
173 | } | ||
174 | start_time = ut.ut_time; | ||
175 | |||
176 | time(&down_time); | ||
177 | going_down = 0; | ||
178 | boot_down = NORMAL; /* 0 */ | ||
179 | zlist = NULL; | ||
180 | boot_time = 0; | ||
181 | pos = 0; | ||
182 | for (;;) { | ||
183 | pos -= (off_t)sizeof(ut); | ||
184 | /* Bug? What if file changes size? | ||
185 | * What if size is not a multiple of sizeof(ut)? */ | ||
186 | if (lseek(file, pos, SEEK_END) < 0) { | ||
187 | /* Beyond the beginning of the file boundary => | ||
188 | * the whole file has been read. */ | ||
189 | break; | ||
190 | } | ||
191 | if (full_read(file, &ut, sizeof(ut)) != sizeof(ut)) | ||
192 | break; | ||
193 | |||
194 | switch (get_ut_type(&ut)) { | ||
195 | case SHUTDOWN_TIME: | ||
196 | down_time = ut.ut_time; | ||
197 | boot_down = DOWN; | ||
198 | going_down = 1; | ||
199 | break; | ||
200 | case RUN_LVL: | ||
201 | if (is_runlevel_shutdown(&ut)) { | ||
202 | down_time = ut.ut_time; | ||
203 | going_down = 1; | ||
204 | boot_down = DOWN; | ||
205 | } | ||
206 | break; | ||
207 | case BOOT_TIME: | ||
208 | strcpy(ut.ut_line, "system boot"); | ||
209 | show_entry(&ut, REBOOT, down_time); | ||
210 | boot_down = CRASH; | ||
211 | going_down = 1; | ||
212 | break; | ||
213 | case DEAD_PROCESS: | ||
214 | if (!ut.ut_line[0]) { | ||
215 | break; | ||
216 | } | ||
217 | /* add_entry */ | ||
218 | llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); | ||
219 | break; | ||
220 | case USER_PROCESS: { | ||
221 | int show; | ||
222 | |||
223 | if (!ut.ut_line[0]) { | ||
224 | break; | ||
225 | } | ||
226 | /* find_entry */ | ||
227 | show = 1; | ||
228 | { | ||
229 | llist_t *el, *next; | ||
230 | for (el = zlist; el; el = next) { | ||
231 | struct utmp *up = (struct utmp *)el->data; | ||
232 | next = el->link; | ||
233 | if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) { | ||
234 | if (show) { | ||
235 | show_entry(&ut, NORMAL, up->ut_time); | ||
236 | show = 0; | ||
237 | } | ||
238 | llist_unlink(&zlist, el); | ||
239 | free(el->data); | ||
240 | free(el); | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (show) { | ||
246 | int state = boot_down; | ||
247 | |||
248 | if (boot_time == 0) { | ||
249 | state = LOGGED; | ||
250 | /* Check if the process is alive */ | ||
251 | if ((ut.ut_pid > 0) | ||
252 | && (kill(ut.ut_pid, 0) != 0) | ||
253 | && (errno == ESRCH)) { | ||
254 | state = GONE; | ||
255 | } | ||
256 | } | ||
257 | show_entry(&ut, state, boot_time); | ||
258 | } | ||
259 | /* add_entry */ | ||
260 | llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); | ||
261 | break; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | if (going_down) { | ||
266 | boot_time = ut.ut_time; | ||
267 | llist_free(zlist, free); | ||
268 | zlist = NULL; | ||
269 | going_down = 0; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
274 | llist_free(zlist, free); | ||
275 | } | ||
276 | |||
277 | exit: | ||
278 | printf("\nwtmp begins %s", ctime(&start_time)); | ||
279 | |||
280 | if (ENABLE_FEATURE_CLEAN_UP) | ||
281 | close(file); | ||
282 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
283 | } | ||