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

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 7 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.