| /* last.c - Show listing of last logged in users. |
| * |
| * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com> |
| * Copyright 2013 Kyungwan Han <asura321@gmail.com> |
| * |
| * No Standard. |
| |
| USE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN)) |
| |
| config LAST |
| bool "last" |
| default n |
| help |
| usage: last [-W] [-f FILE] |
| |
| Show listing of last logged in users. |
| |
| -W Display the information without host-column truncation. |
| -f FILE Read from file FILE instead of /var/log/wtmp. |
| */ |
| |
| #define FOR_last |
| #include "toys.h" |
| #include <utmp.h> |
| |
| #ifndef SHUTDOWN_TIME |
| #define SHUTDOWN_TIME 254 |
| #endif |
| |
| GLOBALS( |
| char *file; |
| |
| struct arg_list *list; |
| ) |
| |
| static void free_list() |
| { |
| if (TT.list) { |
| llist_traverse(TT.list, llist_free_arg); |
| TT.list = NULL; |
| } |
| } |
| |
| static void llist_add_node(struct arg_list **old, void *data) |
| { |
| struct arg_list *new = xmalloc(sizeof(struct arg_list)); |
| |
| new->arg = (char*)data; |
| new->next = *old; |
| *old = new; |
| } |
| |
| // Find a node and dlink it from the list. |
| static struct arg_list *find_and_dlink(struct arg_list **list, char *devname) |
| { |
| struct arg_list *l = *list; |
| |
| while (*list) { |
| struct utmp *ut = (struct utmp *)l->arg; |
| |
| if (!strncmp(ut->ut_line, devname, UT_LINESIZE)) { |
| *list = (*list)->next; |
| return l; |
| } |
| list = &(*list)->next; |
| l = *list; |
| } |
| return NULL; |
| } |
| |
| // Compute login, logout and duration of login. |
| static void seize_duration(time_t tm0, time_t tm1) |
| { |
| unsigned days, hours, mins; |
| double diff = difftime(tm1, tm0); |
| |
| diff = (diff > 0) ? (tm1 - tm0) : 0; |
| toybuf[0] = toybuf[18] = toybuf[28] = '\0'; |
| strncpy(toybuf, ctime(&tm0), 16); // Login Time. |
| snprintf(toybuf+18, 8, "- %s", ctime(&tm1) + 11); // Logout Time. |
| days = (mins = diff/60)/(24*60); |
| hours = (mins = (mins%(24*60)))/60; |
| mins = mins%60; |
| sprintf(toybuf+28, "(%u+%02u:%02u)", days, hours, mins); // Duration. |
| } |
| |
| void last_main(void) |
| { |
| struct utmp ut; |
| time_t tm[3] = {0,}; //array for time avlues, previous, current |
| char *file = "/var/log/wtmp"; |
| int fd, pwidth, curlog_type = EMPTY; |
| off_t loc; |
| |
| if (toys.optflags & FLAG_f) file = TT.file; |
| |
| pwidth = (toys.optflags & FLAG_W) ? 46 : 16; |
| *tm = time(tm+1); |
| fd = xopenro(file); |
| loc = xlseek(fd, 0, SEEK_END); |
| |
| // Loop through file structures in reverse order. |
| for (;;) { |
| loc -= sizeof(ut); |
| if(loc < 0) break; |
| xlseek(fd, loc, SEEK_SET); |
| |
| // Read next structure, determine type |
| xreadall(fd, &ut, sizeof(ut)); |
| *tm = ut.ut_tv.tv_sec; |
| if (*ut.ut_line == '~') { |
| if (!strcmp(ut.ut_user, "runlevel")) ut.ut_type = RUN_LVL; |
| else if (!strcmp(ut.ut_user, "reboot")) ut.ut_type = BOOT_TIME; |
| else if (!strcmp(ut.ut_user, "shutdown")) ut.ut_type = SHUTDOWN_TIME; |
| } else if (!*ut.ut_user) ut.ut_type = DEAD_PROCESS; |
| else if (*ut.ut_user && *ut.ut_line && ut.ut_type != DEAD_PROCESS |
| && strcmp(ut.ut_user, "LOGIN")) ut.ut_type = USER_PROCESS; |
| /* The pair of terminal names '|' / '}' logs the |
| * old/new system time when date changes it. |
| */ |
| if (!strcmp(ut.ut_user, "date")) { |
| if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; |
| if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; |
| } |
| |
| if ((ut.ut_type == SHUTDOWN_TIME) || ((ut.ut_type == RUN_LVL) && |
| (((ut.ut_pid & 255) == '0') || ((ut.ut_pid & 255) == '6')))) |
| { |
| tm[1] = tm[2] = (time_t)ut.ut_tv.tv_sec; |
| free_list(); |
| curlog_type = RUN_LVL; |
| } else if (ut.ut_type == BOOT_TIME) { |
| seize_duration(tm[0], tm[1]); |
| strcpy(ut.ut_line, "system boot"); |
| free_list(); |
| printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, |
| ut.ut_line, pwidth, pwidth, ut.ut_host, |
| toybuf, toybuf+18, toybuf+28); |
| curlog_type = BOOT_TIME; |
| tm[2] = (time_t)ut.ut_tv.tv_sec; |
| } else if (ut.ut_type == USER_PROCESS && *ut.ut_line) { |
| struct arg_list *l = find_and_dlink(&TT.list, ut.ut_line); |
| |
| if (l) { |
| struct utmp *u = (struct utmp *)l->arg; |
| seize_duration(tm[0], u->ut_tv.tv_sec); |
| printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, |
| ut.ut_line, pwidth, pwidth, ut.ut_host, |
| toybuf, toybuf+18, toybuf+28); |
| free(l->arg); |
| free(l); |
| } else { |
| int type = !tm[2] ? EMPTY : curlog_type; |
| if (!tm[2]) { //check process's current status (alive or dead). |
| if ((ut.ut_pid > 0) && (kill(ut.ut_pid, 0)!=0) && (errno == ESRCH)) |
| type = INIT_PROCESS; |
| } |
| seize_duration(tm[0], tm[2]); |
| switch (type) { |
| case EMPTY: |
| strcpy(toybuf+18, " still"); |
| strcpy(toybuf+28, "logged in"); |
| break; |
| case RUN_LVL: |
| strcpy(toybuf+18, "- down "); |
| break; |
| case BOOT_TIME: |
| strcpy(toybuf+18, "- crash"); |
| break; |
| case INIT_PROCESS: |
| strcpy(toybuf+18, " gone"); |
| strcpy(toybuf+28, "- no logout"); |
| break; |
| default: |
| break; |
| } |
| printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, |
| ut.ut_line, pwidth, pwidth, ut.ut_host, |
| toybuf, toybuf+18, toybuf+28); |
| } |
| llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); |
| } else if (ut.ut_type == DEAD_PROCESS && *ut.ut_line) |
| llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); |
| |
| loc -= sizeof(ut); |
| if(loc < 0) break; |
| xlseek(fd, loc, SEEK_SET); |
| } |
| |
| if (CFG_TOYBOX_FREE) { |
| xclose(fd); |
| free_list(); |
| } |
| |
| xprintf("\n%s begins %-24.24s\n", basename(file), ctime(tm)); |
| } |