toybox/toys/pending/last.c
<<
>>
Prefs
   1/* last.c - Show listing of last logged in users.
   2 *
   3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5 *
   6 * No Standard.
   7
   8USE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN))
   9
  10config 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
  30GLOBALS(
  31  char *file;
  32
  33  struct arg_list *list;
  34)
  35
  36static void free_list()
  37{
  38  if (TT.list) {
  39    llist_traverse(TT.list, llist_free_arg);
  40    TT.list = NULL;
  41  }
  42}
  43
  44static 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.
  54static 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.
  72static 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
  87void 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