diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c --- a/usr.sbin/daemon/daemon.c +++ b/usr.sbin/daemon/daemon.c @@ -86,6 +86,7 @@ int pipe_rd; int pipe_wr; int keep_cur_workdir; + int kqueue_fd; int restart_delay; int stdmask; int syslog_priority; @@ -113,6 +114,7 @@ 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 int pidfile_truncate(struct pidfh *); @@ -344,6 +346,8 @@ /* 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); @@ -408,27 +412,13 @@ state->pipe_rd = pipe_fd[0]; state->pipe_wr = pipe_fd[1]; - kq = kqueuex(KQUEUE_CLOEXEC); + kq = state->kqueue_fd; EV_SET(&event, state->pipe_rd, 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. */ @@ -521,11 +511,12 @@ } continue; default: + assert(0 && "Unexpected kevent filter type"); continue; } } - close(kq); + /* EVFILT_READ kqueue filter goes away here. */ close(state->pipe_rd); state->pipe_rd = -1; @@ -539,19 +530,76 @@ } } +/* + * 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_rd == -1); + assert(state->pipe_wr == -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 @@ -745,6 +793,7 @@ .pipe_rd = -1, .pipe_wr = -1, .keep_cur_workdir = 1, + .kqueue_fd = -1, .restart_delay = 1, .stdmask = STDOUT_FILENO | STDERR_FILENO, .syslog_enabled = false, @@ -765,6 +814,9 @@ { assert(state != NULL); + if (state->kqueue_fd >= 0) { + close(state->kqueue_fd); + } if (state->output_fd >= 0) { close(state->output_fd); } @@ -835,6 +887,35 @@ close(state->pipe_rd); } +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); +} + static int pidfile_truncate(struct pidfh *pfh) {