summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--text-utils/tailf.14
-rw-r--r--text-utils/tailf.c240
3 files changed, 161 insertions, 84 deletions
diff --git a/configure.ac b/configure.ac
index e38c5019d..ca1c525f4 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 8bc53fcae..294d9d6a9 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@acm.org) and may be freely
+This program was originally written by Rik Faith (faith@acm.org) 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@redhat.com).
.SH "SEE ALSO"
.BR tail "(1), " less "(1)"
.SH AVAILABILITY
diff --git a/text-utils/tailf.c b/text-utils/tailf.c
index f4616ccb8..e30e22f88 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;
}
+