last_fancy.c 6.2 KB

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