Re: [ANN] nitro 0.5, a tiny but flexible init system and process supervisor
Leah Neukirchen <[email protected]> Sun, 19 Oct 2025 17:27:26 +0200
| Newsgroups | gmane.comp.sysutils.supervision.general |
|---|---|
| Message-ID | <[email protected]> |
--=-=-= Content-Type: text/plain Demi Marie Obenour <[email protected]> writes: > On 10/17/25 08:55, Leah Neukirchen wrote: >> Hello, >> >> I'm happy to announce a new release of "nitro", a new supervisor akin >> to daemontools/runit/perp/s6. >> >> These are the main applications nitro is designed for: >> - As init for a Linux or NetBSD machine for embedded, desktop or >> server purposes >> - As init for a Linux initramfs >> - As init for a Linux container (Docker/Podman/LXC/Kubernetes) >> - As unprivileged supervision daemon on generic POSIX systems >> >> nitro 0.5 is pretty much feature complete and now supports (relative >> to the the previous release announcement): >> - usage as NetBSD init >> - s6-compatible readiness notification >> - event-driven nitroctl which can perform multiple actions in parallel >> - improved usability and robustness >> - new test suite, which passes on new and old Linux, FreeBSD, NetBSD, OpenBSD > > Do you plan to support cgroups on Linux? Those allow for much > more robust supervision, including of daemons that do nasty things > like double-fork. It's also necessary to be able to terminate child > daemons reliably without the use of PID namespaces. I considered it, but I don't think support in PID 1 itself is necessary (for the double fork perhaps, but most software doesn't double fork anymore and it hurts supervision anyway). Basically, you can do everything in scripts, say: /etc/nitro/sshd/run: #!/bin/sh mkdir -p /sys/fs/cgroup/nitro/sshd echo "+memory +cpu +io" > /sys/fs/cgroup/cgroup.subtree_control echo "+memory +cpu +io" > /sys/fs/cgroup/nitro/cgroup.subtree_control echo $$ >/sys/fs/cgroup/nitro/sshd/cgroup.procs exec /usr/bin/sshd -D (you can configure limits here of course) /etc/nitro/sshd/finish: #!/bin/sh echo 1 > /sys/fs/cgroup/nitro/sshd/cgroup.kill Unfortunately, cgroups only can be killed using SIGTERM, I wrote a small tool cgslay to do this a bit more gently (see attached). --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=cgslay.c /* cgslay - gently kill a cgroup * * Usage: cgslay [-s SIGNAL] [-t TIMEOUT] /sys/fs/cgroup/... * -s SIGNAL send signal (number), default 15 (SIGTERM) * -t TIMEOUT use cgroup.kill after TIMEOUT seconds */ #include <sys/poll.h> #include <sys/timerfd.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define fatal(...) do { fprintf(stderr, __VA_ARGS__); exit(2); } while (0) int eventsfd; struct pollfd fds[2]; int check_events(char *needle) { static char buf[512]; if (strstr(buf, needle)) return 1; poll(fds, sizeof fds / sizeof fds[0], -1); if (fds[0].revents & POLLIN) return -1; lseek(fds[1].fd, 0, SEEK_SET); ssize_t n = read(fds[1].fd, buf, sizeof buf - 1); if (n < 0) fatal("can't read from eventsfd: %s\n", strerror(errno)); buf[n+1] = 0; if (strstr(buf, needle)) return 1; return 0; } int main(int argc, char *argv[]) { int sig = SIGTERM; int timeout = -1; int opt; while ((opt = getopt(argc, argv, "s:t:")) != -1) switch (opt) { case 's': sig = atoi(optarg); break; case 't': timeout = atoi(optarg); break; default: goto usage; } if (argc != optind + 1) { usage: fatal("Usage: cgslay [-s SIGNAL] [-t TIMEOUT] /sys/fs/cgroup/...\n"); } char *cgroup = argv[optind]; int dirfd = open(cgroup, O_RDONLY | O_DIRECTORY); if (dirfd < 0) fatal("can't open cgroup directory %s: %s\n", cgroup, strerror(errno)); eventsfd = openat(dirfd, "cgroup.events", O_RDONLY); if (eventsfd < 0) fatal("can't open cgroup.events: %s\n", strerror(errno)); if (sig == SIGKILL) goto kill; if (timeout > 0) { int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); if (timerfd < 0) fatal("timerfd_create: %s\n", strerror(errno)); timerfd_settime(timerfd, 0, &(struct itimerspec) { .it_value = { .tv_sec = timeout } }, 0); fds[0].fd = timerfd; fds[0].events = POLLIN; } else { fds[0].fd = -1; } fds[1].fd = eventsfd; fds[1].events = POLLPRI; int r; r = check_events("populated 0\n"); if (r == 1) exit(0); int freezefd = openat(dirfd, "cgroup.freeze", O_WRONLY); if (freezefd < 0) fatal("can't open cgroup.freeze: %s\n", strerror(errno)); write(freezefd, "1", 1); while ((r = check_events("frozen 1\n")) == 0) ; if (r < 0) { write(freezefd, "0", 1); goto kill; } int procsfd = openat(dirfd, "cgroup.procs", O_RDONLY); if (procsfd < 0) { write(freezefd, "0", 1); fatal("can't open cgroup.procs: %s\n", strerror(errno)); } FILE *procsfile = fdopen(procsfd, "r"); if (!procsfile) { write(freezefd, "0", 1); fatal("can't fdopen %d: %s\n", procsfd, strerror(errno)); } long pid; while (fscanf(procsfile, "%ld%*[\n]", &pid) == 1) { kill(pid, sig); if (sig && sig != SIGCONT) kill(pid, SIGCONT); } write(freezefd, "0", 1); while ((r = check_events("frozen 0\n")) == 0) ; if (timeout < 0) exit(0); while ((r = check_events("populated 0\n")) == 0) ; if (r == 1) exit(0); kill: int killfd = openat(dirfd, "cgroup.kill", O_WRONLY); if (killfd < 0) fatal("can't open cgroup.kill: %s\n", strerror(errno)); write(killfd, "1", 1); exit(0); } --=-=-= Content-Type: text/plain hth, -- Leah Neukirchen <[email protected]> https://leahneukirchen.org --=-=-=--