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