#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 "c.h"
#include "nls.h"
-#include "timeutils.h"
#include "xalloc.h"
#include "closestream.h"
+#include "timeutils.h"
static time_t strtotime(const char *s_time)
{
return timegm(&tm);
}
-#if defined(_HAVE_UT_TV)
static suseconds_t strtousec(const char *s_time)
{
const char *s = strchr(s_time, ',');
- if (s)
- return (suseconds_t) atoi(s + 1);
+
+ if (s && *++s) {
+ suseconds_t us;
+ char *end = NULL;
+
+ errno = 0;
+ us = strtol(s, &end, 10);
+ if (errno == 0 && end && end > s)
+ return us;
+ }
return 0;
}
-#endif
#define cleanse(x) xcleanse(x, sizeof(x))
static void xcleanse(char *s, int len)
*s = '?';
}
-static void print_utline(struct utmp *ut, FILE *out)
+static void print_utline(struct utmpx *ut, FILE *out)
{
const char *addr_string;
char buffer[INET6_ADDRSTRLEN];
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;
+ tv.tv_usec = ut->ut_tv.tv_usec < (int32_t) USEC_PER_SEC ? ut->ut_tv.tv_usec : 0;
- if (strtimeval_iso(&tv,
- ISO_8601_DATE | ISO_8601_TIME | ISO_8601_COMMAUSEC |
- ISO_8601_TIMEZONE | ISO_8601_GMTIME, time_string,
+ if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_GT, time_string,
sizeof(time_string)) != 0)
return;
cleanse(ut->ut_id);
cleanse(ut->ut_line);
cleanse(ut->ut_host);
- /* pid id user line host addr time */
+ /* 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, UT_NAMESIZE, ut->ut_user,
- 12, UT_LINESIZE, ut->ut_line, 20, UT_HOSTSIZE, ut->ut_host,
+ 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);
}
{
FILE *in;
struct stat st;
- struct utmp ut;
+ struct utmpx ut;
off_t pos;
if (!(in = fopen(filename, "r")))
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)
err(EXIT_FAILURE, _("%s: cannot add inotify watch."), filename);
static FILE *dump(FILE *in, const char *filename, int follow, FILE *out)
{
- struct utmp ut;
+ struct utmpx ut;
if (follow)
ignore_result( fseek(in, -10 * sizeof(ut), SEEK_END) );
#ifdef HAVE_INOTIFY_INIT
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, in) == 1)
- print_utline(&ut, out);
- sleep(1);
- }
+ /* fallback for systems without inotify or with non-free
+ * inotify instances */
+ for (;;) {
+ while (fread(&ut, sizeof(ut), 1, in) == 1)
+ print_utline(&ut, out);
+ sleep(1);
+ }
return in;
}
static void undump(FILE *in, FILE *out)
{
- struct utmp ut;
- char s_addr[INET6_ADDRSTRLEN + 1], s_time[29], *linestart, *line;
- int count = 0;
+ struct utmpx ut;
+ char s_addr[INET6_ADDRSTRLEN + 1], s_time[29] = {}, *linestart, *line;
linestart = xmalloc(1024 * sizeof(*linestart));
s_time[28] = 0;
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);
+
+ if (sscanf(line, "[%hd] [%d] [%4c] ",
+ &ut.ut_type, &ut.ut_pid, ut.ut_id) != 3) {
+ warnx(_("parse error: %s"), line);
+ continue;
+ }
line += 19;
line += gettok(line, ut.ut_user, sizeof(ut.ut_user), 1);
inet_pton(AF_INET, s_addr, &(ut.ut_addr_v6));
else
inet_pton(AF_INET6, s_addr, &(ut.ut_addr_v6));
-#if defined(_HAVE_UT_TV)
+
ut.ut_tv.tv_sec = strtotime(s_time);
ut.ut_tv.tv_usec = strtousec(s_time);
-#else
- ut.ut_time = strtotime(s_time);
-#endif
- ignore_result( fwrite(&ut, sizeof(ut), 1, out) );
- ++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,
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);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_HELP_OPTIONS(22));
fprintf(out, USAGE_MAN_TAIL("utmpdump(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
const char *filename = NULL;
static const struct option longopts[] = {
- { "follow", 0, 0, 'f' },
- { "reverse", 0, 0, 'r' },
- { "output", required_argument, 0, 'o' },
- { "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, "fro:hV", longopts, NULL)) != -1) {
switch (c) {
break;
case 'h':
- usage(stdout);
- break;
+ 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 (follow && (out != stdout || !isatty(STDOUT_FILENO))) {
+ setvbuf(out, NULL, _IOLBF, 0);
+ }
+
if (optind < argc) {
filename = argv[optind];
in = fopen(filename, "r");
in = dump(in, filename, follow, out);
}
- if (out != stdout)
- if (close_stream(out))
- err(EXIT_FAILURE, _("write failed"));
+ if (out != stdout && close_stream(out))
+ err(EXIT_FAILURE, _("write failed"));
if (in && in != stdin)
fclose(in);