10f66f451Sopenharmony_ci/* last.c - Show listing of last logged in users. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com> 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * No Standard. 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ciUSE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN)) 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciconfig LAST 110f66f451Sopenharmony_ci bool "last" 120f66f451Sopenharmony_ci default n 130f66f451Sopenharmony_ci help 140f66f451Sopenharmony_ci usage: last [-W] [-f FILE] 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ci Show listing of last logged in users. 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci -W Display the information without host-column truncation 190f66f451Sopenharmony_ci -f FILE Read from file FILE instead of /var/log/wtmp 200f66f451Sopenharmony_ci*/ 210f66f451Sopenharmony_ci 220f66f451Sopenharmony_ci#define FOR_last 230f66f451Sopenharmony_ci#include "toys.h" 240f66f451Sopenharmony_ci#include <utmp.h> 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ci#ifndef SHUTDOWN_TIME 270f66f451Sopenharmony_ci#define SHUTDOWN_TIME 254 280f66f451Sopenharmony_ci#endif 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ciGLOBALS( 310f66f451Sopenharmony_ci char *file; 320f66f451Sopenharmony_ci 330f66f451Sopenharmony_ci struct arg_list *list; 340f66f451Sopenharmony_ci) 350f66f451Sopenharmony_ci 360f66f451Sopenharmony_cistatic void free_list() 370f66f451Sopenharmony_ci{ 380f66f451Sopenharmony_ci if (TT.list) { 390f66f451Sopenharmony_ci llist_traverse(TT.list, llist_free_arg); 400f66f451Sopenharmony_ci TT.list = NULL; 410f66f451Sopenharmony_ci } 420f66f451Sopenharmony_ci} 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_cistatic void llist_add_node(struct arg_list **old, void *data) 450f66f451Sopenharmony_ci{ 460f66f451Sopenharmony_ci struct arg_list *new = xmalloc(sizeof(struct arg_list)); 470f66f451Sopenharmony_ci 480f66f451Sopenharmony_ci new->arg = (char*)data; 490f66f451Sopenharmony_ci new->next = *old; 500f66f451Sopenharmony_ci *old = new; 510f66f451Sopenharmony_ci} 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci// Find a node and dlink it from the list. 540f66f451Sopenharmony_cistatic struct arg_list *find_and_dlink(struct arg_list **list, char *devname) 550f66f451Sopenharmony_ci{ 560f66f451Sopenharmony_ci struct arg_list *l = *list; 570f66f451Sopenharmony_ci 580f66f451Sopenharmony_ci while (*list) { 590f66f451Sopenharmony_ci struct utmp *ut = (struct utmp *)l->arg; 600f66f451Sopenharmony_ci 610f66f451Sopenharmony_ci if (!strncmp(ut->ut_line, devname, UT_LINESIZE)) { 620f66f451Sopenharmony_ci *list = (*list)->next; 630f66f451Sopenharmony_ci return l; 640f66f451Sopenharmony_ci } 650f66f451Sopenharmony_ci list = &(*list)->next; 660f66f451Sopenharmony_ci l = *list; 670f66f451Sopenharmony_ci } 680f66f451Sopenharmony_ci return NULL; 690f66f451Sopenharmony_ci} 700f66f451Sopenharmony_ci 710f66f451Sopenharmony_ci// Compute login, logout and duration of login. 720f66f451Sopenharmony_cistatic void seize_duration(time_t tm0, time_t tm1) 730f66f451Sopenharmony_ci{ 740f66f451Sopenharmony_ci unsigned days, hours, mins; 750f66f451Sopenharmony_ci double diff = difftime(tm1, tm0); 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ci diff = (diff > 0) ? (tm1 - tm0) : 0; 780f66f451Sopenharmony_ci toybuf[0] = toybuf[18] = toybuf[28] = '\0'; 790f66f451Sopenharmony_ci strncpy(toybuf, ctime(&tm0), 16); // Login Time. 800f66f451Sopenharmony_ci snprintf(toybuf+18, 8, "- %s", ctime(&tm1) + 11); // Logout Time. 810f66f451Sopenharmony_ci days = (mins = diff/60)/(24*60); 820f66f451Sopenharmony_ci hours = (mins = (mins%(24*60)))/60; 830f66f451Sopenharmony_ci mins = mins%60; 840f66f451Sopenharmony_ci sprintf(toybuf+28, "(%u+%02u:%02u)", days, hours, mins); // Duration. 850f66f451Sopenharmony_ci} 860f66f451Sopenharmony_ci 870f66f451Sopenharmony_civoid last_main(void) 880f66f451Sopenharmony_ci{ 890f66f451Sopenharmony_ci struct utmp ut; 900f66f451Sopenharmony_ci time_t tm[3] = {0,}; //array for time avlues, previous, current 910f66f451Sopenharmony_ci char *file = "/var/log/wtmp"; 920f66f451Sopenharmony_ci int fd, pwidth, curlog_type = EMPTY; 930f66f451Sopenharmony_ci off_t loc; 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci if (toys.optflags & FLAG_f) file = TT.file; 960f66f451Sopenharmony_ci 970f66f451Sopenharmony_ci pwidth = (toys.optflags & FLAG_W) ? 46 : 16; 980f66f451Sopenharmony_ci *tm = time(tm+1); 990f66f451Sopenharmony_ci fd = xopenro(file); 1000f66f451Sopenharmony_ci loc = xlseek(fd, 0, SEEK_END); 1010f66f451Sopenharmony_ci 1020f66f451Sopenharmony_ci // Loop through file structures in reverse order. 1030f66f451Sopenharmony_ci for (;;) { 1040f66f451Sopenharmony_ci loc -= sizeof(ut); 1050f66f451Sopenharmony_ci if(loc < 0) break; 1060f66f451Sopenharmony_ci xlseek(fd, loc, SEEK_SET); 1070f66f451Sopenharmony_ci 1080f66f451Sopenharmony_ci // Read next structure, determine type 1090f66f451Sopenharmony_ci xreadall(fd, &ut, sizeof(ut)); 1100f66f451Sopenharmony_ci *tm = ut.ut_tv.tv_sec; 1110f66f451Sopenharmony_ci if (*ut.ut_line == '~') { 1120f66f451Sopenharmony_ci if (!strcmp(ut.ut_user, "runlevel")) ut.ut_type = RUN_LVL; 1130f66f451Sopenharmony_ci else if (!strcmp(ut.ut_user, "reboot")) ut.ut_type = BOOT_TIME; 1140f66f451Sopenharmony_ci else if (!strcmp(ut.ut_user, "shutdown")) ut.ut_type = SHUTDOWN_TIME; 1150f66f451Sopenharmony_ci } else if (!*ut.ut_user) ut.ut_type = DEAD_PROCESS; 1160f66f451Sopenharmony_ci else if (*ut.ut_user && *ut.ut_line && ut.ut_type != DEAD_PROCESS 1170f66f451Sopenharmony_ci && strcmp(ut.ut_user, "LOGIN")) ut.ut_type = USER_PROCESS; 1180f66f451Sopenharmony_ci /* The pair of terminal names '|' / '}' logs the 1190f66f451Sopenharmony_ci * old/new system time when date changes it. 1200f66f451Sopenharmony_ci */ 1210f66f451Sopenharmony_ci if (!strcmp(ut.ut_user, "date")) { 1220f66f451Sopenharmony_ci if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; 1230f66f451Sopenharmony_ci if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; 1240f66f451Sopenharmony_ci } 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_ci if ((ut.ut_type == SHUTDOWN_TIME) || ((ut.ut_type == RUN_LVL) && 1270f66f451Sopenharmony_ci (((ut.ut_pid & 255) == '0') || ((ut.ut_pid & 255) == '6')))) 1280f66f451Sopenharmony_ci { 1290f66f451Sopenharmony_ci tm[1] = tm[2] = (time_t)ut.ut_tv.tv_sec; 1300f66f451Sopenharmony_ci free_list(); 1310f66f451Sopenharmony_ci curlog_type = RUN_LVL; 1320f66f451Sopenharmony_ci } else if (ut.ut_type == BOOT_TIME) { 1330f66f451Sopenharmony_ci seize_duration(tm[0], tm[1]); 1340f66f451Sopenharmony_ci strcpy(ut.ut_line, "system boot"); 1350f66f451Sopenharmony_ci free_list(); 1360f66f451Sopenharmony_ci printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, 1370f66f451Sopenharmony_ci ut.ut_line, pwidth, pwidth, ut.ut_host, 1380f66f451Sopenharmony_ci toybuf, toybuf+18, toybuf+28); 1390f66f451Sopenharmony_ci curlog_type = BOOT_TIME; 1400f66f451Sopenharmony_ci tm[2] = (time_t)ut.ut_tv.tv_sec; 1410f66f451Sopenharmony_ci } else if (ut.ut_type == USER_PROCESS && *ut.ut_line) { 1420f66f451Sopenharmony_ci struct arg_list *l = find_and_dlink(&TT.list, ut.ut_line); 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci if (l) { 1450f66f451Sopenharmony_ci struct utmp *u = (struct utmp *)l->arg; 1460f66f451Sopenharmony_ci seize_duration(tm[0], u->ut_tv.tv_sec); 1470f66f451Sopenharmony_ci printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, 1480f66f451Sopenharmony_ci ut.ut_line, pwidth, pwidth, ut.ut_host, 1490f66f451Sopenharmony_ci toybuf, toybuf+18, toybuf+28); 1500f66f451Sopenharmony_ci free(l->arg); 1510f66f451Sopenharmony_ci free(l); 1520f66f451Sopenharmony_ci } else { 1530f66f451Sopenharmony_ci int type = !tm[2] ? EMPTY : curlog_type; 1540f66f451Sopenharmony_ci if (!tm[2]) { //check process's current status (alive or dead). 1550f66f451Sopenharmony_ci if ((ut.ut_pid > 0) && (kill(ut.ut_pid, 0)!=0) && (errno == ESRCH)) 1560f66f451Sopenharmony_ci type = INIT_PROCESS; 1570f66f451Sopenharmony_ci } 1580f66f451Sopenharmony_ci seize_duration(tm[0], tm[2]); 1590f66f451Sopenharmony_ci switch (type) { 1600f66f451Sopenharmony_ci case EMPTY: 1610f66f451Sopenharmony_ci strcpy(toybuf+18, " still"); 1620f66f451Sopenharmony_ci strcpy(toybuf+28, "logged in"); 1630f66f451Sopenharmony_ci break; 1640f66f451Sopenharmony_ci case RUN_LVL: 1650f66f451Sopenharmony_ci strcpy(toybuf+18, "- down "); 1660f66f451Sopenharmony_ci break; 1670f66f451Sopenharmony_ci case BOOT_TIME: 1680f66f451Sopenharmony_ci strcpy(toybuf+18, "- crash"); 1690f66f451Sopenharmony_ci break; 1700f66f451Sopenharmony_ci case INIT_PROCESS: 1710f66f451Sopenharmony_ci strcpy(toybuf+18, " gone"); 1720f66f451Sopenharmony_ci strcpy(toybuf+28, "- no logout"); 1730f66f451Sopenharmony_ci break; 1740f66f451Sopenharmony_ci default: 1750f66f451Sopenharmony_ci break; 1760f66f451Sopenharmony_ci } 1770f66f451Sopenharmony_ci printf("%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n", ut.ut_user, 1780f66f451Sopenharmony_ci ut.ut_line, pwidth, pwidth, ut.ut_host, 1790f66f451Sopenharmony_ci toybuf, toybuf+18, toybuf+28); 1800f66f451Sopenharmony_ci } 1810f66f451Sopenharmony_ci llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); 1820f66f451Sopenharmony_ci } else if (ut.ut_type == DEAD_PROCESS && *ut.ut_line) 1830f66f451Sopenharmony_ci llist_add_node(&TT.list, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut))); 1840f66f451Sopenharmony_ci 1850f66f451Sopenharmony_ci loc -= sizeof(ut); 1860f66f451Sopenharmony_ci if(loc < 0) break; 1870f66f451Sopenharmony_ci xlseek(fd, loc, SEEK_SET); 1880f66f451Sopenharmony_ci } 1890f66f451Sopenharmony_ci 1900f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) { 1910f66f451Sopenharmony_ci xclose(fd); 1920f66f451Sopenharmony_ci free_list(); 1930f66f451Sopenharmony_ci } 1940f66f451Sopenharmony_ci 1950f66f451Sopenharmony_ci xprintf("\n%s begins %-24.24s\n", basename(file), ctime(tm)); 1960f66f451Sopenharmony_ci} 197