[PATCH] tailf: inotify based reimplementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch:
 - clean up tailf(1) code
 - remove stupid "for() { malloc() }" array allocation in the tailf() function
 - add inotify(7) support

Signed-off-by: Karel Zak <kzak@xxxxxxxxxx>
---
 configure.ac       |    1 +
 text-utils/tailf.1 |    4 +-
 text-utils/tailf.c |  240 ++++++++++++++++++++++++++++++++++------------------
 3 files changed, 161 insertions(+), 84 deletions(-)

diff --git a/configure.ac b/configure.ac
index e38c501..ca1c525 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,7 @@ AC_CHECK_FUNCS(
 	personality \
 	updwtmp \
 	lchown \
+	inotify_init \
 	rpmatch])
 AC_FUNC_FSEEKO
 
diff --git a/text-utils/tailf.1 b/text-utils/tailf.1
index 8bc53fc..294d9d6 100644
--- a/text-utils/tailf.1
+++ b/text-utils/tailf.1
@@ -49,9 +49,11 @@ output the last
 .I N
 lines, instead of the last 10.
 .SH AUTHOR
-This program was written by Rik Faith (faith@xxxxxxx) and may be freely
+This program was originally written by Rik Faith (faith@xxxxxxx) and may be freely
 distributed under the terms of the X11/MIT License.  There is ABSOLUTELY
 NO WARRANTY for this program.
+
+The latest inotify based implementation was written by Karel Zak (kzak@xxxxxxxxxx).
 .SH "SEE ALSO"
 .BR tail "(1), " less "(1)"
 .SH AVAILABILITY
diff --git a/text-utils/tailf.c b/text-utils/tailf.c
index f4616cc..8898574 100644
--- a/text-utils/tailf.c
+++ b/text-utils/tailf.c
@@ -19,7 +19,7 @@
  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
- * 
+ *
  * less -F and tail -f cause a disk access every five seconds.  This
  * program avoids this problem by waiting for the file size to change.
  * Hence, the file is not accessed, and the access time does not need to be
@@ -31,111 +31,185 @@
 #include <unistd.h>
 #include <malloc.h>
 #include <string.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <ctype.h>
+#include <errno.h>
 #include <err.h>
+#include <sys/inotify.h>
 #include "nls.h"
 
 #define DEFAULT_LINES  10
 
-static size_t filesize(const char *filename)
+static void
+tailf(const char *filename, int lines)
 {
-    struct stat sb;
+	char *buf, *p;
+	int  head = 0;
+	int  tail = 0;
+	FILE *str;
+	int  i;
+
+	if (!(str = fopen(filename, "r")))
+		err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename);
+
+	buf = malloc(lines * BUFSIZ);
+	p = buf;
+	while(fgets(p, BUFSIZ, str)) {
+		if (++tail >= lines) {
+			tail = 0;
+			head = 1;
+		}
+		p = buf + (tail * BUFSIZ);
+	}
+
+	if (head) {
+		for (i = tail; i < lines; i++)
+			fputs(buf + (i * BUFSIZ), stdout);
+		for (i = 0; i < tail; i++)
+			fputs(buf + (i * BUFSIZ), stdout);
+	} else {
+		for (i = head; i < tail; i++)
+			fputs(buf + (i * BUFSIZ), stdout);
+	}
 
-    if (!stat(filename, &sb)) return sb.st_size;
-    return 0;
+	fflush(stdout);
+	free(buf);
+	fclose(str);
 }
 
-static void tailf(const char *filename, int lines)
+static void
+roll_file(const char *filename, off_t *size)
 {
-    char **buffer;
-    int  head = 0;
-    int  tail = 0;
-    FILE *str;
-    int  i;
-
-    if (!(str = fopen(filename, "r")))
-	err(1, _("cannot open \"%s\" for read"), filename);
-
-    buffer = malloc(lines * sizeof(*buffer));
-    for (i = 0; i < lines; i++) buffer[i] = malloc(BUFSIZ + 1);
-
-    while (fgets(buffer[tail], BUFSIZ, str)) {
-	if (++tail >= lines) {
-	    tail = 0;
-	    head = 1;
+	char buf[BUFSIZ];
+	int fd;
+	struct stat st;
+
+	if (!(fd = open(filename, O_RDONLY)))
+		err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename);
+
+	if (fstat(fd, &st) == -1)
+		err(EXIT_FAILURE, _("cannot stat \"%s\""), filename);
+
+	if (st.st_size == *size) {
+		close(fd);
+		return;
 	}
-    }
 
-    if (head) {
-	for (i = tail; i < lines; i++) fputs(buffer[i], stdout);
-	for (i = 0; i < tail; i++)     fputs(buffer[i], stdout);
-    } else {
-	for (i = head; i < tail; i++)  fputs(buffer[i], stdout);
-    }
-    fflush(stdout);
+	if (!lseek(fd, *size, SEEK_SET) != (off_t)-1) {
+		ssize_t rc, wc;
 
-    for (i = 0; i < lines; i++) free(buffer[i]);
-    free(buffer);
+		while ((rc = read(fd, buf, sizeof(buf))) > 0) {
+			wc = write(STDOUT_FILENO, buf, rc);
+			if (rc != wc)
+				warnx(_("incomplete write to \"%s\" (written %ld, expected %ld)\n"),
+					filename, wc, rc);
+		}
+		fflush(stdout);
+	}
+	close(fd);
+	*size = st.st_size;
+}
 
