rats

RAdix iniT Suite
git clone https://git.daat.foo/repos/rats.git
Log | Files | Refs | README | LICENSE

commit a3e8dd750e830d79b09f52874c47c4a9f93cdf6a
parent 4a2b8fe4162e2efad7724fba0f5c353abf4c54b5
Author: cowmonk <cowmonk@based.pt>
Date:   Tue,  6 Oct 2026 12:17:36 -0700

replace _GNU_SOURCE with _POSIX_C_SOURCE and replace usleep() with nanosleep()

Other notable changes are:
  - signal_handler() to save and restore `errno` around its write to the self-pipe.
  - supervisor ignores `SIGPIPE`, checking that setting the disposition
    suceeds.
  - Restored the default `SIGPIPE` dispostion in both the service and
    logger child before exec.

among many others to improve portability and quality of the supervisor

Diffstat:
Mserva.c | 157+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 90 insertions(+), 67 deletions(-)

diff --git a/serva.c b/serva.c @@ -1,7 +1,7 @@ /* serva: a simple service supervisor */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* needed for PATH_MAX */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 #endif #include "util.h" @@ -64,16 +64,37 @@ static size_t nsvcs; static int sigpipe[2]; static volatile sig_atomic_t got_term; +static void +cloexec(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFD); + if (flags < 0 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) + eprintf("fcntl FD_CLOEXEC:"); +} + +static void +sleep_briefly(void) +{ + struct timespec delay = { 0, 100000000L }; + + while (nanosleep(&delay, &delay) < 0 && errno == EINTR) + ; +} + /* signal handler: wake self-pipe on SIGCHLD, flag SIGTERM */ static void signal_handler(int sig) { char c; + int saved_errno = errno; if (sig == SIGTERM) got_term = 1; c = 0; write(sigpipe[1], &c, 1); + errno = saved_errno; } /* parse @-templates: "tty@tty1" -> arg "tty1" */ @@ -336,8 +357,12 @@ start_service(struct Service *s) /* create logging pipe if needed */ if (s->has_logger && s->pipefd[0] < 0) { - if (pipe(s->pipefd) < 0) + if (pipe(s->pipefd) < 0) { weprintf("pipe:"); + return; + } + cloexec(s->pipefd[0]); + cloexec(s->pipefd[1]); } s->last_start = time(NULL); @@ -355,6 +380,7 @@ start_service(struct Service *s) sigemptyset(&empty); sigprocmask(SIG_SETMASK, &empty, NULL); + signal(SIGPIPE, SIG_DFL); setsid(); if (chdir(s->dir) < 0) @@ -393,6 +419,7 @@ start_service(struct Service *s) sigemptyset(&empty); sigprocmask(SIG_SETMASK, &empty, NULL); + signal(SIGPIPE, SIG_DFL); if (chdir(s->dir) < 0) eprintf("chdir %s:", s->dir); @@ -432,7 +459,7 @@ stop_service(struct Service *s) break; if (i == 40) kill(s->pid, SIGKILL); - usleep(100000); + sleep_briefly(); } s->pid = 0; } @@ -459,7 +486,7 @@ stop_for_dep(struct Service *s) break; if (i == 40) kill(s->pid, SIGKILL); - usleep(100000); + sleep_briefly(); } s->pid = 0; } @@ -642,52 +669,50 @@ status_str(struct Service *s) return "DOWN"; } -/* format_status: append one status line for s to resp */ -static size_t -format_status(struct Service *s, char *resp, size_t size, size_t off) +/* format_status: write one service's status to the control stream */ +static void +format_status(struct Service *s, FILE *out) { char full[2 * NAME_MAX + 2]; int j; snprintf(full, sizeof(full), "%s/%s", s->stage, s->name); - off += snprintf(resp + off, size - off, - "%-32s %s%s pid=%d runs=%lu uptime=%lds", + fprintf(out, + "%-32s %s%s pid=%ld runs=%lu uptime=%lds", full, status_str(s), s->noreset ? " (noreset)" : "", - s->pid, + (long)s->pid, s->nruns, s->pid > 0 ? (long)(time(NULL) - s->last_start) : 0L); if (s->last_exit >= 0) - off += snprintf(resp + off, size - off, - " exit=%d", s->last_exit); + fprintf(out, " exit=%d", s->last_exit); if (s->last_sig > 0) - off += snprintf(resp + off, size - off, - " sig=%d", s->last_sig); + fprintf(out, " sig=%d", s->last_sig); if (s->nneed > 0) { - off += snprintf(resp + off, size - off, " need="); + fprintf(out, " need="); for (j = 0; j < s->nneed; j++) - off += snprintf(resp + off, size - off, "%s%s", + fprintf(out, "%s%s", j ? "," : "", s->need[j]); } if (s->nafter > 0) { - off += snprintf(resp + off, size - off, " after="); + fprintf(out, " after="); for (j = 0; j < s->nafter; j++) - off += snprintf(resp + off, size - off, "%s%s", + fprintf(out, "%s%s", j ? "," : "", s->after[j]); } - off += snprintf(resp + off, size - off, "\n"); - return off; + fputc('\n', out); } /* handle_conn: process one control connection */ static void handle_conn(int conn) { - char buf[4096], resp[8192], cmd, *name, *p; + char buf[4096], cmd, *name, *p; ssize_t n; - size_t i, off; + size_t i; struct Service *s; + FILE *out; n = read(conn, buf, sizeof(buf) - 1); if (n <= 0) { @@ -695,6 +720,12 @@ handle_conn(int conn) return; } buf[n] = '\0'; + out = fdopen(conn, "w"); + if (!out) { + weprintf("fdopen:"); + close(conn); + return; + } p = strchr(buf, '\n'); if (p) @@ -712,13 +743,11 @@ handle_conn(int conn) svcs[i].want_up = 1; } check_restarts(); - off = snprintf(resp, sizeof(resp), - "OK: started all\n"); + fprintf(out, "OK: started all\n"); } else { s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } /* explicit svc -u overrides down and deps */ @@ -727,92 +756,73 @@ handle_conn(int conn) s->next_restart = 0; s->backoff = 0; start_service(s); - off = snprintf(resp, sizeof(resp), - "OK: %s up\n", name); + fprintf(out, "OK: %s up\n", name); } break; case 'd': if (strcmp(name, "a") == 0) { for (i = 0; i < nsvcs; i++) stop_service(&svcs[i]); - off = snprintf(resp, sizeof(resp), - "OK: stopped all\n"); + fprintf(out, "OK: stopped all\n"); } else { s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } stop_service(s); - off = snprintf(resp, sizeof(resp), - "OK: %s down\n", name); + fprintf(out, "OK: %s down\n", name); } stop_cascade(); break; case 'r': s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } restart_service(s); - off = snprintf(resp, sizeof(resp), - "OK: %s restarted\n", name); + fprintf(out, "OK: %s restarted\n", name); break; case 'k': s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } if (s->pid > 0) kill(s->pid, SIGKILL); - off = snprintf(resp, sizeof(resp), - "OK: %s killed\n", name); + fprintf(out, "OK: %s killed\n", name); break; case 't': s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } if (s->pid > 0) kill(s->pid, SIGTERM); - off = snprintf(resp, sizeof(resp), - "OK: %s terminated\n", name); + fprintf(out, "OK: %s terminated\n", name); break; case 's': - off = 0; if (name[0]) { s = find_service(name); if (!s) { - off = snprintf(resp, sizeof(resp), - "ERR: no such service: %s\n", name); + fprintf(out, "ERR: no such service: %s\n", name); break; } - off = format_status(s, resp, sizeof(resp), 0); + format_status(s, out); break; } - for (i = 0; i < nsvcs; i++) { - s = &svcs[i]; - if (off + 128 >= sizeof(resp)) - break; - off = format_status(s, resp, sizeof(resp), off); - } + for (i = 0; i < nsvcs && !ferror(out); i++) + format_status(&svcs[i], out); break; default: - off = snprintf(resp, sizeof(resp), - "ERR: unknown command\n"); + fprintf(out, "ERR: unknown command\n"); break; } - writeall(conn, resp, off); - shutdown(conn, SHUT_WR); - close(conn); + fshut(out, SOCK_PATH); } static void @@ -832,14 +842,20 @@ main(void) struct sockaddr_un addr; struct pollfd pfds[2]; struct sigaction sa; + mode_t mask; char c; size_t i; /* self-pipe for signal integration with poll */ if (pipe(sigpipe) < 0) eprintf("pipe:"); - fcntl(sigpipe[0], F_SETFL, O_NONBLOCK); - fcntl(sigpipe[1], F_SETFL, O_NONBLOCK); + cloexec(sigpipe[0]); + cloexec(sigpipe[1]); + if (fcntl(sigpipe[0], F_SETFL, O_NONBLOCK) < 0 || + fcntl(sigpipe[1], F_SETFL, O_NONBLOCK) < 0) + eprintf("fcntl O_NONBLOCK:"); + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + eprintf("signal SIGPIPE:"); /* install signal handlers */ memset(&sa, 0, sizeof(sa)); @@ -855,15 +871,20 @@ main(void) sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) eprintf("socket:"); + cloexec(sock); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strlcpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path)); + if (strlcpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) + eprintf("socket path too long: %s\n", SOCK_PATH); unlink(SOCK_PATH); + mask = umask(0077); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) eprintf("bind %s:", SOCK_PATH); + umask(mask); + if (chmod(SOCK_PATH, 0600) < 0) + eprintf("chmod %s:", SOCK_PATH); if (listen(sock, 16) < 0) eprintf("listen:"); - chmod(SOCK_PATH, 0600); /* mark all non-down services as wanting to be up */ for (i = 0; i < nsvcs; i++) { @@ -901,8 +922,10 @@ main(void) if (pfds[0].revents & POLLIN) { conn = accept(sock, NULL, NULL); - if (conn >= 0) + if (conn >= 0) { + cloexec(conn); handle_conn(conn); + } } if (pfds[1].revents & POLLIN) {