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