From nobody Thu Apr 10 14:59:37 2025 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ZYNJV0gm2z5t343; Thu, 10 Apr 2025 14:59:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ZYNJT4csMz3K58; Thu, 10 Apr 2025 14:59:37 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1744297177; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6yxjZqxXlSqRNKVqhDh4Seai6ojGhskOAy2wy4P7Oyw=; b=c4H+ToDe9yKhMrbzV/NTKU+n2fWMIK5N1L7+sB5usz3755/ZioB+8obGch0pJaknbLIBdS zMNGWViFTSyy//DdnymjXu4S9xH3qfFM8JrtEOpJZiK28HMI9SwFWLWWF2Ls2CknhbxKh9 NfdC25yRWnZS5izKzvd3qebCmTSwJVEeKJj82CeohNrlVCmzKIqzjYgpDfhjngiK4dhUrf DzXHOHPnhpGygYUOExtVzaj8AkozzXijyRYTPjNFBaLEJmqX7Rz0wTEF9NGdi+uHs0t9um iUXI2ezTTzuHTjLiqKu9uMcOAem9S3wtBiqM6RXLBzs6L7P6Zw2SlWaXnoXwxQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1744297177; a=rsa-sha256; cv=none; b=hWO9AoOB0SmlB33OWgofORSomlyb4FQtw8HJNs5+cfZ2ROyKk4ff1ZQKkUE129zzyTOitj CZ6Ap1WRVomGGbLBGtxDqlrkJWi3+eAb4gpfUF+J2ybyiq0V9NQuuxrKqwXJ8KYKyi4SzQ YWDIz0hGxEDBVxTNgftmca1ujrQUDksACt0s9bA/V2Mzr5vSNptBalPfN/vv/rVIIXmI7N 1piHqKJd5NY9nwWgeAS6ADqSkb6MxYHlnp8fg+z4cnVHQLHKl276fqndmXQkN+bZl9AEkk TY6q9FhggkY5L0ASpogltCO8Rf/jikwroF/6kP1enviJsuAyyJdDo3oRlWMOMw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1744297177; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6yxjZqxXlSqRNKVqhDh4Seai6ojGhskOAy2wy4P7Oyw=; b=RIweeUDDnUQ9AVX5oezYInQEQnTfIVsvaKv4yyUAhlG1FUasIrWQUW1Fmw4CEP1Yj/pmME FLdgo9fbOLGQlgXOhyHJck9AzT0iz+E2ZImbLxxjD54XpyOFoDBCEFTjZtDAacuJ5yEqgI xZMhTjsXnQOD6bEf+SiIxAZ/Oklk5ogJgO6uiGQ8abmGWR1V+TiuEL2GWTcnF7XcvIzRqQ cuPDL4vBTALRk+VbRVbk/mDsYLwQbtR3n/GjRth04yjXOcheW0ETOpn55DtcSCQZ4oOF1N rKbxYOXl7GyBsAHzCguL/E1UnCYiUQvCzE4lJunChJrYbsMAFFYEKxClmrvWWA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ZYNJT3rR3z16sJ; Thu, 10 Apr 2025 14:59:37 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 53AExb35058357; Thu, 10 Apr 2025 14:59:37 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 53AExbs4058353; Thu, 10 Apr 2025 14:59:37 GMT (envelope-from git) Date: Thu, 10 Apr 2025 14:59:37 GMT Message-Id: <202504101459.53AExbs4058353@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Philip Paeps Subject: git: a1f4a530dea3 - releng/13.4 - daemon: stop rebuilding the kqueue every restart of the child List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: philip X-Git-Repository: src X-Git-Refname: refs/heads/releng/13.4 X-Git-Reftype: branch X-Git-Commit: a1f4a530dea32e6987b8788e5ad1f99ffb96fbbf Auto-Submitted: auto-generated The branch releng/13.4 has been updated by philip: URL: https://cgit.FreeBSD.org/src/commit/?id=a1f4a530dea32e6987b8788e5ad1f99ffb96fbbf commit a1f4a530dea32e6987b8788e5ad1f99ffb96fbbf Author: Kyle Evans AuthorDate: 2024-11-19 19:51:27 +0000 Commit: Philip Paeps CommitDate: 2025-04-10 14:39:15 +0000 daemon: stop rebuilding the kqueue every restart of the child We populate the kqueue with all of four kevents: three signal handlers and one for read of the child pipe. Every time we start the child, we rebuild this kqueue from scratch for the child and tear it down before we exit and check if we need to restart the child. As a consequence, we effectively drop any of the signals we're interested in between restarts. Push the kqueue out into the daemon state to avoid losing any signal events in the process, and reimplement the restart timer in terms of kqueue timers. The pipe read event will be automatically deleted upon last close, which leaves us with only the signal events that really get retained between restarts of the child. Approved by: so Security: FreeBSD-EN-25:06.daemon PR: 277959 Reviewed by: des, markj (cherry picked from commit bc1dfc316a2bba97773a14b96f5e976a52524be4) (cherry picked from commit 4bb1a558a2811a27b1211579cb257a11df49c0e1) --- usr.sbin/daemon/daemon.c | 121 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 20 deletions(-) diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c index 52fbfca1dcd2..411929e6e4df 100644 --- a/usr.sbin/daemon/daemon.c +++ b/usr.sbin/daemon/daemon.c @@ -79,6 +79,7 @@ struct daemon_state { enum daemon_mode mode; int pid; int keep_cur_workdir; + int kqueue_fd; int restart_delay; int stdmask; int syslog_priority; @@ -104,6 +105,7 @@ static void daemon_terminate(struct daemon_state *); static void daemon_exec(struct daemon_state *); static bool daemon_is_child_dead(struct daemon_state *); static void daemon_set_child_pipe(struct daemon_state *); +static int daemon_setup_kqueue(void); static const char shortopts[] = "+cfHSp:P:ru:o:s:l:t:m:R:T:h"; @@ -322,6 +324,8 @@ main(int argc, char *argv[]) /* Write out parent pidfile if needed. */ pidfile_write(state.parent_pidfh); + state.kqueue_fd = daemon_setup_kqueue(); + do { state.mode = MODE_SUPERVISE; daemon_eventloop(&state); @@ -377,27 +381,13 @@ daemon_eventloop(struct daemon_state *state) err(1, "pipe"); } - kq = kqueuex(KQUEUE_CLOEXEC); + kq = state->kqueue_fd; EV_SET(&event, state->pipe_fd[0], EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, NULL); if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { err(EXIT_FAILURE, "failed to register kevent"); } - EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); - if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { - err(EXIT_FAILURE, "failed to register kevent"); - } - - EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); - if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { - err(EXIT_FAILURE, "failed to register kevent"); - } - - EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); - if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { - err(EXIT_FAILURE, "failed to register kevent"); - } memset(&event, 0, sizeof(struct kevent)); /* Spawn a child to exec the command. */ @@ -490,28 +480,86 @@ daemon_eventloop(struct daemon_state *state) } continue; default: + assert(0 && "Unexpected kevent filter type"); continue; } } - close(kq); + /* EVFILT_READ kqueue filter goes away here. */ close(state->pipe_fd[0]); state->pipe_fd[0] = -1; } +/* + * Note that daemon_sleep() should not be called with anything but the signal + * events in the kqueue without further consideration. + */ static void daemon_sleep(struct daemon_state *state) { - struct timespec ts = { state->restart_delay, 0 }; + struct kevent event = { 0 }; + int ret; + + assert(state->pipe_fd[0] == -1); + assert(state->pipe_fd[1] == -1); if (!state->restart_enabled) { return; } - while (nanosleep(&ts, &ts) == -1) { - if (errno != EINTR) { - err(1, "nanosleep"); + + EV_SET(&event, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, + state->restart_delay, NULL); + if (kevent(state->kqueue_fd, &event, 1, NULL, 0, NULL) == -1) { + err(1, "failed to register timer"); + } + + for (;;) { + ret = kevent(state->kqueue_fd, NULL, 0, &event, 1, NULL); + if (ret == -1) { + if (errno != EINTR) { + err(1, "kevent"); + } + + continue; + } + + /* + * Any other events being raised are indicative of a problem + * that we need to investigate. Most likely being that + * something was not cleaned up from the eventloop. + */ + assert(event.filter == EVFILT_TIMER || + event.filter == EVFILT_SIGNAL); + + if (event.filter == EVFILT_TIMER) { + /* Break's over, back to work. */ + break; + } + + /* Process any pending signals. */ + switch (event.ident) { + case SIGTERM: + /* + * We could disarm the timer, but we'll be terminating + * promptly anyways. + */ + state->restart_enabled = false; + return; + case SIGHUP: + if (state->log_reopen && state->output_fd >= 0) { + reopen_log(state); + } + + break; + case SIGCHLD: + default: + /* Discard */ + break; } } + + /* SIGTERM should've returned immediately. */ + assert(state->restart_enabled); } static void @@ -701,6 +749,7 @@ daemon_state_init(struct daemon_state *state) .restart_enabled = false, .pid = 0, .keep_cur_workdir = 1, + .kqueue_fd = -1, .restart_delay = 1, .stdmask = STDOUT_FILENO | STDERR_FILENO, .syslog_enabled = false, @@ -719,6 +768,9 @@ daemon_terminate(struct daemon_state *state) { assert(state != NULL); + if (state->kqueue_fd >= 0) { + close(state->kqueue_fd); + } if (state->output_fd >= 0) { close(state->output_fd); } @@ -788,3 +840,32 @@ daemon_set_child_pipe(struct daemon_state *state) /* The child gets dup'd pipes. */ close(state->pipe_fd[0]); } + +static int +daemon_setup_kqueue(void) +{ + int kq; + struct kevent event = { 0 }; + + kq = kqueuex(KQUEUE_CLOEXEC); + if (kq == -1) { + err(EXIT_FAILURE, "kqueue"); + } + + EV_SET(&event, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { + err(EXIT_FAILURE, "failed to register kevent"); + } + + EV_SET(&event, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { + err(EXIT_FAILURE, "failed to register kevent"); + } + + EV_SET(&event, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kq, &event, 1, NULL, 0, NULL) == -1) { + err(EXIT_FAILURE, "failed to register kevent"); + } + + return (kq); +}