aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/daemon/daemon.c121
1 files changed, 101 insertions, 20 deletions
diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c
index 6d0ce2cc10e6..73524e3bd286 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 int pidfile_truncate(struct pidfh *);
@@ -324,6 +326,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);
@@ -379,27 +383,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. */
@@ -492,11 +482,12 @@ 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;
@@ -510,19 +501,76 @@ daemon_eventloop(struct daemon_state *state)
}
}
+/*
+ * 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
@@ -712,6 +760,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,
@@ -730,6 +779,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);
}
@@ -801,6 +853,35 @@ daemon_set_child_pipe(struct daemon_state *state)
}
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)
{
int pfd = pidfile_fileno(pfh);