*/
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/fcntl.h>
+#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <ctype.h>
-#include <utmp.h>
+#include <utmpx.h>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
+#include <libgen.h>
#include "c.h"
#include "nls.h"
#include "carefulputc.h"
#include "strutils.h"
#include "timeutils.h"
-#include "boottime.h"
-
-#if defined(_HAVE_UT_TV)
-# define UL_UT_TIME ut_tv.tv_sec
-#else
-# define UL_UT_TIME ut_time
-#endif
+#include "monotonic.h"
#ifndef SHUTDOWN_TIME
# define SHUTDOWN_TIME 254
char **show; /* Match search list */
- char **altv; /* Alternate wtmp files */
- unsigned int altc; /* Number of alternative files */
- unsigned int alti; /* Index number of the alternative file */
-
struct timeval boot_time; /* system boot time */
time_t since; /* at what time to start displaying the file */
time_t until; /* at what time to stop displaying the file */
/* Double linked list of struct utmp's */
struct utmplist {
- struct utmp ut;
+ struct utmpx ut;
struct utmplist *next;
struct utmplist *prev;
};
-struct utmplist *utmplist = NULL;
/* Types of listing */
enum {
enum {
LAST_TIMEFTM_NONE = 0,
- LAST_TIMEFTM_SHORT_CTIME,
- LAST_TIMEFTM_FULL_CTIME,
- LAST_TIMEFTM_ISO8601
+ LAST_TIMEFTM_SHORT,
+ LAST_TIMEFTM_CTIME,
+ LAST_TIMEFTM_ISO8601,
+
+ LAST_TIMEFTM_HHMM, /* non-public */
};
struct last_timefmt {
const char *name;
- int in;
- int out;
+ int in_len; /* log-in */
+ int in_fmt;
+ int out_len; /* log-out */
+ int out_fmt;
};
static struct last_timefmt timefmts[] = {
- [LAST_TIMEFTM_NONE] = { "notime", 0, 0 },
- [LAST_TIMEFTM_SHORT_CTIME] = { "short", 16, 7},
- [LAST_TIMEFTM_FULL_CTIME] = { "full", 24, 26},
- [LAST_TIMEFTM_ISO8601] = { "iso", 24, 26}
+ [LAST_TIMEFTM_NONE] = { .name = "notime" },
+ [LAST_TIMEFTM_SHORT] = {
+ .name = "short",
+ .in_len = 16,
+ .out_len = 7,
+ .in_fmt = LAST_TIMEFTM_CTIME,
+ .out_fmt = LAST_TIMEFTM_HHMM
+ },
+ [LAST_TIMEFTM_CTIME] = {
+ .name = "full",
+ .in_len = 24,
+ .out_len = 26,
+ .in_fmt = LAST_TIMEFTM_CTIME,
+ .out_fmt = LAST_TIMEFTM_CTIME
+ },
+ [LAST_TIMEFTM_ISO8601] = {
+ .name = "iso",
+ .in_len = 25,
+ .out_len = 27,
+ .in_fmt = LAST_TIMEFTM_ISO8601,
+ .out_fmt = LAST_TIMEFTM_ISO8601
+ }
};
/* Global variables */
static unsigned int recsdone; /* Number of records listed */
static time_t lastdate; /* Last date we've seen */
+static time_t currentdate; /* date when we started processing the file */
/* --time-format=option parser */
-static int which_time_format(const char *optarg)
+static int which_time_format(const char *s)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
- if (strcmp(timefmts[i].name, optarg) == 0)
+ if (strcmp(timefmts[i].name, s) == 0)
return i;
}
- errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
+ errx(EXIT_FAILURE, _("unknown time format: %s"), s);
}
/*
* Read one utmp entry, return in new format.
* Automatically reposition file pointer.
*/
-static int uread(const struct last_control *ctl, FILE *fp, struct utmp *u,
- int *quit)
+static int uread(FILE *fp, struct utmpx *u, int *quit, const char *filename)
{
static int utsize;
static char buf[UCHUNKSIZE];
/*
* Normal read.
*/
- return fread(u, sizeof(struct utmp), 1, fp);
+ return fread(u, sizeof(struct utmpx), 1, fp);
}
if (u == NULL) {
/*
* Initialize and position.
*/
- utsize = sizeof(struct utmp);
+ utsize = sizeof(struct utmpx);
fseeko(fp, 0, SEEK_END);
fpos = ftello(fp);
if (fpos == 0)
return 0;
o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE;
if (fseeko(fp, o, SEEK_SET) < 0) {
- warn(_("seek on %s failed"), ctl->altv[ctl->alti]);
+ warn(_("seek on %s failed"), filename);
return 0;
}
bpos = (int)(fpos - o);
if (fread(buf, bpos, 1, fp) != 1) {
- warn(_("cannot read %s"), ctl->altv[ctl->alti]);
+ warn(_("cannot read %s"), filename);
return 0;
}
fpos = o;
*/
bpos -= utsize;
if (bpos >= 0) {
- memcpy(u, buf + bpos, sizeof(struct utmp));
+ memcpy(u, buf + bpos, sizeof(struct utmpx));
return 1;
}
*/
memcpy(tmp + (-bpos), buf, utsize + bpos);
if (fseeko(fp, fpos, SEEK_SET) < 0) {
- warn(_("seek on %s failed"), ctl->altv[ctl->alti]);
+ warn(_("seek on %s failed"), filename);
return 0;
}
* Read another UCHUNKSIZE bytes.
*/
if (fread(buf, UCHUNKSIZE, 1, fp) != 1) {
- warn(_("cannot read %s"), ctl->altv[ctl->alti]);
+ warn(_("cannot read %s"), filename);
return 0;
}
memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos);
bpos += UCHUNKSIZE;
- memcpy(u, tmp, sizeof(struct utmp));
+ memcpy(u, tmp, sizeof(struct utmpx));
return 1;
}
return getnameinfo(sa, salen, result, size, NULL, 0, flags);
}
-static int time_formatter(const struct last_control *ctl, char *dst,
- size_t dlen, time_t *when, int pos)
+static int time_formatter(int fmt, char *dst, size_t dlen, time_t *when)
{
- struct tm *tm;
int ret = 0;
- switch (ctl->time_fmt) {
+ switch (fmt) {
case LAST_TIMEFTM_NONE:
*dst = 0;
break;
- case LAST_TIMEFTM_SHORT_CTIME:
- if (pos == 0)
- ret = sprintf(dst, "%s", ctime(when));
- else {
- tm = localtime(when);
- if (!strftime(dst, dlen, "- %H:%M", tm))
- ret = -1;
- }
+ case LAST_TIMEFTM_HHMM:
+ {
+ struct tm *tm = localtime(when);
+ if (!snprintf(dst, dlen, "%02d:%02d", tm->tm_hour, tm->tm_min))
+ ret = -1;
break;
- case LAST_TIMEFTM_FULL_CTIME:
- if (pos == 0)
- ret = sprintf(dst, "%s", ctime(when));
- else
- ret = sprintf(dst, "- %s", ctime(when));
+ }
+ case LAST_TIMEFTM_CTIME:
+ snprintf(dst, dlen, "%s", ctime(when));
+ ret = rtrim_whitespace((unsigned char *) dst);
break;
case LAST_TIMEFTM_ISO8601:
- tm = localtime(when);
- if (pos == 0) {
- if (!strftime(dst, dlen, "%Y-%m-%dT%H:%M:%S%z", tm))
- ret = -1;
- } else if (!strftime(dst, dlen, "- %Y-%m-%dT%H:%M:%S%z", tm))
- ret = -1;
+ ret = strtime_iso(when, ISO_TIMESTAMP_T, dst, dlen);
break;
default:
abort();
/*
* Show one line of information on screen
*/
-static int list(const struct last_control *ctl, struct utmp *p, time_t t, int what)
+static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_time, int what)
{
- time_t secs, tmp, epoch;
+ time_t secs, utmp_time;
char logintime[LAST_TIMESTAMP_LEN];
char logouttime[LAST_TIMESTAMP_LEN];
char length[LAST_TIMESTAMP_LEN];
char final[512];
- char utline[UT_LINESIZE+1];
+ char utline[sizeof(p->ut_line) + 1];
char domain[256];
char *s;
int mins, hours, days;
* uucp and ftp have special-type entries
*/
utline[0] = 0;
- strncat(utline, p->ut_line, UT_LINESIZE);
+ strncat(utline, p->ut_line, sizeof(utline) - 1);
if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3]))
utline[3] = 0;
if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4]))
if (ctl->show) {
char **walk;
for (walk = ctl->show; *walk; walk++) {
- if (strncmp(p->ut_user, *walk, UT_NAMESIZE) == 0 ||
+ if (strncmp(p->ut_user, *walk, sizeof(p->ut_user)) == 0 ||
strcmp(utline, *walk) == 0 ||
(strncmp(utline, "tty", 3) == 0 &&
strcmp(utline + 3, *walk) == 0)) break;
/*
* Calculate times
*/
- tmp = p->UL_UT_TIME;
+ fmt = &timefmts[ctl->time_fmt];
- if (ctl->present && (ctl->present < tmp || (0 < t && t < ctl->present)))
- return 0;
+ utmp_time = p->ut_tv.tv_sec;
- if (time_formatter(ctl, &logintime[0], sizeof(logintime), &tmp, 0) < 0 ||
- time_formatter(ctl, &logouttime[0], sizeof(logouttime), &t, 1) < 0)
+ if (ctl->present) {
+ if (ctl->present < utmp_time)
+ return 0;
+ if (0 < logout_time && logout_time < ctl->present)
+ return 0;
+ }
+
+ /* log-in time */
+ if (time_formatter(fmt->in_fmt, logintime,
+ sizeof(logintime), &utmp_time) < 0)
errx(EXIT_FAILURE, _("preallocation size exceeded"));
- secs = t - p->UL_UT_TIME;
+ /* log-out time */
+ secs = logout_time - utmp_time; /* Under strange circumstances, secs < 0 can happen */
mins = (secs / 60) % 60;
hours = (secs / 3600) % 24;
days = secs / 86400;
- epoch = time(NULL);
- if (t == epoch) {
- if (ctl->time_fmt > LAST_TIMEFTM_SHORT_CTIME) {
+ strcpy(logouttime, "- ");
+ if (time_formatter(fmt->out_fmt, logouttime + 2,
+ sizeof(logouttime) - 2, &logout_time) < 0)
+ errx(EXIT_FAILURE, _("preallocation size exceeded"));
+
+ if (logout_time == currentdate) {
+ if (ctl->time_fmt > LAST_TIMEFTM_SHORT) {
sprintf(logouttime, " still running");
length[0] = 0;
} else {
sprintf(logouttime, " still");
sprintf(length, "running");
}
- } else if (days)
- sprintf(length, "(%d+%02d:%02d)", days, hours, mins);
- else
- sprintf(length, " (%02d:%02d)", hours, mins);
+ } else if (days) {
+ sprintf(length, "(%d+%02d:%02d)", days, abs(hours), abs(mins)); /* hours and mins always shown as positive (w/o minus sign!) even if secs < 0 */
+ } else if (hours) {
+ sprintf(length, " (%02d:%02d)", hours, abs(mins)); /* mins always shown as positive (w/o minus sign!) even if secs < 0 */
+ } else if (secs >= 0) {
+ sprintf(length, " (%02d:%02d)", hours, mins);
+ } else {
+ sprintf(length, " (-00:%02d)", abs(mins)); /* mins always shown as positive (w/o minus sign!) even if secs < 0 */
+ }
switch(what) {
case R_CRASH:
sprintf(logouttime, "- down ");
break;
case R_NOW:
- if (ctl->time_fmt > LAST_TIMEFTM_SHORT_CTIME) {
+ if (ctl->time_fmt > LAST_TIMEFTM_SHORT) {
sprintf(logouttime, " still logged in");
length[0] = 0;
} else {
}
break;
case R_PHANTOM:
- if (ctl->time_fmt > LAST_TIMEFTM_SHORT_CTIME) {
+ if (ctl->time_fmt > LAST_TIMEFTM_SHORT) {
sprintf(logouttime, " gone - no logout");
length[0] = 0;
- } else if (ctl->time_fmt == LAST_TIMEFTM_SHORT_CTIME) {
+ } else if (ctl->time_fmt == LAST_TIMEFTM_SHORT) {
sprintf(logouttime, " gone");
sprintf(length, "- no logout");
} else {
sprintf(length, "no logout");
}
break;
- case R_REBOOT:
- break;
case R_TIMECHANGE:
logouttime[0] = 0;
length[0] = 0;
break;
case R_NORMAL:
+ case R_REBOOT:
break;
default:
abort();
*/
r = -1;
if (ctl->usedns || ctl->useip)
- r = dns_lookup(domain, sizeof(domain), ctl->useip, p->ut_addr_v6);
- if (r < 0) {
- len = UT_HOSTSIZE;
- if (len >= (int)sizeof(domain)) len = sizeof(domain) - 1;
- domain[0] = 0;
- strncat(domain, p->ut_host, len);
- }
-
- fmt = &timefmts[ctl->time_fmt];
+ r = dns_lookup(domain, sizeof(domain), ctl->useip, (int32_t*)p->ut_addr_v6);
+ if (r < 0)
+ mem2strcpy(domain, p->ut_host, sizeof(p->ut_host), sizeof(domain));
if (ctl->showhost) {
if (!ctl->altlist) {
"%-8.*s %-12.12s %-16.*s %-*.*s %-*.*s %s\n",
ctl->name_len, p->ut_user, utline,
ctl->domain_len, domain,
- fmt->in, fmt->in, logintime, fmt->out, fmt->out,
+ fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
logouttime, length);
} else {
len = snprintf(final, sizeof(final),
"%-8.*s %-12.12s %-*.*s %-*.*s %-12.12s %s\n",
ctl->name_len, p->ut_user, utline,
- fmt->in, fmt->in, logintime, fmt->out, fmt->out,
+ fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
logouttime, length, domain);
}
} else
len = snprintf(final, sizeof(final),
"%-8.*s %-12.12s %-*.*s %-*.*s %s\n",
ctl->name_len, p->ut_user, utline,
- fmt->in, fmt->in, logintime, fmt->out, fmt->out,
+ fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
logouttime, length);
#if defined(__GLIBC__)
}
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(const struct last_control *ctl)
{
+ FILE *out = stdout;
fputs(USAGE_HEADER, out);
fprintf(out, _(
" %s [options] [<username>...] [<tty>...]\n"), program_invocation_short_name);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Show a listing of last logged in users.\n"), out);
+
fputs(USAGE_OPTIONS, out);
fputs(_(" -<number> how many lines to show\n"), out);
fputs(_(" -a, --hostlast display hostnames in the last column\n"), out);
fputs(_(" -d, --dns translate the IP number back into a hostname\n"), out);
fprintf(out,
- _(" -f, --file <file> use a specific file instead of %s\n"), _PATH_WTMP);
+ _(" -f, --file <file> use a specific file instead of %s\n"), ctl->lastb ? _PATH_BTMP : _PATH_WTMP);
fputs(_(" -F, --fulltimes print full login and logout times and dates\n"), out);
fputs(_(" -i, --ip display IP numbers in numbers-and-dots notation\n"), out);
fputs(_(" -n, --limit <number> how many lines to show\n"), out);
fputs(_(" -R, --nohostname don't display the hostname field\n"), out);
fputs(_(" -s, --since <time> display the lines since the specified time\n"), out);
fputs(_(" -t, --until <time> display the lines until the specified time\n"), out);
- fputs(_(" -p, --present <time> display who where present at the specified time\n"), out);
+ fputs(_(" -p, --present <time> display who were present at the specified time\n"), out);
fputs(_(" -w, --fullnames display full user and domain names\n"), out);
fputs(_(" -x, --system display system shutdown entries and run level changes\n"), out);
- fputs(_(" --time-format <format> show time stamp using format:\n"), out);
- fputs(_(" [notime|short|full|iso]\n"), out);
+ fputs(_(" --time-format <format> show timestamps in the specified <format>:\n"
+ " notime|short|full|iso\n"), out);
fputs(USAGE_SEPARATOR, out);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
- fprintf(out, USAGE_MAN_TAIL("last(1)"));
+ printf(USAGE_HELP_OPTIONS(22));
+ printf(USAGE_MAN_TAIL("last(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
-static int is_phantom(const struct last_control *ctl, struct utmp *ut)
+static int is_phantom(const struct last_control *ctl, struct utmpx *ut)
{
struct passwd *pw;
- char path[32];
- FILE *f = NULL;
- unsigned int loginuid;
+ char path[sizeof(ut->ut_line) + 16];
int ret = 0;
- if (ut->UL_UT_TIME < ctl->boot_time.tv_sec)
+ if (ut->ut_tv.tv_sec < ctl->boot_time.tv_sec)
return 1;
- pw = getpwnam(ut->ut_name);
+ ut->ut_user[sizeof(ut->ut_user) - 1] = '\0';
+ pw = getpwnam(ut->ut_user);
if (!pw)
return 1;
sprintf(path, "/proc/%u/loginuid", ut->ut_pid);
- if (access(path, R_OK) != 0 || !(f = fopen(path, "r")))
- return 1;
+ if (access(path, R_OK) == 0) {
+ unsigned int loginuid;
+ FILE *f = NULL;
+
+ if (!(f = fopen(path, "r")))
+ return 1;
+ if (fscanf(f, "%u", &loginuid) != 1)
+ ret = 1;
+ fclose(f);
+ if (!ret && pw->pw_uid != loginuid)
+ return 1;
+ } else {
+ struct stat st;
- if (fscanf(f, "%u", &loginuid) != 1)
- ret = 1;
- fclose(f);
- if (!ret && pw->pw_uid != loginuid)
- return 1;
+ sprintf(path, "/dev/%s", ut->ut_line);
+ if (stat(path, &st))
+ return 1;
+ if (pw->pw_uid != st.st_uid)
+ return 1;
+ }
return ret;
}
-static void process_wtmp_file(const struct last_control *ctl)
+static void process_wtmp_file(const struct last_control *ctl,
+ const char *filename)
{
- FILE *fp; /* Filepointer of wtmp file */
+ FILE *fp; /* File pointer of wtmp file */
- struct utmp ut; /* Current utmp entry */
+ struct utmpx ut; /* Current utmp entry */
+ struct utmplist *ulist = NULL; /* All entries */
struct utmplist *p; /* Pointer into utmplist */
struct utmplist *next; /* Pointer into utmplist */
int down = 0; /* Down flag */
time(&lastdown);
- lastrch = lastdown;
-
/*
* Fill in 'lastdate'
*/
- lastdate = lastdown;
+ lastdate = currentdate = lastrch = lastdown;
/*
* Install signal handlers
/*
* Open the utmp file
*/
- if ((fp = fopen(ctl->altv[ctl->alti], "r")) == NULL)
- err(EXIT_FAILURE, _("cannot open %s"), ctl->altv[ctl->alti]);
+ if ((fp = fopen(filename, "r")) == NULL)
+ err(EXIT_FAILURE, _("cannot open %s"), filename);
/*
* Optimize the buffer size.
/*
* Read first structure to capture the time field
*/
- if (uread(ctl, fp, &ut, NULL) == 1)
- begintime = ut.UL_UT_TIME;
+ if (uread(fp, &ut, NULL, filename) == 1)
+ begintime = ut.ut_tv.tv_sec;
else {
if (fstat(fileno(fp), &st) != 0)
- err(EXIT_FAILURE, _("stat failed %s"), ctl->altv[ctl->alti]);
+ err(EXIT_FAILURE, _("stat of %s failed"), filename);
begintime = st.st_ctime;
quit = 1;
}
* Go to end of file minus one structure
* and/or initialize utmp reading code.
*/
- uread(ctl, fp, NULL, NULL);
+ uread(fp, NULL, NULL, filename);
/*
* Read struct after struct backwards from the file.
*/
while (!quit) {
- if (uread(ctl, fp, &ut, &quit) != 1)
+ if (uread(fp, &ut, &quit, filename) != 1)
break;
- if (ctl->since && ut.UL_UT_TIME < ctl->since)
+ if (ctl->since && ut.ut_tv.tv_sec < ctl->since)
continue;
- if (ctl->until && ctl->until < ut.UL_UT_TIME)
+ if (ctl->until && ctl->until < ut.ut_tv.tv_sec)
continue;
- lastdate = ut.UL_UT_TIME;
+ lastdate = ut.ut_tv.tv_sec;
if (ctl->lastb) {
- quit = list(ctl, &ut, ut.UL_UT_TIME, R_NORMAL);
+ quit = list(ctl, &ut, ut.ut_tv.tv_sec, R_NORMAL);
continue;
}
strcpy(ut.ut_line, "system down");
quit = list(ctl, &ut, lastboot, R_NORMAL);
}
- lastdown = lastrch = ut.UL_UT_TIME;
+ lastdown = lastrch = ut.ut_tv.tv_sec;
down = 1;
break;
case OLD_TIME:
case BOOT_TIME:
strcpy(ut.ut_line, "system boot");
quit = list(ctl, &ut, lastdown, R_REBOOT);
- lastboot = ut.UL_UT_TIME;
+ lastboot = ut.ut_tv.tv_sec;
down = 1;
break;
case RUN_LVL:
quit = list(ctl, &ut, lastrch, R_NORMAL);
}
if (x == '0' || x == '6') {
- lastdown = ut.UL_UT_TIME;
+ lastdown = ut.ut_tv.tv_sec;
down = 1;
ut.ut_type = SHUTDOWN_TIME;
}
- lastrch = ut.UL_UT_TIME;
+ lastrch = ut.ut_tv.tv_sec;
break;
case USER_PROCESS:
* the same ut_line.
*/
c = 0;
- for (p = utmplist; p; p = next) {
+ for (p = ulist; p; p = next) {
next = p->next;
if (strncmp(p->ut.ut_line, ut.ut_line,
- UT_LINESIZE) == 0) {
+ sizeof(ut.ut_line)) == 0) {
/* Show it */
if (c == 0) {
- quit = list(ctl, &ut, p->ut.UL_UT_TIME, R_NORMAL);
+ quit = list(ctl, &ut, p->ut.ut_tv.tv_sec, R_NORMAL);
c = 1;
}
- if (p->next) p->next->prev = p->prev;
+ if (p->next)
+ p->next->prev = p->prev;
if (p->prev)
p->prev->next = p->next;
else
- utmplist = p->next;
+ ulist = p->next;
free(p);
}
}
c = whydown;
quit = list(ctl, &ut, lastboot, c);
}
- /* FALLTHRU */
+ /* fallthrough */
case DEAD_PROCESS:
/*
if (ut.ut_line[0] == 0)
break;
p = xmalloc(sizeof(struct utmplist));
- memcpy(&p->ut, &ut, sizeof(struct utmp));
- p->next = utmplist;
+ memcpy(&p->ut, &ut, sizeof(struct utmpx));
+ p->next = ulist;
p->prev = NULL;
- if (utmplist) utmplist->prev = p;
- utmplist = p;
+ if (ulist)
+ ulist->prev = p;
+ ulist = p;
break;
case EMPTY:
case INIT_PROCESS:
case LOGIN_PROCESS:
+#ifdef ACCOUNTING
case ACCOUNTING:
+#endif
/* ignored ut_types */
break;
default:
- warnx("unrecogized ut_type: %d", ut.ut_type);
+ warnx("unrecognized ut_type: %d", ut.ut_type);
}
/*
* If we saw a shutdown/reboot record we can remove
- * the entire current utmplist.
+ * the entire current ulist.
*/
if (down) {
- lastboot = ut.UL_UT_TIME;
+ lastboot = ut.ut_tv.tv_sec;
whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH;
- for (p = utmplist; p; p = next) {
+ for (p = ulist; p; p = next) {
next = p->next;
free(p);
}
- utmplist = NULL;
+ ulist = NULL;
down = 0;
}
}
- printf(_("\n%s begins %s"), basename(ctl->altv[ctl->alti]), ctime(&begintime));
+ if (ctl->time_fmt != LAST_TIMEFTM_NONE) {
+ struct last_timefmt *fmt;
+ char timestr[LAST_TIMESTAMP_LEN];
+ char *tmp = xstrdup(filename);
+
+ fmt = &timefmts[ctl->time_fmt];
+ if (time_formatter(fmt->in_fmt, timestr,
+ sizeof(timestr), &begintime) < 0)
+ errx(EXIT_FAILURE, _("preallocation size exceeded"));
+ printf(_("\n%s begins %s\n"), basename(tmp), timestr);
+ free(tmp);
+ }
+
fclose(fp);
- for (p = utmplist; p; p = next) {
+
+ for (p = ulist; p; p = next) {
next = p->next;
free(p);
}
struct last_control ctl = {
.showhost = TRUE,
.name_len = LAST_LOGIN_LEN,
- .time_fmt = LAST_TIMEFTM_SHORT_CTIME,
+ .time_fmt = LAST_TIMEFTM_SHORT,
.domain_len = LAST_DOMAIN_LEN
};
+ char **files = NULL;
+ size_t i, nfiles = 0;
int c;
usec_t p;
{ "time-format", required_argument, NULL, OPT_TIME_FORMAT },
{ NULL, 0, NULL, 0 }
};
- static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
{ 'F', OPT_TIME_FORMAT }, /* fulltime, time-format */
{ 0 }
};
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
-
+ /*
+ * Which file do we want to read?
+ */
+ ctl.lastb = strcmp(program_invocation_short_name, "lastb") == 0 ? 1 : 0;
while ((c = getopt_long(argc, argv,
"hVf:n:RxadFit:p:s:0123456789w", long_opts, NULL)) != -1) {
switch(c) {
case 'h':
- usage(stdout);
+ usage(&ctl);
break;
case 'V':
printf(UTIL_LINUX_VERSION);
ctl.maxrecs = strtos32_or_err(optarg, _("failed to parse number"));
break;
case 'f':
- if (!ctl.altv)
- ctl.altv = xmalloc(sizeof(char *) * argc);
- ctl.altv[ctl.altc++] = xstrdup(optarg);
+ if (!files)
+ files = xmalloc(sizeof(char *) * argc);
+ files[nfiles++] = xstrdup(optarg);
break;
case 'd':
ctl.usedns = 1;
ctl.altlist = 1;
break;
case 'F':
- ctl.time_fmt = LAST_TIMEFTM_FULL_CTIME;
+ ctl.time_fmt = LAST_TIMEFTM_CTIME;
break;
case 'p':
if (parse_timestamp(optarg, &p) < 0)
ctl.until = (time_t) (p / 1000000);
break;
case 'w':
- if (ctl.name_len < UT_NAMESIZE)
- ctl.name_len = UT_NAMESIZE;
- if (ctl.domain_len < UT_HOSTSIZE)
- ctl.domain_len = UT_HOSTSIZE;
+ if (ctl.name_len < sizeof(((struct utmpx *) 0)->ut_user))
+ ctl.name_len = sizeof(((struct utmpx *) 0)->ut_user);
+ if (ctl.domain_len < sizeof(((struct utmpx *) 0)->ut_host))
+ ctl.domain_len = sizeof(((struct utmpx *) 0)->ut_host);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ctl.time_fmt = which_time_format(optarg);
break;
default:
- usage(stderr);
- break;
+ errtryhelp(EXIT_FAILURE);
}
}
if (optind < argc)
ctl.show = argv + optind;
- /*
- * Which file do we want to read?
- */
- ctl.lastb = strcmp(program_invocation_short_name, "lastb") == 0 ? 1 : 0;
- if (!ctl.altc) {
- ctl.altv = xmalloc(sizeof(char *));
- if (ctl.lastb)
- ctl.altv[0] = xstrdup(_PATH_BTMP);
- else
- ctl.altv[0] = xstrdup(_PATH_WTMP);
- ctl.altc++;
+ if (!files) {
+ files = xmalloc(sizeof(char *));
+ files[nfiles++] = xstrdup(ctl.lastb ? _PATH_BTMP : _PATH_WTMP);
}
- for (; ctl.alti < ctl.altc; ctl.alti++) {
+ for (i = 0; i < nfiles; i++) {
get_boot_time(&ctl.boot_time);
- process_wtmp_file(&ctl);
- free(ctl.altv[ctl.alti]);
+ process_wtmp_file(&ctl, files[i]);
+ free(files[i]);
}
- free(ctl.altv);
+ free(files);
return EXIT_SUCCESS;
}