diff --git a/.gitignore b/.gitignore index d09ccdd..4c98fb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o catsit +catsit-timer catsit-watch catsit.conf catsitd diff --git a/Makefile b/Makefile index 573c1f3..2f52b2f 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ RC_SCRIPT = ${UNAME}/catsitd -include config.mk -BINS = catsit-watch +BINS = catsit-timer catsit-watch SBINS = catsit catsitd MAN1 = ${BINS:=.1} MAN5 = catsit.conf.5 diff --git a/catsit-timer.1 b/catsit-timer.1 new file mode 100644 index 0000000..9ad8956 --- /dev/null +++ b/catsit-timer.1 @@ -0,0 +1,62 @@ +.Dd February 25, 2021 +.Dt CATSIT-TIMER 1 +.Os +. +.Sh NAME +.Nm catsit-timer +.Nd run command at interval +. +.Sh SYNOPSIS +.Nm +.Ar interval +.Ar command ... +. +.Sh DESCRIPTION +The +.Nm +utility runs a command +at an interval. +The command is run once immediately, +then again every +.Ar interval +after waiting for the command to exit +each time. +The +.Nm +utility is not appropriate +for scheduling commands +to run at specific times. +. +.Pp +The format of the +.Ar interval +specifier is a series of integers +followed by units: +.Cm s +for seconds, +.Cm m +for minutes +and +.Cm h +for hours. +An integer with no unit +is assumed to be in seconds. +The +.Ar interval +is the sum of each +integer-unit pair. +For example, +.Cm 1m30s +is equivalent to +.Cm 90s . +. +.Sh EXIT STATUS +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 diff --git a/catsit-timer.c b/catsit-timer.c new file mode 100644 index 0000000..2869311 --- /dev/null +++ b/catsit-timer.c @@ -0,0 +1,70 @@ +/* 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 + +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[]) { + if (argc < 2) errx(EX_USAGE, "interval required"); + if (argc < 3) errx(EX_USAGE, "command required"); + + unsigned interval = 0; + char *spec = argv[1]; + while (*spec) { + unsigned num = strtoul(spec, &spec, 10); + switch (*spec) { + break; case '\0': interval += num; + break; case 's': spec++; interval += num; + break; case 'm': spec++; interval += 60 * num; + break; case 'h': spec++; interval += 60 * 60 * num; + break; default: errx(EX_USAGE, "invalid interval unit %c", *spec); + } + } + if (!interval) errx(EX_USAGE, "invalid zero interval"); + +#ifdef __OpenBSD__ + int error = pledge("stdio proc exec", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + for (;;) { + run(&argv[2]); + sleep(interval); + } +} diff --git a/catsit.conf.5 b/catsit.conf.5 index 7469e74..ccac76d 100644 --- a/catsit.conf.5 +++ b/catsit.conf.5 @@ -80,6 +80,7 @@ pounce/tilde pounce ${0#*/}.conf .Ed . .Sh SEE ALSO +.Xr catsit-timer 1 , .Xr catsit-watch 1 , .Xr catsitd 8 .