last.c 5.5 KB

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