/* Copyright (C) 2021 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include static void watch(int kq, char *path) { int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) err(EX_TEMPFAIL, "%s", path); struct kevent event; EV_SET( &event, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE | NOTE_DELETE, 0, path ); int nevents = kevent(kq, &event, 1, NULL, 0, NULL); if (nevents < 0) err(EX_OSERR, "kevent"); } static void run(char *argv[]) { pid_t pid = fork(); if (pid < 0) err(EX_OSERR, "fork"); if (!pid) { execvp(argv[0], argv); err(126, "%s", argv[0]); } int status; pid = wait(&status); if (pid < 0) err(EX_OSERR, "wait"); if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) exit(status); } else { exit(status); } } int main(int argc, char *argv[]) { int kq = kqueue(); if (kq < 0) err(EX_OSERR, "kqueue"); fcntl(kq, F_SETFD, FD_CLOEXEC); int init = 0; int delay = 0; int append = 0; for (int opt; 0 < (opt = getopt(argc, argv, "ad:f:i"));) { switch (opt) { break; case 'a': append = 1; break; case 'd': delay = strtol(optarg, NULL, 10); break; case 'f': watch(kq, optarg); break; case 'i': init = 1; break; default: return EX_USAGE; } } argc -= optind; argv += optind; if (!argc) errx(EX_USAGE, "command required"); char **rest = argv; if (append) { rest = calloc(argc + 2, sizeof(*rest)); if (!rest) err(EX_OSERR, "calloc"); memcpy(rest, argv, sizeof(*argv) * argc); } #ifdef __OpenBSD__ int error = pledge("stdio proc exec", NULL); if (error) err(EX_OSERR, "pledge"); #endif if (init) run(rest); for (;;) { struct kevent event; int nevents = kevent(kq, NULL, 0, &event, 1, NULL); if (nevents < 0) err(EX_OSERR, "kevent"); if (delay) { struct kevent timer; EV_SET( &timer, 0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, delay, event.udata ); nevents = kevent(kq, &timer, 1, NULL, 0, NULL); if (nevents < 0) err(EX_OSERR, "kevent"); while ( event.filter != EVFILT_TIMER && !(event.fflags & NOTE_DELETE) ) { nevents = kevent(kq, NULL, 0, &event, 1, NULL); if (nevents < 0) err(EX_OSERR, "kevent"); } } if (event.filter == EVFILT_VNODE && event.fflags & NOTE_DELETE) { errx(EX_TEMPFAIL, "%s: file removed", (char *)event.udata); } if (append) rest[argc] = (char *)event.udata; run(rest); } }