/* * nqtail [FILES...] - follow output of nq jobs, quitting when they are done * * To the extent possible under law, Leah Neukirchen * has waived all copyright and related or neighboring rights to this work. * http://creativecommons.org/publicdomain/zero/1.0/ */ #include #include #include #include #include #include #include #include #include #include #include #define DELAY 250000 #ifdef __linux__ #define USE_INOTIFY #endif #if defined(__FreeBSD__) || defined(__APPLE__) #define USE_KEVENT #endif #ifdef USE_INOTIFY #include char ibuf[8192]; #endif #ifdef USE_KEVENT #include #endif char buf[8192]; static int islocked(int fd) { if (flock(fd, LOCK_SH | LOCK_NB) == -1) { return (errno == EWOULDBLOCK); } else { flock(fd, LOCK_UN); return 0; } } static int alphabetic(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); } int main(int argc, char *argv[]) { int i, fd, dirfd; off_t off, loff; ssize_t rd; int didsth = 0, seen_nl = 0; int opt = 0, aflag = 0, nflag = 0, qflag = 0; char *path; #ifdef USE_INOTIFY int ifd, wd; #endif #ifdef USE_KEVENT int kq, note; struct kevent kev; #endif close(0); while ((opt = getopt(argc, argv, "+anq")) != -1) { switch (opt) { case 'a': aflag = 1; break; case 'n': nflag = 1; break; case 'q': qflag = 1; break; default: fputs("usage: nqtail [-anq] [JOBID...]\n", stderr); exit(1); } } path = getenv("NQDIR"); if (!path) path = "."; #ifdef O_DIRECTORY dirfd = open(path, O_RDONLY | O_DIRECTORY); #else dirfd = open(path, O_RDONLY); #endif if (dirfd < 0) { perror("open dir"); exit(111); } if (optind == argc) { /* behave as if $NQDIR/,* was passed. */ DIR *dir; struct dirent *d; int len = 0; argc = 0; argv = 0; optind = 0; dir = fdopendir(dirfd); if (!dir) { perror("fdopendir"); exit(111); } while ((d = readdir(dir))) { if (d->d_name[0] != ',') continue; if (argc >= len) { len = 2*len + 1; argv = realloc(argv, len * sizeof (char *)); if (!argv) exit(222); } argv[argc] = strdup(d->d_name); if (!argv[argc]) exit(222); argc++; } qsort(argv, argc, sizeof (char *), alphabetic); } #ifdef USE_INOTIFY ifd = inotify_init(); if (ifd < 0) exit(111); #endif #ifdef USE_KEVENT kq = kqueue(); if (kq < 0) exit(111); #endif for (i = optind; i < argc; i++) { loff = 0; seen_nl = 0; fd = openat(dirfd, argv[i], O_RDONLY); if (fd < 0) continue; /* skip not running jobs, unless -a was passed, or we did not * output anything yet and are at the last argument. */ if (!aflag && !islocked(fd) && (didsth || i != argc - 1)) { close(fd); continue; } write(1, "==> ", 4); write(1, argv[i], strlen(argv[i])); write(1, qflag ? " " : "\n", 1); didsth = 1; #ifdef USE_INOTIFY char fullpath[PATH_MAX]; snprintf(fullpath, sizeof fullpath, "%s/%s", path, argv[i]); wd = inotify_add_watch(ifd, fullpath, IN_MODIFY | IN_CLOSE_WRITE); if (wd == -1) { perror("inotify_add_watch"); exit(111); } #endif #ifdef USE_KEVENT note = NOTE_WRITE; #ifdef __APPLE__ note |= NOTE_FUNLOCK; #endif #ifdef __FreeBSD__ note |= NOTE_CLOSE_WRITE; #endif EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, note, 0, NULL); if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0) { perror("kevent"); exit(111); } #endif while (1) { off = lseek(fd, 0, SEEK_END); if (off < loff) loff = off; /* file truncated */ if (off == loff) { if (nflag && islocked(fd)) break; if (flock(fd, LOCK_SH | LOCK_NB) == -1 && errno == EWOULDBLOCK) { #if defined(USE_INOTIFY) /* any inotify event is good */ read(ifd, ibuf, sizeof ibuf); #elif defined(USE_KEVENT) kevent(kq, NULL, 0, &kev, 1, NULL); #else /* poll for size change */ while (off == lseek(fd, 0, SEEK_END)) usleep(DELAY); #endif continue; } else { flock(fd, LOCK_UN); break; } } if (off - loff > sizeof buf) off = loff + sizeof buf; rd = pread(fd, &buf, off - loff, loff); if (qflag) { if (!seen_nl) { char *s; if ((s = memchr(buf, '\n', rd))) { write(1, buf, s+1-buf); seen_nl = 1; } else { write(1, buf, rd); } } } else { write(1, buf, rd); } loff += rd; } if (qflag && !seen_nl) write(1, "\n", 1); #ifdef USE_INOTIFY inotify_rm_watch(ifd, wd); #endif #ifdef USE_KEVENT EV_SET(&kev, fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); kevent(kq, &kev, 1, NULL, 0, NULL); #endif close(fd); } #ifdef USE_INOTIFY close(ifd); #endif #ifdef USE_KEVENT close(kq); #endif return 0; }