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 | |
| 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')
| -rw-r--r-- | miscutils/Config.in | 8 | ||||
| -rw-r--r-- | miscutils/last.c | 8 | ||||
| -rw-r--r-- | miscutils/last_fancy.c | 283 |
3 files changed, 299 insertions, 0 deletions
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 | ||
| 232 | config 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 | |||
| 232 | config LESS | 240 | config 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 | |||
| 29 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 35 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 30 | int last_main(int argc, char **argv ATTRIBUTE_UNUSED) | 36 | int 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 | |||
| 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 | } | ||
