diff options
author | Sami Kerola | 2012-07-10 10:59:58 +0200 |
---|---|---|
committer | Karel Zak | 2012-07-10 10:59:58 +0200 |
commit | 7346f2b61cacb00588e7c828656744193a91716c (patch) | |
tree | c7f4c4101e53adb2012823f1b14e9017abb7f8b8 /login-utils/utmpdump.c | |
parent | utmpdump: fixes based on static analysis [cppcheck] (diff) | |
download | kernel-qcow2-util-linux-7346f2b61cacb00588e7c828656744193a91716c.tar.gz kernel-qcow2-util-linux-7346f2b61cacb00588e7c828656744193a91716c.tar.xz kernel-qcow2-util-linux-7346f2b61cacb00588e7c828656744193a91716c.zip |
utmpdump: use inotify to when following file
Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
Diffstat (limited to 'login-utils/utmpdump.c')
-rw-r--r-- | login-utils/utmpdump.c | 114 |
1 files changed, 107 insertions, 7 deletions
diff --git a/login-utils/utmpdump.c b/login-utils/utmpdump.c index 6c0751d57..3f20f2dd3 100644 --- a/login-utils/utmpdump.c +++ b/login-utils/utmpdump.c @@ -35,6 +35,10 @@ #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/stat.h> +#ifdef HAVE_INOTIFY_INIT +#include <sys/inotify.h> +#endif #include "c.h" #include "nls.h" @@ -99,21 +103,117 @@ static void print_utline(struct utmp ut) addr_string, time_string); } -static void dump(FILE *fp, int forever) +#ifdef HAVE_INOTIFY_INIT +#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) { + FILE *fp; + struct stat st; struct utmp ut; + off_t pos; - if (forever) - fseek(fp, -10 * sizeof(ut), SEEK_END); + if (!(fp = fopen(filename, "r"))) + err(EXIT_FAILURE, _("%s: open failed"), filename); + + if (fstat(fileno(fp), &st) == -1) + err(EXIT_FAILURE, _("%s: stat failed"), filename); + + if (st.st_size == *size) { + fclose(fp); + return; + } - do { + if (fseek(fp, *size, SEEK_SET) != (off_t) -1) { while (fread(&ut, sizeof(ut), 1, fp) == 1) print_utline(ut); - if (forever) + } + + pos = ftello(fp); + /* 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; + + fclose(fp); +} + +static int follow_by_inotify(FILE *fp, const char *filename) +{ + char buf[NEVENTS * sizeof(struct inotify_event)]; + struct utmp ut; + int fd, wd, event; + ssize_t length; + off_t size; + + fd = inotify_init(); + if (fd == -1) + return -1; /* probably reached any limit ... */ + + size = ftello(fp); + fclose(fp); + + wd = inotify_add_watch(fd, filename, EVENTS); + if (wd == -1) + err(EXIT_FAILURE, _("%s: cannot add inotify watch."), filename); + + while (wd >= 0) { + errno = 0; + length = read(fd, buf, sizeof(buf)); + + if (length < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + if (length < 0) + err(EXIT_FAILURE, _("%s: cannot read inotify events"), + filename); + + for (event = 0; event < length;) { + struct inotify_event *ev = + (struct inotify_event *) &buf[event]; + + if (ev->mask & IN_MODIFY) + roll_file(filename, &size); + else { + close(wd); + wd = -1; + break; + } + event += sizeof(struct inotify_event) + ev->len; + } + } + + close(fd); + return 0; +} +#endif /* HAVE_INOTIFY_INIT */ + +static void dump(FILE *fp, const char *filename, int follow) +{ + struct utmp ut; + + if (follow) + fseek(fp, -10 * sizeof(ut), SEEK_END); + + while (fread(&ut, sizeof(ut), 1, fp) == 1) + print_utline(ut); + + if (!follow) + return; +#ifdef HAVE_INOTIFY_INIT + if (follow_by_inotify(fp, filename) != 0) +#endif + /* fallback for systems without inotify or with non-free + * inotify instances */ + for (;;) { + while (fread(&ut, sizeof(ut), 1, fp) == 1) + print_utline(ut); sleep(1); - } while (forever); + } } + /* This function won't work properly if there's a ']' or a ' ' in the real * token. Thankfully, this should never happen. */ static int gettok(char *line, char *dest, int size, int eatspace) @@ -248,7 +348,7 @@ int main(int argc, char **argv) undump(fp); } else { fprintf(stderr, _("Utmp dump of %s\n"), filename); - dump(fp, forever); + dump(fp, filename, forever); } if (fp != stdin) |