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