/* 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));
}