#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <utmp.h>
+#include <utmpx.h>
#include <time.h>
#include <ctype.h>
#include <getopt.h>
#include "nls.h"
#include "xalloc.h"
#include "closestream.h"
-
-static char *timetostr(const time_t time)
-{
- static char s[29]; /* [Sun Sep 01 00:00:00 1998 PST] */
-
- if (time != 0)
- strftime(s, 29, "%a %b %d %T %Y %Z", localtime(&time));
- else
- s[0] = '\0';
-
- return s;
-}
+#include "timeutils.h"
static time_t strtotime(const char *s_time)
{
if (s_time[0] == ' ' || s_time[0] == '\0')
return (time_t)0;
- strptime(s_time, "%a %b %d %T %Y", &tm);
-
- /* Cheesy way of checking for DST */
- if (s_time[26] == 'D')
- tm.tm_isdst = 1;
+ if (isdigit(s_time[0])) {
+ /* [1998-09-01T01:00:00,000000+00:00]
+ * Subseconds are parsed with strtousec(). Timezone is
+ * always UTC-0 */
+ strptime(s_time, "%Y-%m-%dT%H:%M:%S", &tm);
+ } else {
+ /* [Tue Sep 01 00:00:00 1998 GMT] */
+ strptime(s_time, "%a %b %d %T %Y", &tm);
+ /* Cheesy way of checking for DST. This could be needed
+ * with legacy dumps that used localtime(3). */
+ if (s_time[26] == 'D')
+ tm.tm_isdst = 1;
+ }
+ return timegm(&tm);
+}
- return mktime(&tm);
+static suseconds_t strtousec(const char *s_time)
+{
+ const char *s = strchr(s_time, ',');
+ if (s)
+ return (suseconds_t) atoi(s + 1);
+ return 0;
}
#define cleanse(x) xcleanse(x, sizeof(x))
*s = '?';
}
-static void print_utline(struct utmp ut)
+static void print_utline(struct utmpx *ut, FILE *out)
{
- char *addr_string, *time_string;
- struct in_addr in;
-
- in.s_addr = ut.ut_addr;
- addr_string = inet_ntoa(in);
- time_string = timetostr(ut.ut_time);
- cleanse(ut.ut_id);
- cleanse(ut.ut_user);
- cleanse(ut.ut_line);
- cleanse(ut.ut_host);
-
- /* pid id user line host addr time */
- printf("[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15.15s] [%-28.28s]\n",
- ut.ut_type, ut.ut_pid, ut.ut_id, 8, UT_NAMESIZE, ut.ut_user,
- 12, UT_LINESIZE, ut.ut_line, 20, UT_HOSTSIZE, ut.ut_host,
+ const char *addr_string;
+ char buffer[INET6_ADDRSTRLEN];
+ char time_string[40];
+ struct timeval tv;
+
+ if (ut->ut_addr_v6[1] || ut->ut_addr_v6[2] || ut->ut_addr_v6[3])
+ addr_string = inet_ntop(AF_INET6, &(ut->ut_addr_v6), buffer, sizeof(buffer));
+ else
+ addr_string = inet_ntop(AF_INET, &(ut->ut_addr_v6), buffer, sizeof(buffer));
+
+ tv.tv_sec = ut->ut_tv.tv_sec;
+ tv.tv_usec = ut->ut_tv.tv_usec;
+
+ if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_GT, time_string,
+ sizeof(time_string)) != 0)
+ return;
+ cleanse(ut->ut_id);
+ cleanse(ut->ut_user);
+ cleanse(ut->ut_line);
+ cleanse(ut->ut_host);
+
+ /* type pid id user line host addr time */
+ fprintf(out, "[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15s] [%s]\n",
+ ut->ut_type, ut->ut_pid, ut->ut_id,
+ 8, (int)sizeof(ut->ut_user), ut->ut_user,
+ 12, (int)sizeof(ut->ut_line), ut->ut_line,
+ 20, (int)sizeof(ut->ut_host), ut->ut_host,
addr_string, time_string);
}
#define EVENTS (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
#define NEVENTS 4
-static void roll_file(const char *filename, off_t *size)
+static void roll_file(const char *filename, off_t *size, FILE *out)
{
- FILE *fp;
+ FILE *in;
struct stat st;
- struct utmp ut;
+ struct utmpx ut;
off_t pos;
- if (!(fp = fopen(filename, "r")))
- err(EXIT_FAILURE, _("%s: open failed"), filename);
+ if (!(in = fopen(filename, "r")))
+ err(EXIT_FAILURE, _("cannot open %s"), filename);
- if (fstat(fileno(fp), &st) == -1)
- err(EXIT_FAILURE, _("%s: stat failed"), filename);
+ if (fstat(fileno(in), &st) == -1)
+ err(EXIT_FAILURE, _("stat of %s failed"), filename);
if (st.st_size == *size)
goto done;
- if (fseek(fp, *size, SEEK_SET) != (off_t) -1) {
- while (fread(&ut, sizeof(ut), 1, fp) == 1)
- print_utline(ut);
+ if (fseek(in, *size, SEEK_SET) != (off_t) -1) {
+ while (fread(&ut, sizeof(ut), 1, in) == 1)
+ print_utline(&ut, out);
}
- pos = ftello(fp);
+ pos = ftello(in);
/* If we've successfully read something, use the file position, this
* avoids data duplication. If we read nothing or hit an error,
* reset to the reported size, this handles truncated files.
*size = (pos != -1 && pos != *size) ? pos : st.st_size;
done:
- fclose(fp);
+ fclose(in);
}
-static int follow_by_inotify(FILE *fp, const char *filename)
+static int follow_by_inotify(FILE *in, const char *filename, FILE *out)
{
char buf[NEVENTS * sizeof(struct inotify_event)];
int fd, wd, event;
if (fd == -1)
return -1; /* probably reached any limit ... */
- size = ftello(fp);
- fclose(fp);
+ size = ftello(in);
+ fclose(in);
+
+ if (size < 0)
+ err(EXIT_FAILURE, _("%s: cannot get file position"), filename);
wd = inotify_add_watch(fd, filename, EVENTS);
if (wd == -1)
(struct inotify_event *) &buf[event];
if (ev->mask & IN_MODIFY)
- roll_file(filename, &size);
+ roll_file(filename, &size, out);
else {
close(wd);
wd = -1;
}
#endif /* HAVE_INOTIFY_INIT */
-static FILE *dump(FILE *fp, const char *filename, int follow)
+static FILE *dump(FILE *in, const char *filename, int follow, FILE *out)
{
- struct utmp ut;
+ struct utmpx ut;
if (follow)
- fseek(fp, -10 * sizeof(ut), SEEK_END);
+ ignore_result( fseek(in, -10 * sizeof(ut), SEEK_END) );
- while (fread(&ut, sizeof(ut), 1, fp) == 1)
- print_utline(ut);
+ while (fread(&ut, sizeof(ut), 1, in) == 1)
+ print_utline(&ut, out);
if (!follow)
- return fp;
+ return in;
#ifdef HAVE_INOTIFY_INIT
- if (follow_by_inotify(fp, filename) == 0)
+ if (follow_by_inotify(in, filename, out) == 0)
return NULL; /* file already closed */
else
#endif
/* fallback for systems without inotify or with non-free
* inotify instances */
for (;;) {
- while (fread(&ut, sizeof(ut), 1, fp) == 1)
- print_utline(ut);
+ while (fread(&ut, sizeof(ut), 1, in) == 1)
+ print_utline(&ut, out);
sleep(1);
}
- return fp;
+ return in;
}
return eaten + 1;
}
-static void undump(FILE *fp)
+static void undump(FILE *in, FILE *out)
{
- struct utmp ut;
- char s_addr[16], s_time[29], *linestart, *line;
- int count = 0;
+ struct utmpx ut;
+ char s_addr[INET6_ADDRSTRLEN + 1], s_time[29], *linestart, *line;
- line = linestart = xmalloc(1024 * sizeof(*linestart));
- s_addr[15] = 0;
+ linestart = xmalloc(1024 * sizeof(*linestart));
s_time[28] = 0;
- while (fgets(linestart, 1023, fp)) {
+ while (fgets(linestart, 1023, in)) {
line = linestart;
memset(&ut, '\0', sizeof(ut));
sscanf(line, "[%hd] [%d] [%4c] ", &ut.ut_type, &ut.ut_pid, ut.ut_id);
line += gettok(line, ut.ut_line, sizeof(ut.ut_line), 1);
line += gettok(line, ut.ut_host, sizeof(ut.ut_host), 1);
line += gettok(line, s_addr, sizeof(s_addr) - 1, 1);
- line += gettok(line, s_time, sizeof(s_time) - 1, 0);
-
- ut.ut_addr = inet_addr(s_addr);
- ut.ut_time = strtotime(s_time);
+ gettok(line, s_time, sizeof(s_time) - 1, 0);
+ if (strchr(s_addr, '.'))
+ inet_pton(AF_INET, s_addr, &(ut.ut_addr_v6));
+ else
+ inet_pton(AF_INET6, s_addr, &(ut.ut_addr_v6));
- fwrite(&ut, sizeof(ut), 1, stdout);
+ ut.ut_tv.tv_sec = strtotime(s_time);
+ ut.ut_tv.tv_usec = strtousec(s_time);
- ++count;
+ ignore_result( fwrite(&ut, sizeof(ut), 1, out) );
}
free(linestart);
}
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options] [filename]\n"), program_invocation_short_name);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Dump UTMP and WTMP files in raw format.\n"), out);
+
fputs(USAGE_OPTIONS, out);
- fputs(_(" -f, --follow output appended data as the file grows\n"
- " -r, --reverse write back dumped data into utmp file\n"), out);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
+ fputs(_(" -f, --follow output appended data as the file grows\n"), out);
+ fputs(_(" -r, --reverse write back dumped data into utmp file\n"), out);
+ fputs(_(" -o, --output <file> write to file instead of standard output\n"), out);
+ printf(USAGE_HELP_OPTIONS(22));
- fprintf(out, USAGE_MAN_TAIL("utmpdump(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ printf(USAGE_MAN_TAIL("utmpdump(1)"));
+ exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int c;
- FILE *fp = NULL;
+ FILE *in = NULL, *out = NULL;
int reverse = 0, follow = 0;
const char *filename = NULL;
static const struct option longopts[] = {
- { "follow", 0, 0, 'f' },
- { "reverse", 0, 0, 'r' },
- { "help", 0, 0, 'h' },
- { "version", 0, 0, 'V' },
- { NULL, 0, 0, 0 }
+ { "follow", no_argument, NULL, 'f' },
+ { "reverse", no_argument, NULL, 'r' },
+ { "output", required_argument, NULL, 'o' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
};
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
- while ((c = getopt_long(argc, argv, "frhV", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "fro:hV", longopts, NULL)) != -1) {
switch (c) {
case 'r':
reverse = 1;
follow = 1;
break;
- case 'h':
- usage(stdout);
+ case 'o':
+ out = fopen(optarg, "w");
+ if (!out)
+ err(EXIT_FAILURE, _("cannot open %s"),
+ optarg);
break;
+
+ case 'h':
+ usage();
case 'V':
- printf(UTIL_LINUX_VERSION);
- return EXIT_SUCCESS;
+ print_version(EXIT_SUCCESS);
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
+ if (!out)
+ out = stdout;
+
if (optind < argc) {
filename = argv[optind];
- fp = fopen(filename, "r");
- if (!fp)
- err(EXIT_FAILURE, _("%s: open failed"), filename);
+ in = fopen(filename, "r");
+ if (!in)
+ err(EXIT_FAILURE, _("cannot open %s"), filename);
} else {
if (follow)
errx(EXIT_FAILURE, _("following standard input is unsupported"));
filename = "/dev/stdin";
- fp = stdin;
+ in = stdin;
}
if (reverse) {
fprintf(stderr, _("Utmp undump of %s\n"), filename);
- undump(fp);
+ undump(in, out);
} else {
fprintf(stderr, _("Utmp dump of %s\n"), filename);
- fp = dump(fp, filename, follow);
+ in = dump(in, filename, follow, out);
}
- if (fp && fp != stdin)
- fclose(fp);
+ if (out != stdout && close_stream(out))
+ err(EXIT_FAILURE, _("write failed"));
+
+ if (in && in != stdin)
+ fclose(in);
return EXIT_SUCCESS;
}