]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - login-utils/utmpdump.c
include/pidfd-utils: small cleanup
[thirdparty/util-linux.git] / login-utils / utmpdump.c
index d907e02074b1765e0c66bfaa3cad7e2d3e69ffa1..defa230b9a937299ceca7f41eb8bb9494896ac09 100644 (file)
@@ -28,7 +28,7 @@
 #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)
 {
@@ -66,13 +55,28 @@ 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))
@@ -83,23 +87,35 @@ static void xcleanse(char *s, int len)
                        *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);
 }
 
@@ -107,28 +123,28 @@ static void print_utline(struct utmp ut)
 #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.
@@ -136,10 +152,10 @@ static void roll_file(const char *filename, off_t *size)
        *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;
@@ -150,8 +166,11 @@ static int follow_by_inotify(FILE *fp, const char *filename)
        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)
@@ -172,7 +191,7 @@ static int follow_by_inotify(FILE *fp, const char *filename)
                                    (struct inotify_event *) &buf[event];
 
                        if (ev->mask & IN_MODIFY)
-                               roll_file(filename, &size);
+                               roll_file(filename, &size, out);
                        else {
                                close(wd);
                                wd = -1;
@@ -187,33 +206,33 @@ static int follow_by_inotify(FILE *fp, const char *filename)
 }
 #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;
 }
 
 
@@ -245,17 +264,15 @@ static int gettok(char *line, char *dest, int size, int eatspace)
        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);
@@ -265,57 +282,64 @@ static void undump(FILE *fp)
                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;
@@ -325,39 +349,50 @@ int main(int argc, char **argv)
                        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;
 }