Home | History | Annotate | Download | only in pending
      1 /* last.c - Show listing of last logged in users.
      2  *
      3  * Copyright 2013 Ranjan Kumar <ranjankumar.bth (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * No Standard.
      7 
      8 USE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN))
      9 
     10 config LAST
     11   bool "last"
     12   default n
     13   help
     14     usage: last [-W] [-f FILE]
     15 
     16     Show listing of last logged in users.
     17 
     18     -W      Display the information without host-column truncation
     19     -f FILE Read from file FILE instead of /var/log/wtmp
     20 */
     21 
     22 #define FOR_last
     23 #include "toys.h"
     24 #include <utmp.h>
     25 
     26 #ifndef SHUTDOWN_TIME
     27 #define SHUTDOWN_TIME 254
     28 #endif
     29 
     30 GLOBALS(
     31   char *file;
     32 
     33   struct arg_list *list;
     34 )
     35 
     36 static void free_list()
     37 {
     38   if (TT.list) {
     39     llist_traverse(TT.list, llist_free_arg);
     40     TT.list = NULL;
     41   }
     42 }
     43 
     44 static void llist_add_node(struct arg_list **old, void *data)
     45 {
     46   struct arg_list *new = xmalloc(sizeof(struct arg_list));
     47 
     48   new->arg = (char*)data;
     49   new->next = *old;
     50   *old = new;
     51 }
     52 
     53 // Find a node and dlink it from the list.
     54 static struct arg_list *find_and_dlink(struct arg_list **list, char *devname)
     55 {
     56   struct arg_list *l = *list;
     57 
     58   while (*list) {
     59     struct utmp *ut = (struct utmp *)l->arg;
     60 
     61     if (!strncmp(ut->ut_line, devname, UT_LINESIZE)) {
     62       *list = (*list)->next;
     63       return l;
     64     }
     65     list = &(*list)->next;
     66     l = *list;
     67   }
     68   return NULL;
     69 }
     70 
     71 // Compute login, logout and duration of login.
     72 static void seize_duration(time_t tm0, time_t tm1)
     73 {
     74   unsigned days, hours, mins;
     75   double diff = difftime(tm1, tm0);
     76 
     77   diff = (diff > 0) ? (tm1 - tm0) : 0;
     78   toybuf[0] = toybuf[18] = toybuf[28] = '\0';
     79   strncpy(toybuf, ctime(&tm0), 16); // Login Time.
     80   snprintf(toybuf+18, 8, "- %s", ctime(&tm1) + 11); // Logout Time.
     81   days = (mins = diff/60)/(24*60);
     82   hours = (mins = (mins%(24*60)))/60;
     83   mins = mins%60;
     84   sprintf(toybuf+28, "(%u+%02u:%02u)", days, hours, mins); // Duration.
     85 }
     86 
     87 void last_main(void)
     88 {
     89   struct utmp ut;
     90   time_t tm[3] = {0,}; //array for time avlues, previous, current
     91   char *file = "/var/log/wtmp";
     92   int fd, pwidth, curlog_type = EMPTY;
     93   off_t loc;
     94 
     95   if (toys.optflags & FLAG_f) file = TT.file;
     96 
     97   pwidth = (toys.optflags & FLAG_W) ? 46 : 16;
     98   *tm = time(tm+1);
     99   fd = xopenro(file);
    100   loc = xlseek(fd, 0, SEEK_END);
    101 
    102   // Loop through file structures in reverse order.
    103   for (;;) {
    104     loc -= sizeof(ut);
    105     if(loc < 0) break;
    106     xlseek(fd, loc, SEEK_SET);
    107 
    108     // Read next structure, determine type
    109     xreadall(fd, &ut, sizeof(ut));
    110     *tm = ut.ut_tv.tv_sec;
    111     if (*ut.ut_line == '~') {
    112       if (!strcmp(ut.ut_user, "runlevel")) ut.ut_type = RUN_LVL;
    113       else if (!strcmp(ut.ut_user, "reboot")) ut.ut_type = BOOT_TIME;
    114       else if (!strcmp(ut.ut_user, "shutdown")) ut.ut_type = SHUTDOWN_TIME;
    115     } else if (!*ut.ut_user) ut.ut_type = DEAD_PROCESS;
    116     else if (*ut.ut_user && *ut.ut_line && ut.ut_type != DEAD_PROCESS
    117         && strcmp(ut.ut_user, "LOGIN")) ut.ut_type = USER_PROCESS;
    118     /* The pair of terminal names '|' / '}' logs the
    119      * old/new system time when date changes it.
    120      */
    121     if (!strcmp(ut.ut_user, "date")) {
    122       if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
    123       if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
    124     }
    125 
    126     if ((ut.ut_type == SHUTDOWN_TIME) || ((ut.ut_type == RUN_LVL) &&
    127         (((ut.ut_pid & 255) == '0') || ((ut.ut_pid & 255) == '6'))))
    128     {
    129       tm[1] = tm[2] = (time_t)ut.ut_tv.tv_sec;
    130       free_list();
    131       curlog_type = RUN_LVL;
    132     } else if (ut.ut_type == BOOT_TIME) {
    133       seize_duration(tm[0], tm[1]);
    134       strcpy(ut.ut_line, "system boot");
    135       free_list();
    136       printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
    137           ut.ut_line, pwidth, pwidth, ut.ut_host,
    138           toybuf, toybuf+18, toybuf+28);
    139       curlog_type = BOOT_TIME;
    140       tm[2] = (time_t)ut.ut_tv.tv_sec;
    141     } else if (ut.ut_type == USER_PROCESS && *ut.ut_line) {
    142       struct arg_list *l = find_and_dlink(&TT.list, ut.ut_line);
    143 
    144       if (l) {
    145         struct utmp *u = (struct utmp *)l->arg;
    146         seize_duration(tm[0], u->ut_tv.tv_sec);
    147         printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
    148             ut.ut_line, pwidth, pwidth, ut.ut_host,
    149             toybuf, toybuf+18, toybuf+28);
    150         free(l->arg);
    151         free(l);
    152       } else {
    153         int type = !tm[2] ? EMPTY : curlog_type;
    154         if (!tm[2]) { //check process's current status (alive or dead).
    155           if ((ut.ut_pid > 0) && (kill(ut.ut_pid, 0)!=0) && (errno == ESRCH))
    156             type = INIT_PROCESS;
    157         }
    158         seize_duration(tm[0], tm[2]);
    159         switch (type) {
    160           case EMPTY:
    161             strcpy(toybuf+18, "  still");
    162             strcpy(toybuf+28, "logged in");
    163             break;
    164           case RUN_LVL:
    165             strcpy(toybuf+18, "- down ");
    166             break;
    167           case BOOT_TIME:
    168             strcpy(toybuf+18, "- crash");
    169             break;
    170           case INIT_PROCESS:
    171             strcpy(toybuf+18, "   gone");
    172             strcpy(toybuf+28, "- no logout");
    173             break;
    174           default:
    175             break;
    176         }
    177         printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
    178             ut.ut_line, pwidth, pwidth, ut.ut_host,
    179             toybuf, toybuf+18, toybuf+28);
    180       }
    181       llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
    182     } else if (ut.ut_type == DEAD_PROCESS && *ut.ut_line)
    183       llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
    184 
    185     loc -= sizeof(ut);
    186     if(loc < 0) break;
    187     xlseek(fd, loc, SEEK_SET);
    188   }
    189 
    190   if (CFG_TOYBOX_FREE) {
    191     xclose(fd);
    192     free_list();
    193   }
    194 
    195   xprintf("\n%s begins %-24.24s\n", basename(file), ctime(tm));
    196 }
    197