-    fclose(str);
+static void
+watch_file(const char *filename, off_t *size)
+{
+	do {
+		roll_file(filename, size);
+		usleep(250000);
+	} while(1);
 }
 
-int main(int argc, char **argv)
+
+#ifdef HAVE_INOTIFY_INIT
+
+#define EVENTS		(IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
+#define NEVENTS		4
+
+static int
+watch_file_inotify(const char *filename, off_t *size)
 {
-    char       buffer[BUFSIZ];
-    size_t     osize, nsize;
-    FILE       *str;
-    const char *filename;
-    int        count, wcount;
-    int        lines = DEFAULT_LINES;
-
-    setlocale(LC_ALL, "");
-    bindtextdomain(PACKAGE, LOCALEDIR);
-    textdomain(PACKAGE);
-
-    argc--;
-    argv++;
-
-    for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
-	if (!strcmp(*argv, "-n") || !strcmp(*argv, "--lines")) {
-	    argc--;
-	    argv++;
-	    if (argc > 0 && (lines = atoi(argv[0])) <= 0)
-		errx(EXIT_FAILURE, _("invalid number of lines"));
+	char buf[ NEVENTS * sizeof(struct inotify_event) ];
+	int fd, ffd, e;
+	ssize_t len;
+
+	fd = inotify_init();
+	if (fd == -1)
+		return 0;
+
+	ffd = inotify_add_watch(fd, filename, EVENTS);
+	while (ffd >= 0) {
+		len = read(fd, buf, sizeof(buf));
+		if (len < 0 && (errno == EINTR || errno == EAGAIN))
+			continue;
+		if (len < 0)
+			err(EXIT_FAILURE, "%s: cannot read inotify events", filename);
+
+		for (e = 0; e < len; ) {
+			struct inotify_event *ev = (struct inotify_event *) &buf[e];
+
+			if (ev->mask & IN_MODIFY)
+				roll_file(filename, size);
+			else {
+				close(ffd);
+				ffd = -1;
+				break;
+			}
+			e += sizeof(struct inotify_event) + ev->len;
+		}
 	}
-	else if (isdigit(argv[0][1])) {
-	    if ((lines = atoi(*argv + 1)) <= 0)
-		errx(EXIT_FAILURE, _("invalid number of lines"));
+	close(fd);
+	return 1;
+}
+
+#endif /* HAVE_INOTIFY_INIT */
+
+int main(int argc, char **argv)
+{
+	const char *filename;
+	int lines = DEFAULT_LINES;
+	struct stat st;
+	off_t size = 0;
+
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+	argc--;
+	argv++;
+
+	for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
+		if (!strcmp(*argv, "-n") || !strcmp(*argv, "--lines")) {
+			argc--;	argv++;
+			if (argc > 0 && (lines = atoi(argv[0])) <= 0)
+				errx(EXIT_FAILURE, _("invalid number of lines"));
+		}
+		else if (isdigit(argv[0][1])) {
+			if ((lines = atoi(*argv + 1)) <= 0)
+				errx(EXIT_FAILURE, _("invalid number of lines"));
+		}
+		else
+			errx(EXIT_FAILURE, _("invalid option"));
 	}
-	else
-		errx(EXIT_FAILURE, _("invalid option"));
-    }
 
-    if (argc != 1)
-	errx(EXIT_FAILURE, _("usage: tailf [-n N | -N] logfile"));
+	if (argc != 1)
+		errx(EXIT_FAILURE, _("usage: tailf [-n N | -N] logfile"));
 
-    filename = argv[0];
-    tailf(filename, lines);
+	filename = argv[0];
 
-    for (osize = filesize(filename);;) {
-	nsize = filesize(filename);
-	if (nsize != osize) {
-	    if (!(str = fopen(filename, "r")))
-		err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename);
+	if (stat(filename, &st) != 0)
+		err(EXIT_FAILURE, _("cannot stat \"%s\""), filename);
 
-	    if (!fseek(str, osize, SEEK_SET))
-                while ((count = fread(buffer, 1, sizeof(buffer), str)) > 0) {
-                    wcount = fwrite(buffer, 1, count, stdout);
-                    if (wcount != count)
-			warnx(_("incomplete write to \"%s\" (written %d, expected %d)\n"),
-				filename, wcount, count);
-		}
-	    fflush(stdout);
-	    fclose(str);
-	    osize = nsize;
-	}
-	usleep(250000);		/* 250mS */
-    }
-    return EXIT_SUCCESS;
+	size = st.st_size;;
+	tailf(filename, lines);
+
+#ifdef HAVE_INOTIFY_INIT
+	if (!watch_file_inotify(filename, &size))
+#endif
+		watch_file(filename, &size);
+
+	return EXIT_SUCCESS;
 }
+
-- 
1.5.3.1

-
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux