From 62133a68b7a1aecf65765f7137ef8d6e0eed913e Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 8 May 2020 01:50:49 +0200 Subject: Cleanup: complete redesign of the initd supervisor - Cleanly seperate service description parsed from file from the actual run-time data. - Remove the use of the signalfd and make asyncronous calls into the supervisor from signal context work. Signed-off-by: David Oberhollenzer --- initd/init.h | 19 +++- initd/main.c | 73 +++----------- initd/supervisor.c | 291 ++++++++++++++++++++++++++++------------------------- 3 files changed, 188 insertions(+), 195 deletions(-) (limited to 'initd') diff --git a/initd/init.h b/initd/init.h index 67d5fc4..9e18b96 100644 --- a/initd/init.h +++ b/initd/init.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -33,6 +32,24 @@ enum { STATUS_STARTED, }; +/* service run time data */ + +enum { + STATE_OFF, + STATE_RUNNING, + STATE_QUEUED, + STATE_COMPLETED, + STATE_FAILED, +}; + +typedef struct { + service_t *svc; /* the underlying service description */ + int state; /* what STATE_* the service is currently in */ + int rspwn_count; /* services respawn counter */ + int status; /* if exited, process exit status */ + pid_t pid; /* if still running, the pid */ +} svc_run_data_t; + /********** main.c **********/ void target_completed(int target); diff --git a/initd/main.c b/initd/main.c index 5a4c37a..caa2a1e 100644 --- a/initd/main.c +++ b/initd/main.c @@ -1,20 +1,12 @@ /* SPDX-License-Identifier: ISC */ #include "init.h" -static int sigfd = -1; - -static void handle_signal(void) +static void handle_signal(int signo) { - struct signalfd_siginfo info; int status; pid_t pid; - if (read(sigfd, &info, sizeof(info)) != sizeof(info)) { - perror("read on signal fd"); - return; - } - - switch (info.ssi_signo) { + switch (signo) { case SIGCHLD: while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { status = WIFEXITED(status) ? WEXITSTATUS(status) : @@ -52,66 +44,29 @@ void target_completed(int target) } } -static int sigsetup(void) +int main(void) { - sigset_t mask; - int sfd; + struct sigaction act; - sigfillset(&mask); - if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { - perror("sigprocmask"); - return -1; - } + supervisor_init(); - sfd = signalfd(-1, &mask, SFD_CLOEXEC); - if (sfd == -1) { - perror("signalfd"); - return -1; - } + memset(&act, 0, sizeof(act)); + act.sa_handler = handle_signal; + + sigaction(SIGCHLD, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGUSR1, &act, NULL); if (reboot(LINUX_REBOOT_CMD_CAD_OFF)) perror("cannot disable CTRL+ALT+DEL"); - return sfd; -} - -int main(void) -{ - int i, ret, count; - struct pollfd pfd[2]; - - if (getpid() != 1) { - fputs("init does not have pid 1, terminating!\n", stderr); - return EXIT_FAILURE; - } - - supervisor_init(); - - sigfd = sigsetup(); - if (sigfd < 0) - return -1; - for (;;) { while (supervisor_process_queues()) ; - memset(pfd, 0, sizeof(pfd)); - count = 0; - - pfd[count].fd = sigfd; - pfd[count].events = POLLIN; - ++count; - - ret = poll(pfd, count, -1); - if (ret <= 0) - continue; - - for (i = 0; i < count; ++i) { - if (pfd[i].revents & POLLIN) { - if (pfd[i].fd == sigfd) - handle_signal(); - } - } + pause(); } return EXIT_SUCCESS; diff --git a/initd/supervisor.c b/initd/supervisor.c index 32a23f2..8de61c9 100644 --- a/initd/supervisor.c +++ b/initd/supervisor.c @@ -1,193 +1,214 @@ /* SPDX-License-Identifier: ISC */ #include "init.h" +/* service configurations */ static service_list_t cfg; -static int service_id = 1; +/* service run time data, sorted by target and topological order */ +static svc_run_data_t *rt_data = NULL; +static size_t rt_count = 0; + +/* maps a target to range in rt_data */ +static size_t queue_start[TGT_MAX]; +static size_t queue_count[TGT_MAX]; + +/* current state */ +static size_t queue_idx; + static int target = -1; -static service_t *running = NULL; -static service_t *terminated = NULL; -static service_t *queue = NULL; -static service_t *completed = NULL; -static service_t *failed = NULL; -static int singleshot = 0; +static size_t singleshot = 0; static bool waiting = false; -static int start_service(service_t *svc) +/*****************************************************************************/ + +static svc_run_data_t *run_time_data_from_pid(pid_t pid) { - if (svc->id < 1) - svc->id = service_id++; - - svc->pid = runsvc(svc); - if (svc->pid == -1) { - print_status(svc->desc, STATUS_FAIL, false); - svc->next = completed; - completed = svc; - return -1; + size_t i; + + for (i = 0; i < rt_count; ++i) { + if (rt_data[i].pid == pid) + return rt_data + i; } - svc->next = running; - running = svc; - return 0; + return NULL; } -static void handle_terminated_service(service_t *svc) +static void respawn(svc_run_data_t *rt) { - switch (svc->type) { - case SVC_RESPAWN: - if (target == TGT_REBOOT || target == TGT_SHUTDOWN) - break; + if (rt->svc->rspwn_limit > 0) { + rt->rspwn_count += 1; - if (svc->flags & SVC_FLAG_ADMIN_STOPPED) - break; + if (rt->rspwn_count >= rt->svc->rspwn_limit) + goto fail; + } - if (svc->rspwn_limit > 0) { - svc->rspwn_count += 1; + rt->pid = runsvc(rt->svc); + if (rt->pid == -1) + goto fail; - if (svc->rspwn_count >= svc->rspwn_limit) { - print_status(svc->desc, STATUS_FAIL, false); - goto out_failure; - } - } - - start_service(svc); - return; - case SVC_WAIT: - waiting = false; - print_status(svc->desc, - svc->status == EXIT_SUCCESS ? - STATUS_OK : STATUS_FAIL, true); - if (singleshot == 0 && queue == NULL) - target_completed(target); - if (svc->status != EXIT_SUCCESS) - goto out_failure; - break; - case SVC_ONCE: - singleshot -= 1; - print_status(svc->desc, - svc->status == EXIT_SUCCESS ? - STATUS_OK : STATUS_FAIL, false); - if (singleshot == 0 && queue == NULL && !waiting) - target_completed(target); - if (svc->status != EXIT_SUCCESS) - goto out_failure; - break; - } - svc->next = completed; - completed = svc; + rt->state = STATE_RUNNING; + return; +fail: + print_status(rt->svc->desc, STATUS_FAIL, false); + rt->state = STATE_FAILED; return; -out_failure: - svc->next = failed; - failed = svc; } void supervisor_handle_exited(pid_t pid, int status) { - service_t *prev = NULL, *svc = running; - - while (svc != NULL && svc->pid != pid) { - prev = svc; - svc = svc->next; - } + svc_run_data_t *rt = run_time_data_from_pid(pid); + service_t *svc; - if (svc == NULL) + if (rt == NULL) return; - if (prev != NULL) { - prev->next = svc->next; + svc = rt->svc; + rt->status = status; + rt->pid = -1; + + if (svc->type == SVC_RESPAWN) { + if (target != TGT_REBOOT && target != TGT_SHUTDOWN) + respawn(rt); } else { - running = svc->next; - } + if (rt->status == EXIT_SUCCESS) { + rt->state = STATE_COMPLETED; + print_status(svc->desc, STATUS_OK, + svc->type == SVC_WAIT); + } else { + rt->state = STATE_FAILED; + print_status(svc->desc, STATUS_FAIL, + svc->type == SVC_WAIT); + } + + waiting = false; + if (svc->type == SVC_ONCE) + singleshot -= 1; - svc->status = status; - svc->next = terminated; - terminated = svc; + if (singleshot == 0 && queue_idx >= queue_count[target] && !waiting) + target_completed(target); + } } void supervisor_set_target(int next) { - service_t *svc; - if (target == TGT_REBOOT || target == TGT_SHUTDOWN || next == target) return; - if (next == TGT_REBOOT || next == TGT_SHUTDOWN) { - while (queue != NULL) { - svc = queue; - queue = queue->next; - delsvc(svc); - } + if (queue_idx < queue_count[target]) { + if (next != TGT_REBOOT && next != TGT_SHUTDOWN) + return; } - if (queue != NULL) { - for (svc = queue; svc->next != NULL; svc = svc->next) - ; - svc->next = cfg.targets[next]; - } else { - queue = cfg.targets[next]; - } - - cfg.targets[next] = NULL; target = next; + queue_idx = 0; } void supervisor_init(void) { - int status = STATUS_OK; + int status = STATUS_FAIL; + service_t *it; + size_t i, j; if (svcscan(SVCDIR, &cfg)) + goto out; + + /* allocate run time data */ + rt_count = 0; + + for (i = 0; i < TGT_MAX; ++i) { + for (it = cfg.targets[i]; it != NULL; it = it->next) + ++rt_count; + } + + rt_data = calloc(rt_count, sizeof(rt_data[0])); + if (rt_data == NULL) { status = STATUS_FAIL; + rt_count = 0; + goto out; + } + + /* map runtime data to services */ + j = 0; + + for (i = 0; i < TGT_MAX; ++i) { + queue_start[i] = j; + + for (it = cfg.targets[i]; it != NULL; it = it->next) { + rt_data[j].svc = it; + rt_data[j].state = STATE_OFF; + rt_data[j].pid = -1; + ++j; + } + + queue_count[i] = j - queue_start[i]; + } + /* initialize state */ + singleshot = 0; + queue_idx = 0; + waiting = false; target = TGT_BOOT; - queue = cfg.targets[TGT_BOOT]; - cfg.targets[TGT_BOOT] = NULL; + status = STATUS_OK; + for (i = 0; i < queue_count[target]; ++i) + rt_data[queue_start[target] + i].state = STATE_QUEUED; +out: print_status("reading configuration from " SVCDIR, status, false); } bool supervisor_process_queues(void) { + sigset_t mask, old_mask; + svc_run_data_t *rt; service_t *svc; - - if (terminated != NULL) { - svc = terminated; - terminated = terminated->next; - - handle_terminated_service(svc); - return true; - } - - if (waiting || queue == NULL) - return false; - - svc = queue; - queue = queue->next; - - if (!(svc->flags & SVC_FLAG_HAS_EXEC)) { + size_t count; + bool ret = false; + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &old_mask); + + if (waiting) + goto out_unblock; + + count = queue_count[target]; + if (queue_idx >= count) + goto out_unblock; + + rt = rt_data + queue_start[target] + queue_idx++; + svc = rt->svc; + ret = true; + + if (svc->flags & SVC_FLAG_HAS_EXEC) { + rt->pid = runsvc(rt->svc); + + if (rt->pid == -1) { + rt->state = STATE_FAILED; + print_status(rt->svc->desc, STATUS_FAIL, false); + } else { + rt->state = STATE_RUNNING; + + switch (svc->type) { + case SVC_WAIT: + print_status(svc->desc, STATUS_WAIT, false); + waiting = true; + break; + case SVC_RESPAWN: + print_status(svc->desc, STATUS_STARTED, false); + break; + case SVC_ONCE: + singleshot += 1; + break; + } + } + } else { print_status(svc->desc, STATUS_OK, false); - svc->status = EXIT_SUCCESS; - svc->next = completed; - completed = svc; - goto out; + rt->status = EXIT_SUCCESS; + rt->state = STATE_COMPLETED; } - if (start_service(svc) != 0) - return true; - - switch (svc->type) { - case SVC_WAIT: - print_status(svc->desc, STATUS_WAIT, false); - waiting = true; - break; - case SVC_RESPAWN: - print_status(svc->desc, STATUS_STARTED, false); - break; - case SVC_ONCE: - singleshot += 1; - break; - } -out: - if (singleshot == 0 && queue == NULL && !waiting) + if (singleshot == 0 && !waiting && queue_idx >= count) target_completed(target); - return true; +out_unblock: + sigprocmask(SIG_SETMASK, &old_mask, NULL); + return ret; } -- cgit v1.2.3