diff options
| author | Laurent Bercot <ska-skaware@skarnet.org> | 2025-10-20 23:59:19 +0000 |
|---|---|---|
| committer | Laurent Bercot <ska-skaware@skarnet.org> | 2025-10-20 23:59:19 +0000 |
| commit | a384be48fd6039ed4ecd48ba98008ecf5c8505a7 (patch) | |
| tree | 8ae143117e61ef4a43639848217593e727250319 | |
| parent | 6ae8c05d11dd822dd56b70c38c79c1b55ac5c1ee (diff) | |
| download | s6-a384be48fd6039ed4ecd48ba98008ecf5c8505a7.tar.gz | |
Add s6-background-watch (untested)
| -rw-r--r-- | package/deps.mak | 3 | ||||
| -rw-r--r-- | package/modes | 1 | ||||
| -rw-r--r-- | package/targets.mak | 12 | ||||
| -rwxr-xr-x | s6-background-watch | bin | 0 -> 34056 bytes | |||
| -rw-r--r-- | src/supervision/deps-exe/s6-background-watch | 3 | ||||
| -rw-r--r-- | src/supervision/s6-background-watch.c | 298 |
6 files changed, 316 insertions, 1 deletions
diff --git a/package/deps.mak b/package/deps.mak index 0fee849..bad353e 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -124,6 +124,7 @@ src/pipe-tools/s6-ftrig-listen1.o src/pipe-tools/s6-ftrig-listen1.lo: src/pipe-t src/pipe-tools/s6-ftrig-notify.o src/pipe-tools/s6-ftrig-notify.lo: src/pipe-tools/s6-ftrig-notify.c src/include/s6/ftrigw.h src/pipe-tools/s6-ftrig-wait.o src/pipe-tools/s6-ftrig-wait.lo: src/pipe-tools/s6-ftrig-wait.c src/include/s6/ftrigr.h src/pipe-tools/s6-mkfifodir.o src/pipe-tools/s6-mkfifodir.lo: src/pipe-tools/s6-mkfifodir.c src/include/s6/ftrigw.h +src/supervision/s6-background-watch.o src/supervision/s6-background-watch.lo: src/supervision/s6-background-watch.c src/supervision/s6-notifyoncheck.o src/supervision/s6-notifyoncheck.lo: src/supervision/s6-notifyoncheck.c src/include/s6/s6.h src/supervision/s6-permafailon.o src/supervision/s6-permafailon.lo: src/supervision/s6-permafailon.c src/include/s6/supervise.h src/supervision/s6-supervise.o src/supervision/s6-supervise.lo: src/supervision/s6-supervise.c src/include/s6/config.h src/include/s6/ftrigw.h src/include/s6/supervise.h @@ -262,6 +263,8 @@ s6-ftrig-wait: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-ftrig-wait: src/pipe-tools/s6-ftrig-wait.o ${LIBS6} -lskarnet s6-mkfifodir: EXTRA_LIBS := s6-mkfifodir: src/pipe-tools/s6-mkfifodir.o ${LIBS6} -lskarnet +s6-background-watch: EXTRA_LIBS := ${SPAWN_LIB} ${KEVENTPTHREAD_LIB} +s6-background-watch: src/supervision/s6-background-watch.o -lskarnet s6-notifyoncheck: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB} ${SPAWN_LIB} s6-notifyoncheck: src/supervision/s6-notifyoncheck.o ${LIBS6} -lskarnet s6-permafailon: EXTRA_LIBS := ${SYSCLOCK_LIB} diff --git a/package/modes b/package/modes index 837f0a2..dc5b2c3 100644 --- a/package/modes +++ b/package/modes @@ -19,6 +19,7 @@ s6-svdt-clear 0755 s6-svwait 0755 s6-svlisten1 0755 s6-svlisten 0755 +s6-background-watch 0755 s6-svperms 0755 s6-notifyoncheck 0755 s6-permafailon 0755 diff --git a/package/targets.mak b/package/targets.mak index dbe0b68..30900d6 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -60,11 +60,21 @@ s6-instance-create \ s6-instance-delete \ s6-instance-control \ s6-instance-status \ -s6-instance-list +s6-instance-list \ + +LIBEXEC_TARGETS := \ +s6-background-watch \ LIB_DEFS := S6=s6 S6_DESCRIPTION := +$(shell grep -qFx "kevent: yes" $(sysdeps)/sysdeps) +ifeq ($(.SHELLSTATUS),0) +KEVENTPTHREAD_LIB := $(PTHREAD_LIB) +else +KEVENTPTHREAD_LIB := +endif + ifneq ($(EXECLINE_LIB),) LIB_DEFS += S6AUTO=s6auto S6AUTO_DESCRIPTION := The s6auto library (C helpers to create service directories) diff --git a/s6-background-watch b/s6-background-watch Binary files differnew file mode 100755 index 0000000..4fa40e2 --- /dev/null +++ b/s6-background-watch diff --git a/src/supervision/deps-exe/s6-background-watch b/src/supervision/deps-exe/s6-background-watch new file mode 100644 index 0000000..31dec83 --- /dev/null +++ b/src/supervision/deps-exe/s6-background-watch @@ -0,0 +1,3 @@ +-lskarnet +${SPAWN_LIB} +${KEVENTPTHREAD_LIB} diff --git a/src/supervision/s6-background-watch.c b/src/supervision/s6-background-watch.c new file mode 100644 index 0000000..cfb1fb7 --- /dev/null +++ b/src/supervision/s6-background-watch.c @@ -0,0 +1,298 @@ +/* ISC license. */ + +#include <skalibs/sysdeps.h> + +#if defined(SKALIBS_HASPRSETCHILDSUBREAPER) || defined(SKALIBS_HASKEVENT) + +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> +#include <errno.h> +#ifdef SKALIBS_HASPRSETCHILDSUBREAPER +# include <sys/prctl.h> +#else +# include <sys/types.h> +# include <sys/event.h> +# include <sys/time.h> +# include <pthread.h> +# include <fcntl.h> +# include <limits.h> +#endif + +#include <skalibs/posixplz.h> +#include <skalibs/types.h> +#include <skalibs/prog.h> +#include <skalibs/gol.h> +#include <skalibs/bytestr.h> +#include <skalibs/strerr.h> +#include <skalibs/tai.h> +#include <skalibs/selfpipe.h> +#include <skalibs/iopause.h> +#include <skalibs/cspawn.h> +#include <skalibs/djbunix.h> + + + /* portable parts */ + +#define USAGE "s6-background-watch [ -t timeout ] [ -d notif ] pidfile prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int handle_signals_early (pid_t pid, char const *pidfile, char const *prog) +{ + int sig ; + for (;;) switch (sig = selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return 0 ; + case SIGCHLD : + { + int wstat ; + pid_t r = wait_pid_nohang(pid, &wstat) ; + if (r == -1) strerr_diefu1sys(111, "wait") ; + if (!r) break ; + if (WIFEXITED(wstat)) + { + if (WEXITSTATUS(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ; + unlink_void(pidfile) ; + strerr_dief3x(wait_estatus(wstat), prog, " (parent) exited ", fmt) ; + } + else return 1 ; + } + else + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + unlink_void(pidfile) ; + strerr_dief3x(wait_estatus(wstat), prog, " (parent) crashed with signal ", fmt) ; + } + break ; + } + default : + kill(pid, sig) ; + break ; + } +} + +static inline int read_file (char const *file, char *buf, size_t n) +{ + ssize_t r = openreadnclose_nb(file, buf, n) ; + if (r == -1) + { + if (errno != ENOENT) strerr_diefu2sys(111, "open ", file) ; + return 0 ; + } + buf[byte_chr(buf, r, '\n')] = 0 ; + return 1 ; +} + +static inline pid_t get_pid_from_pidfile (char const *pidfile, char const *prog) +{ /* for now, assume the pidfile is there once the parent has exited */ + pid_t pid ; + char buf[128] ; + if (!read_file(pidfile, buf, 128)) + strerr_diefu4sys(104, prog, " (parent) exited but pidfile ", pidfile, " does not exist") ; + if (!pid0_scan(buf, &pid)) + strerr_dief3x(104, "pidfile ", pidfile, " does not contain a valid pid") ; + return pid ; +} + +static inline int handle_signals (pid_t pid, int issr, int *code) +{ + int sig ; + for (;;) switch (sig = selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return 0 ; + case SIGCHLD : + { + int wstat ; + pid_t r = wait_pid_nohang(pid, &wstat) ; + if (r == -1) + if (errno != ECHILD || issr) strerr_diefu1sys(111, "wait") ; + else break ; + else if (!r) break ; + *code = wait_estatus(wstat) ; + return 1 ; + } + default : + kill(pid, sig) ; + break ; + } +} + + +/* BSD part. kevent is a unique snowflake in the shape of a dildo */ + +#ifdef SKALIBS_HASKEVENT + +struct thinfo_s +{ + int kq ; + int fdw ; + pid_t pid ; +} ; + +static void *keventwait (void *arg) +{ + struct thinfo_s *t = arg ; + struct kevent ke ; + for (;;) + { + int r = kevent(t->kq, 0, 0, &ke, 1, 0) ; + if (r == -1 && errno != EINTR) strerr_diefu2sys(111, "read ", "kevent") ; + else if (r && (pid_t)ke.ident == t->pid && ke.filter == EVFILT_PROC && ke.fflags & NOTE_EXIT) break ; + } + close(t->fdw) ; + return (void *)(intptr_t)ke.data ; +} + +static void keventstart (pid_t pid, int *fd, pthread_t *th) +{ + struct thinfo_s t ; + int p[2] ; + pthread_attr_t attr ; + struct kevent ke ; + int e ; + + t.kq = kqueue() ; + if (t.kq == -1) strerr_diefu1sys(111, "kqueue") ; + EV_SET(&ke, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0) ; + if (kevent(t.kq, &ke, 1, 0, 0, 0) == -1) strerr_diefu1sys(111, "register kevent") ; + if (pipe(p) == -1) strerr_diefu2sys(111, "pipe") ; + if (coe(p[0]) + t.fdw = p[1] ; + t.pid = pid ; + e = pthread_attr_init(&attr) ; + if (e) { errno = e ; strerr_diefu1sys(111, "pthread_attr_init") ; } + pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) ; + e = pthread_create(th, &attr, &keventwait, &t) ; + if (e) { errno = e ; strerr_diefu1sys(111, "pthread_create") ; } + *fd = p[0] ; +} + +static int getexitcode (pthread_t th) +{ + void *ptr ; + int e = pthread_join(th, &ptr) ; + if (e) { errno = e ; strerr_diefu1sys(111, "pthread_join") ; } + return (int)(intptr_t)ptr ; +} + +#endif + + + /* main */ + +enum rgola_e +{ + GOLA_TIMEOUT, + GOLA_NOTIF, + GOLA_N +} ; + +static gol_arg const rgola[GOLA_N] = +{ + { .so = 't', .lo = "timeout-ready", .i = GOLA_TIMEOUT }, + { .so = 'd', .lo = "notification-fd", .i = GOLA_NOTIF } +} ; + +int main (int argc, char const *const *argv) +{ + iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .events = IOPAUSE_READ, .revents = 0 } } ; + tain tto = TAIN_INFINITE_RELATIVE ; + tain deadline ; + unsigned int notif = 0 ; + pid_t pid ; + + char const *wgola[GOLA_N] = { 0 } ; + unsigned int golc ; + PROG = "s6-background-watch" ; + golc = gol_main(argc, argv, 0, 0, rgola, GOLA_N, 0, wgola) ; + argc -= golc ; argv += golc ; + if (argc < 2) dieusage() ; + if (wgola[GOLA_TIMEOUT]) + { + unsigned int t ; + if (!uint0_scan(wgola[GOLA_TIMEOUT], &t)) dieusage() ; + if (!tain_from_millisecs(&tto, t)) dieusage() ; + } + if (wgola[GOLA_NOTIF]) + { + if (!uint0_scan(wgola[GOLA_NOTIF], ¬if)) dieusage() ; + if (notif < 3) strerr_dief2x(100, "notification-fd", " cannot be 0, 1 or 2") ; + if (fcntl(notif, F_GETFD) == -1) strerr_diefu2sys(111, "check ", "notification-fd") ; + if (coe(notif) == -1) strerr_diefu1sys(111, "coe notification-fd") ; + } + + { + sigset_t full ; + sigfillset(&full) ; + if (!selfpipe_trapset(&full)) + strerr_diefu1sys(111, "trap all signals") ; + x[0].fd = selfpipe_fd() ; + } + +#ifdef SKALIBS_HASPRSETCHILDSUBREAPER + if (prctl(PR_SET_CHILD_SUBREAPER, 1) == -1) + strerr_diefu1sys(111, "prctl to become a subreaper") ; + x[1].fd = -2 ; +#endif + + pid = cspawn(argv[1], argv + 1, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, 0, 0) ; + if (!pid) strerr_diefu2sys(errno == ENOEXEC ? 126 : 127, "spawn ", argv[1]) ; + + tain_now_set_stopwatch_g() ; + tain_add_g(&deadline, &tto) ; + for (;;) + { + int r = iopause_g(x, 1, &deadline) ; + if (r == -1) strerr_diefu1sys(111, "iopause") ; + if (!r) + { + strerr_warnw2x(argv[1], " (parent) did not exit before timeout-ready, killing it") ; + kill(pid, SIGKILL) ; + } + else if (handle_signals_early(pid, argv[0], argv[1])) break ; + } + + pid = get_pid_from_pidfile(argv[0], argv[1]) ; +#ifdef SKALIBS_HASKEVENT + pthread_t th ; + keventstart(pid, &x[1].fd, &th) ; +#endif + if (notif) + { + write(notif, "\n", 1) ; + close(notif) ; + } + + for (;;) + { + int r = iopause_g(x, 1 + (x[1].fd >= 0), 0) ; + if (r == -1) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_READ) + { + int code = 0 ; + if (handle_signals(pid, x[1].fd == -2, &code)) _exit(code) ; + } +#ifdef SKALIBS_KEVENT + else if (x[1].revents & IOPAUSE_READ) _exit(getexitcode(th)) ; +#endif + } +} + +#else + +#include <skalibs/prog.h> +#include <skalibs/strerr.h> + +int main (int argc, char const *const *argv) +{ + PROG = "s6-background-watch" ; + strerr_dief1x(112, "system does not meet the requirements to watch auto-backgrounding daemons") ; +} + +#endif |
