aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-05-22 02:07:58 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-05-22 02:07:58 +0000
commit869d3d318ff3c17d74499f1216abfe3afa40590c (patch)
treef76f5abd31348c95c73d9455fef5f3fb38291686
parent7bd8d8fd264a72f9c228da3ca238eae1b2ad8ce1 (diff)
downloadbusybox-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
-rw-r--r--include/usage.h10
-rw-r--r--miscutils/Config.in8
-rw-r--r--miscutils/last.c8
-rw-r--r--miscutils/last_fancy.c283
4 files changed, 307 insertions, 2 deletions
diff --git a/include/usage.h b/include/usage.h
index cffe78220..1f4213a2e 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3504,9 +3504,15 @@
3504 "lash is deprecated, please use hush" 3504 "lash is deprecated, please use hush"
3505 3505
3506#define last_trivial_usage \ 3506#define last_trivial_usage \
3507 "" 3507 ""USE_FEATURE_LAST_FANCY("[-HW] [-f file]")
3508#define last_full_usage "\n\n" \ 3508#define last_full_usage "\n\n" \
3509 "Show listing of the last users that logged into the system" 3509 "Show listing of the last users that logged into the system" \
3510 USE_FEATURE_LAST_FANCY( "\n" \
3511 "\nOptions:" \
3512 "\n -H Show header line" \
3513 "\n -W Display with no host column truncation" \
3514 "\n -f file Read from file instead of /var/log/wtmp" \
3515 )
3510 3516
3511#define sha1sum_trivial_usage \ 3517#define sha1sum_trivial_usage \
3512 "[OPTION] [FILEs...]" \ 3518 "[OPTION] [FILEs...]" \
diff --git a/miscutils/Config.in b/miscutils/Config.in
index c4eb10838..427906ff8 100644
--- a/miscutils/Config.in
+++ b/miscutils/Config.in
@@ -229,6 +229,14 @@ config LAST
229 help 229 help
230 'last' displays a list of the last users that logged into the system. 230 'last' displays a list of the last users that logged into the system.
231 231
232config FEATURE_LAST_FANCY
233 bool "Fancy output"
234 default n
235 depends on LAST
236 help
237 'last' displays detailed information about the last users that
238 logged into the system (mimics sysvinit last). +900 bytes.
239
232config LESS 240config LESS
233 bool "less" 241 bool "less"
234 default n 242 default n
diff --git a/miscutils/last.c b/miscutils/last.c
index 749862c35..ef41444c5 100644
--- a/miscutils/last.c
+++ b/miscutils/last.c
@@ -26,6 +26,12 @@
26#error struct utmp member char[] size(s) have changed! 26#error struct utmp member char[] size(s) have changed!
27#endif 27#endif
28 28
29#if ENABLE_FEATURE_LAST_FANCY
30
31#include "last_fancy.c"
32
33#else
34
29int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 35int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
30int last_main(int argc, char **argv ATTRIBUTE_UNUSED) 36int last_main(int argc, char **argv ATTRIBUTE_UNUSED)
31{ 37{
@@ -92,3 +98,5 @@ int last_main(int argc, char **argv ATTRIBUTE_UNUSED)
92 98
93 fflush_stdout_and_exit(EXIT_SUCCESS); 99 fflush_stdout_and_exit(EXIT_SUCCESS);
94} 100}
101
102#endif
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
16enum {
17 NORMAL,
18 LOGGED,
19 DOWN,
20 REBOOT,
21 CRASH,
22 GONE
23};
24
25enum {
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
33static 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
93static 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
131static 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
140int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
141int 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}