From 0111dc21783dfde1d367c6b938f1366735ff8cef Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 30 May 2026 16:14:12 +0200 Subject: [PATCH] keventd: convert main loop to libuev Replace the raw poll() + global running/reload_rules flag dance with libuev. All four signals (SIGUSR1, SIGTERM, SIGHUP, SIGCHLD) are now handled via uev_signal_init() -- libuev uses signalfd internally so the handlers run in normal main-loop context, not async signal context. The SIGCHLD handler waitpid(-1, WNOHANG) reaps any subprocess (modprobe loads, rule RUN+= helpers) instead of the old SIG_IGN auto-reap trick. Synchronous waitpid() in modprobe_load() and run_program() still works because signalfd queues the signal -- it only reaches sigchld_cb on the next main-loop iteration. The netlink receive loop becomes uevent_cb() registered via uev_io_init(), with the receive buffer passed via the watcher's arg slot (avoids file-static state). ENOBUFS and EINTR/EAGAIN are still tolerated, any other recv() error still panic()s. Signed-off-by: Joachim Wiberg --- keventd/Makefile.am | 2 + keventd/keventd.c | 163 +++++++++++++++++++++++--------------------- 2 files changed, 88 insertions(+), 77 deletions(-) diff --git a/keventd/Makefile.am b/keventd/Makefile.am index 0efbb4ad..46f2c70f 100644 --- a/keventd/Makefile.am +++ b/keventd/Makefile.am @@ -15,8 +15,10 @@ keventd_CFLAGS = -W -Wall -Wextra -std=gnu99 # handled via explicit checks where present. Matches systemd-udev policy. keventd_CFLAGS += -Wno-format-truncation keventd_CFLAGS += $(lite_CFLAGS) +keventd_CFLAGS += $(uev_CFLAGS) keventd_CFLAGS += $(blkid_CFLAGS) keventd_LDADD = $(lite_LIBS) +keventd_LDADD += $(uev_LIBS) keventd_LDADD += $(blkid_LIBS) # Curated udev rules derived from eudev 3.2.14 (GPL-2.0-or-later). diff --git a/keventd/keventd.c b/keventd/keventd.c index faf219b6..0868a1bc 100644 --- a/keventd/keventd.c +++ b/keventd/keventd.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -50,6 +49,7 @@ #include #include #include +#include #include #include @@ -60,6 +60,8 @@ # include #endif +#include + #include "keventd.h" #include "rules.h" #include "udevdb.h" @@ -76,14 +78,12 @@ static int num_ac_online; static int num_ac; -static int running = 1; static int level; static int logon; static int passive; /* power-supply only, no device management */ static struct rule_list rules = TAILQ_HEAD_INITIALIZER(rules); static char *rules_dir; /* extra rules dir from -r option */ -static volatile sig_atomic_t reload_rules; int debug; /* debug in other modules as well */ @@ -450,24 +450,79 @@ static void set_logging(int prio) level = prio; } -static void toggle_debug(int signo) +static void sigusr1_cb(uev_t *w, void *arg, int events) { - (void)signo; - + (void)w; (void)arg; (void)events; debug ^= 1; set_logging(debug ? LOG_DEBUG : LOG_NOTICE); } -static void shut_down(int signo) +static void sigterm_cb(uev_t *w, void *arg, int events) { - (void)signo; - running = 0; + (void)arg; (void)events; + uev_exit(w->ctx); } -static void sighup_handler(int signo) +static void sighup_cb(uev_t *w, void *arg, int events) { - (void)signo; - reload_rules = 1; + (void)w; (void)arg; (void)events; + rules_free(&rules); + rules_load_all(&rules, rules_dir); +} + +static void sigchld_cb(uev_t *w, void *arg, int events) +{ + (void)w; (void)arg; (void)events; + + /* Reap all the children. */ + while (waitpid(-1, NULL, WNOHANG) > 0) + ; +} + +static void uevent_cb(uev_t *w, void *arg, int events) +{ + char *uevent_buf = arg; + char rebc_buf[UEVENT_BUFFER_SIZE]; + int len; + + if (events & UEV_ERROR) { + warn("netlink watcher error, re-arming"); + uev_io_start(w); + return; + } + + len = recv(w->fd, uevent_buf, UEVENT_BUFFER_SIZE - 1, MSG_DONTWAIT); + if (len == -1) { + switch (errno) { + case EINTR: + case EAGAIN: + return; + case ENOBUFS: + warn("lost events, buffer overflow"); + return; + default: + panic("recv failed"); + } + } + uevent_buf[len] = 0; + + /* Skip libudev events (start with "libudev") */ + if (!strncmp(uevent_buf, "libudev", 7)) + return; + + /* + * Save raw buffer before handle_uevent() -- uevent_parse() + * modifies the buffer in-place (splits @ and = separators). + * Rebroadcast needs the original kernel format intact. + */ + if (rebc_fd != -1) + memcpy(rebc_buf, uevent_buf, len); + + handle_uevent(uevent_buf, len); + + /* Rebroadcast after processing so consumers see ready state. */ + if (rebc_fd != -1) + rebc_event(rebc_buf, len); } static int usage(int rc) @@ -504,12 +559,15 @@ static int usage(int rc) */ int main(int argc, char *argv[]) { + static char uevent_buf[UEVENT_BUFFER_SIZE]; unsigned int nlgroups = REBC_DEFAULT_NLGROUP; struct sockaddr_nl nls = { 0 }; - char buf[UEVENT_BUFFER_SIZE]; + uev_t sigusr1_w, sigterm_w, sighup_w, sigchld_watcher; + uev_t netlink_watcher; + uev_ctx_t ctx; int do_coldplug = 0; int foreground = 0; - struct pollfd pfd; + int nlfd; int c; /* Device nodes are created with explicit MODE= from rules or the @@ -559,10 +617,12 @@ int main(int argc, char *argv[]) set_logging(debug ? LOG_DEBUG : LOG_NOTICE); } - signal(SIGUSR1, toggle_debug); - signal(SIGTERM, shut_down); - signal(SIGHUP, sighup_handler); - signal(SIGCHLD, SIG_IGN); /* Don't wait for modprobe children */ + uev_init(&ctx); + + uev_signal_init(&ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1); + uev_signal_init(&ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM); + uev_signal_init(&ctx, &sighup_watcher, sighup_cb, NULL, SIGHUP); + uev_signal_init(&ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD); /* Initialize condition directories */ init_power_supply(); @@ -574,23 +634,24 @@ int main(int argc, char *argv[]) disable_uevent_helper(); /* Set up netlink socket for kernel uevents */ - pfd.events = POLLIN; - pfd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); - if (pfd.fd == -1) + nlfd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); + if (nlfd == -1) panic("failed creating netlink socket"); nls.nl_family = AF_NETLINK; nls.nl_pid = 0; nls.nl_groups = 1; /* Kernel uevents are on group 1 only */ - if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) + if (bind(nlfd, (void *)&nls, sizeof(struct sockaddr_nl))) panic("bind failed"); /* Increase receive buffer to reduce event loss */ { int rcvbuf = 1024 * 1024; - setsockopt(pfd.fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + setsockopt(nlfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); } + uev_io_init(&ctx, &netlink_watcher, uevent_cb, uevent_buf, nlfd, UEV_READ); + /* Initialize rebroadcast socket (default on, -G to disable). * Skip in passive mode -- the hotplug daemon rebroadcasts. */ if (nlgroups && !passive) @@ -607,63 +668,11 @@ int main(int argc, char *argv[]) pidfile(NULL); logit(LOG_NOTICE, "keventd v%s started, waiting for events...", KEVENTD_VERSION); - while (running) { - char rebc_buf[UEVENT_BUFFER_SIZE]; - int len; - - /* Reload rules if SIGHUP was received */ - if (reload_rules) { - reload_rules = 0; - rules_free(&rules); - rules_load_all(&rules, rules_dir); - } - - if (-1 == poll(&pfd, 1, -1)) { - if (errno == EINTR) - continue; - break; - } - - len = recv(pfd.fd, buf, sizeof(buf) - 1, MSG_DONTWAIT); - if (len == -1) { - switch (errno) { - case EINTR: - continue; - case ENOBUFS: - warn("lost events, buffer overflow"); - continue; - default: - panic("recv failed"); - continue; - } - } - buf[len] = 0; - - /* Skip libudev events (start with "libudev") */ - if (!strncmp(buf, "libudev", 7)) - continue; - - /* - * Save raw buffer before handle_uevent() -- uevent_parse() - * modifies the buffer in-place (splits @ and = separators). - * Rebroadcast needs the original kernel format intact. - */ - if (rebc_fd != -1) - memcpy(rebc_buf, buf, len); - - handle_uevent(buf, len); - - /* - * Rebroadcast after processing so that device nodes and - * symlinks exist by the time consumers receive the event. - */ - if (rebc_fd != -1) - rebc_event(rebc_buf, len); - } + uev_run(&ctx, 0); if (rebc_fd != -1) close(rebc_fd); - close(pfd.fd); + close(nlfd); rules_free(&rules); logit(LOG_NOTICE, "keventd shutting down");