last_fancy.c 6.3 KB

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