summaryrefslogtreecommitdiffstats
path: root/login-utils/utmpdump.c
diff options
context:
space:
mode:
authorSami Kerola2012-07-10 10:59:58 +0200
committerKarel Zak2012-07-10 10:59:58 +0200
commit7346f2b61cacb00588e7c828656744193a91716c (patch)
treec7f4c4101e53adb2012823f1b14e9017abb7f8b8 /login-utils/utmpdump.c
parentutmpdump: fixes based on static analysis [cppcheck] (diff)
downloadkernel-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.c114
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)