Add catsit-watch utility

This commit is contained in:
C. McEnroe 2021-02-25 15:42:24 -05:00
parent 8bd0b51140
commit fd25c666d5
5 changed files with 181 additions and 13 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
*.o *.o
catsit catsit
catsit-watch
catsit.conf catsit.conf
catsitd catsitd
config.mk config.mk

View File

@ -16,16 +16,18 @@ RC_SCRIPT = ${UNAME}/catsitd
-include config.mk -include config.mk
BINS = catsit catsitd BINS = catsit-watch
MAN8 = ${BINS:=.8} SBINS = catsit catsitd
MAN1 = ${BINS:=.1}
MAN5 = catsit.conf.5 MAN5 = catsit.conf.5
MAN8 = ${SBINS:=.8}
OBJS += daemon.o OBJS += daemon.o
OBJS += service.o OBJS += service.o
dev: tags all dev: tags all
all: ${BINS} all: ${BINS} ${SBINS}
catsitd: ${OBJS} catsitd: ${OBJS}
${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@
@ -38,22 +40,40 @@ ${OBJS}: daemon.h
sed -e 's|%%PREFIX%%|${PREFIX}|g' -e 's|%%RUNDIR%%|${RUNDIR}|g' $< > $@ sed -e 's|%%PREFIX%%|${PREFIX}|g' -e 's|%%RUNDIR%%|${RUNDIR}|g' $< > $@
chmod a+x $@ chmod a+x $@
tags: *.c *.h tags: *.[ch]
ctags -w *.c *.h ctags -w *.[ch]
clean: clean:
rm -f ${BINS} ${OBJS} ${RC_SCRIPT} tags rm -f ${BINS} ${SBINS} ${OBJS} ${RC_SCRIPT} tags
install: ${BINS} ${RC_SCRIPT} ${MAN5} ${MAN8} install: ${BINS} ${SBINS} ${RC_SCRIPT} ${MAN1} ${MAN5} ${MAN8}
install -d ${DESTDIR}${PREFIX}/sbin ${DESTDIR}${ETCDIR}/rc.d install -d ${DESTDIR}${PREFIX}/bin
install -d ${DESTDIR}${MANDIR}/man5 ${DESTDIR}${MANDIR}/man8 install -d ${DESTDIR}${PREFIX}/sbin
install ${BINS} ${DESTDIR}${PREFIX}/sbin install -d ${DESTDIR}${MANDIR}/man1
install -d ${DESTDIR}${MANDIR}/man5
install -d ${DESTDIR}${MANDIR}/man8
install -d ${DESTDIR}${ETCDIR}/rc.d
install ${BINS} ${DESTDIR}${PREFIX}/bin
install ${SBINS} ${DESTDIR}${PREFIX}/sbin
install ${RC_SCRIPT} ${DESTDIR}${ETCDIR}/rc.d install ${RC_SCRIPT} ${DESTDIR}${ETCDIR}/rc.d
install -m 644 ${MAN1} ${DESTDIR}${MANDIR}/man1
install -m 644 ${MAN5} ${DESTDIR}${MANDIR}/man5 install -m 644 ${MAN5} ${DESTDIR}${MANDIR}/man5
install -m 644 ${MAN8} ${DESTDIR}${MANDIR}/man8 install -m 644 ${MAN8} ${DESTDIR}${MANDIR}/man8
uninstall: uninstall:
rm -f ${BINS:%=${DESTDIR}${PREFIX}/sbin/%} .for BIN in ${BINS}
rm -f ${MAN5:%=${DESTDIR}${MANDIR}/man5/%} rm -f ${DESTDIR}${PREFIX}/bin/${BIN}
rm -f ${MAN8:%=${DESTDIR}${MANDIR}/man8/%} .endfor
.for SBIN in ${SBINS}
rm -f ${DESTDIR}${PREFIX}/sbin/${SBIN}
.endfor
rm -f ${DESTDIR}${ETCDIR}/rc.d/${RC_SCRIPT:T} rm -f ${DESTDIR}${ETCDIR}/rc.d/${RC_SCRIPT:T}
.for MAN in ${MAN1}
rm -f ${DESTDIR}${MANDIR}/man1/${MAN}
.endfor
.for MAN in ${MAN5}
rm -f ${DESTDIR}${MANDIR}/man5/${MAN}
.endfor
.for MAN in ${MAN8}
rm -f ${DESTDIR}${MANDIR}/man8/${MAN}
.endfor

52
catsit-watch.1 Normal file
View File

@ -0,0 +1,52 @@
.Dd February 25, 2021
.Dt CATSIT-WATCH 1
.Os
.
.Sh NAME
.Nm catsit-watch
.Nd run command when files are modified
.
.Sh SYNOPSIS
.Nm
.Op Fl i
.Op Fl f Ar file
.Ar command ...
.
.Sh DESCRIPTION
The
.Nm
utility runs a command
each time any of a set of files
are modified.
If any watched files are removed
or if the command exits non-zero,
.Nm
exits.
.
.Pp
The arguments are as follows:
.Bl -tag -width Ds
.It Fl f Ar file
Add
.Ar file
to the set of watched files.
.It Fl i
Run the command once initially,
before watching files.
.El
.
.Sh EXIT STATUS
If any watched files are removed,
.Nm
exits with
.Dv EX_TEMPFAIL
(75).
If the command exits non-zero,
.Nm
exits with the same status.
.
.Sh SEE ALSO
.Xr catsitd 8
.
.Sh AUTHORS
.An June Bug Aq Mt june@causal.agency

94
catsit-watch.c Normal file
View File

@ -0,0 +1,94 @@
/* Copyright (C) 2021 C. McEnroe <june@causal.agency>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sysexits.h>
#include <unistd.h>
static void watch(int kq, char *path) {
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) err(EX_NOINPUT, "%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;
for (int opt; 0 < (opt = getopt(argc, argv, "f:i"));) {
switch (opt) {
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");
#ifdef __OpenBSD__
int error = pledge("stdio proc exec", NULL);
if (error) err(EX_OSERR, "pledge");
#endif
if (init) run(argv);
for (;;) {
struct kevent event;
int nevents = kevent(kq, NULL, 0, &event, 1, NULL);
if (nevents < 0) err(EX_OSERR, "kevent");
if (event.fflags & NOTE_DELETE) {
errx(EX_TEMPFAIL, "%s: file removed", (char *)event.udata);
}
run(argv);
}
}

View File

@ -80,6 +80,7 @@ pounce/tilde pounce ${0#*/}.conf
.Ed .Ed
. .
.Sh SEE ALSO .Sh SEE ALSO
.Xr catsit-watch 1 ,
.Xr catsitd 8 .Xr catsitd 8
. .
.Sh AUTHORS .Sh AUTHORS