10f66f451Sopenharmony_ci/* last.c - Show listing of last logged in users.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com>
50f66f451Sopenharmony_ci *
60f66f451Sopenharmony_ci * No Standard.
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ciUSE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN))
90f66f451Sopenharmony_ci
100f66f451Sopenharmony_ciconfig LAST
110f66f451Sopenharmony_ci  bool "last"
120f66f451Sopenharmony_ci  default n
130f66f451Sopenharmony_ci  help
140f66f451Sopenharmony_ci    usage: last [-W] [-f FILE]
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ci    Show listing of last logged in users.
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci    -W      Display the information without host-column truncation
190f66f451Sopenharmony_ci    -f FILE Read from file FILE instead of /var/log/wtmp
200f66f451Sopenharmony_ci*/
210f66f451Sopenharmony_ci
220f66f451Sopenharmony_ci#define FOR_last
230f66f451Sopenharmony_ci#include "toys.h"
240f66f451Sopenharmony_ci#include <utmp.h>
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci#ifndef SHUTDOWN_TIME
270f66f451Sopenharmony_ci#define SHUTDOWN_TIME 254
280f66f451Sopenharmony_ci#endif
290f66f451Sopenharmony_ci
300f66f451Sopenharmony_ciGLOBALS(
310f66f451Sopenharmony_ci  char *file;
320f66f451Sopenharmony_ci
330f66f451Sopenharmony_ci  struct arg_list *list;
340f66f451Sopenharmony_ci)
350f66f451Sopenharmony_ci
360f66f451Sopenharmony_cistatic void free_list()
370f66f451Sopenharmony_ci{
380f66f451Sopenharmony_ci  if (TT.list) {
390f66f451Sopenharmony_ci    llist_traverse(TT.list, llist_free_arg);
400f66f451Sopenharmony_ci    TT.list = NULL;
410f66f451Sopenharmony_ci  }
420f66f451Sopenharmony_ci}
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_cistatic void llist_add_node(struct arg_list **old, void *data)
450f66f451Sopenharmony_ci{
460f66f451Sopenharmony_ci  struct arg_list *new = xmalloc(sizeof(struct arg_list));
470f66f451Sopenharmony_ci
480f66f451Sopenharmony_ci  new->arg = (char*)data;
490f66f451Sopenharmony_ci  new->next = *old;
500f66f451Sopenharmony_ci  *old = new;
510f66f451Sopenharmony_ci}
520f66f451Sopenharmony_ci
530f66f451Sopenharmony_ci// Find a node and dlink it from the list.
540f66f451Sopenharmony_cistatic struct arg_list *find_and_dlink(struct arg_list **list, char *devname)
550f66f451Sopenharmony_ci{
560f66f451Sopenharmony_ci  struct arg_list *l = *list;
570f66f451Sopenharmony_ci
580f66f451Sopenharmony_ci  while (*list) {
590f66f451Sopenharmony_ci    struct utmp *ut = (struct utmp *)l->arg;
600f66f451Sopenharmony_ci
610f66f451Sopenharmony_ci    if (!strncmp(ut->ut_line, devname, UT_LINESIZE)) {
620f66f451Sopenharmony_ci      *list = (*list)->next;
630f66f451Sopenharmony_ci      return l;
640f66f451Sopenharmony_ci    }
650f66f451Sopenharmony_ci    list = &(*list)->next;
660f66f451Sopenharmony_ci    l = *list;
670f66f451Sopenharmony_ci  }
680f66f451Sopenharmony_ci  return NULL;
690f66f451Sopenharmony_ci}
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_ci// Compute login, logout and duration of login.
720f66f451Sopenharmony_cistatic void seize_duration(time_t tm0, time_t tm1)
730f66f451Sopenharmony_ci{
740f66f451Sopenharmony_ci  unsigned days, hours, mins;
750f66f451Sopenharmony_ci  double diff = difftime(tm1, tm0);
760f66f451Sopenharmony_ci
770f66f451Sopenharmony_ci  diff = (diff > 0) ? (tm1 - tm0) : 0;
780f66f451Sopenharmony_ci  toybuf[0] = toybuf[18] = toybuf[28] = '\0';
790f66f451Sopenharmony_ci  strncpy(toybuf, ctime(&tm0), 16); // Login Time.
800f66f451Sopenharmony_ci  snprintf(toybuf+18, 8, "- %s", ctime(&tm1) + 11); // Logout Time.
810f66f451Sopenharmony_ci  days = (mins = diff/60)/(24*60);
820f66f451Sopenharmony_ci  hours = (mins = (mins%(24*60)))/60;
830f66f451Sopenharmony_ci  mins = mins%60;
840f66f451Sopenharmony_ci  sprintf(toybuf+28, "(%u+%02u:%02u)", days, hours, mins); // Duration.
850f66f451Sopenharmony_ci}
860f66f451Sopenharmony_ci
870f66f451Sopenharmony_civoid last_main(void)
880f66f451Sopenharmony_ci{
890f66f451Sopenharmony_ci  struct utmp ut;
900f66f451Sopenharmony_ci  time_t tm[3] = {0,}; //array for time avlues, previous, current
910f66f451Sopenharmony_ci  char *file = "/var/log/wtmp";
920f66f451Sopenharmony_ci  int fd, pwidth, curlog_type = EMPTY;
930f66f451Sopenharmony_ci  off_t loc;
940f66f451Sopenharmony_ci
950f66f451Sopenharmony_ci  if (toys.optflags & FLAG_f) file = TT.file;
960f66f451Sopenharmony_ci
970f66f451Sopenharmony_ci  pwidth = (toys.optflags & FLAG_W) ? 46 : 16;
980f66f451Sopenharmony_ci  *tm = time(tm+1);
990f66f451Sopenharmony_ci  fd = xopenro(file);
1000f66f451Sopenharmony_ci  loc = xlseek(fd, 0, SEEK_END);
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci  // Loop through file structures in reverse order.
1030f66f451Sopenharmony_ci  for (;;) {
1040f66f451Sopenharmony_ci    loc -= sizeof(ut);
1050f66f451Sopenharmony_ci    if(loc < 0) break;
1060f66f451Sopenharmony_ci    xlseek(fd, loc, SEEK_SET);
1070f66f451Sopenharmony_ci
1080f66f451Sopenharmony_ci    // Read next structure, determine type
1090f66f451Sopenharmony_ci    xreadall(fd, &ut, sizeof(ut));
1100f66f451Sopenharmony_ci    *tm = ut.ut_tv.tv_sec;
1110f66f451Sopenharmony_ci    if (*ut.ut_line == '~') {
1120f66f451Sopenharmony_ci      if (!strcmp(ut.ut_user, "runlevel")) ut.ut_type = RUN_LVL;
1130f66f451Sopenharmony_ci      else if (!strcmp(ut.ut_user, "reboot")) ut.ut_type = BOOT_TIME;
1140f66f451Sopenharmony_ci      else if (!strcmp(ut.ut_user, "shutdown")) ut.ut_type = SHUTDOWN_TIME;
1150f66f451Sopenharmony_ci    } else if (!*ut.ut_user) ut.ut_type = DEAD_PROCESS;
1160f66f451Sopenharmony_ci    else if (*ut.ut_user && *ut.ut_line && ut.ut_type != DEAD_PROCESS
1170f66f451Sopenharmony_ci        && strcmp(ut.ut_user, "LOGIN")) ut.ut_type = USER_PROCESS;
1180f66f451Sopenharmony_ci    /* The pair of terminal names '|' / '}' logs the
1190f66f451Sopenharmony_ci     * old/new system time when date changes it.
1200f66f451Sopenharmony_ci     */
1210f66f451Sopenharmony_ci    if (!strcmp(ut.ut_user, "date")) {
1220f66f451Sopenharmony_ci      if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
1230f66f451Sopenharmony_ci      if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
1240f66f451Sopenharmony_ci    }
1250f66f451Sopenharmony_ci
1260f66f451Sopenharmony_ci    if ((ut.ut_type == SHUTDOWN_TIME) || ((ut.ut_type == RUN_LVL) &&
1270f66f451Sopenharmony_ci        (((ut.ut_pid & 255) == '0') || ((ut.ut_pid & 255) == '6'))))
1280f66f451Sopenharmony_ci    {
1290f66f451Sopenharmony_ci      tm[1] = tm[2] = (time_t)ut.ut_tv.tv_sec;
1300f66f451Sopenharmony_ci      free_list();
1310f66f451Sopenharmony_ci      curlog_type = RUN_LVL;
1320f66f451Sopenharmony_ci    } else if (ut.ut_type == BOOT_TIME) {
1330f66f451Sopenharmony_ci      seize_duration(tm[0], tm[1]);
1340f66f451Sopenharmony_ci      strcpy(ut.ut_line, "system boot");
1350f66f451Sopenharmony_ci      free_list();
1360f66f451Sopenharmony_ci      printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
1370f66f451Sopenharmony_ci          ut.ut_line, pwidth, pwidth, ut.ut_host,
1380f66f451Sopenharmony_ci          toybuf, toybuf+18, toybuf+28);
1390f66f451Sopenharmony_ci      curlog_type = BOOT_TIME;
1400f66f451Sopenharmony_ci      tm[2] = (time_t)ut.ut_tv.tv_sec;
1410f66f451Sopenharmony_ci    } else if (ut.ut_type == USER_PROCESS && *ut.ut_line) {
1420f66f451Sopenharmony_ci      struct arg_list *l = find_and_dlink(&TT.list, ut.ut_line);
1430f66f451Sopenharmony_ci
1440f66f451Sopenharmony_ci      if (l) {
1450f66f451Sopenharmony_ci        struct utmp *u = (struct utmp *)l->arg;
1460f66f451Sopenharmony_ci        seize_duration(tm[0], u->ut_tv.tv_sec);
1470f66f451Sopenharmony_ci        printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
1480f66f451Sopenharmony_ci            ut.ut_line, pwidth, pwidth, ut.ut_host,
1490f66f451Sopenharmony_ci            toybuf, toybuf+18, toybuf+28);
1500f66f451Sopenharmony_ci        free(l->arg);
1510f66f451Sopenharmony_ci        free(l);
1520f66f451Sopenharmony_ci      } else {
1530f66f451Sopenharmony_ci        int type = !tm[2] ? EMPTY : curlog_type;
1540f66f451Sopenharmony_ci        if (!tm[2]) { //check process's current status (alive or dead).
1550f66f451Sopenharmony_ci          if ((ut.ut_pid > 0) && (kill(ut.ut_pid, 0)!=0) && (errno == ESRCH))
1560f66f451Sopenharmony_ci            type = INIT_PROCESS;
1570f66f451Sopenharmony_ci        }
1580f66f451Sopenharmony_ci        seize_duration(tm[0], tm[2]);
1590f66f451Sopenharmony_ci        switch (type) {
1600f66f451Sopenharmony_ci          case EMPTY:
1610f66f451Sopenharmony_ci            strcpy(toybuf+18, "  still");
1620f66f451Sopenharmony_ci            strcpy(toybuf+28, "logged in");
1630f66f451Sopenharmony_ci            break;
1640f66f451Sopenharmony_ci          case RUN_LVL:
1650f66f451Sopenharmony_ci            strcpy(toybuf+18, "- down ");
1660f66f451Sopenharmony_ci            break;
1670f66f451Sopenharmony_ci          case BOOT_TIME:
1680f66f451Sopenharmony_ci            strcpy(toybuf+18, "- crash");
1690f66f451Sopenharmony_ci            break;
1700f66f451Sopenharmony_ci          case INIT_PROCESS:
1710f66f451Sopenharmony_ci            strcpy(toybuf+18, "   gone");
1720f66f451Sopenharmony_ci            strcpy(toybuf+28, "- no logout");
1730f66f451Sopenharmony_ci            break;
1740f66f451Sopenharmony_ci          default:
1750f66f451Sopenharmony_ci            break;
1760f66f451Sopenharmony_ci        }
1770f66f451Sopenharmony_ci        printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user,
1780f66f451Sopenharmony_ci            ut.ut_line, pwidth, pwidth, ut.ut_host,
1790f66f451Sopenharmony_ci            toybuf, toybuf+18, toybuf+28);
1800f66f451Sopenharmony_ci      }
1810f66f451Sopenharmony_ci      llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
1820f66f451Sopenharmony_ci    } else if (ut.ut_type == DEAD_PROCESS && *ut.ut_line)
1830f66f451Sopenharmony_ci      llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
1840f66f451Sopenharmony_ci
1850f66f451Sopenharmony_ci    loc -= sizeof(ut);
1860f66f451Sopenharmony_ci    if(loc < 0) break;
1870f66f451Sopenharmony_ci    xlseek(fd, loc, SEEK_SET);
1880f66f451Sopenharmony_ci  }
1890f66f451Sopenharmony_ci
1900f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) {
1910f66f451Sopenharmony_ci    xclose(fd);
1920f66f451Sopenharmony_ci    free_list();
1930f66f451Sopenharmony_ci  }
1940f66f451Sopenharmony_ci
1950f66f451Sopenharmony_ci  xprintf("\n%s begins %-24.24s\n", basename(file), ctime(tm));
1960f66f451Sopenharmony_ci}
197