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 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 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. 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. 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 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 = xopenro(file); 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