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/supervisor.c | 291 ++++++++++++++++++++++++++++------------------------- 1 file changed, 156 insertions(+), 135 deletions(-) (limited to 'initd/supervisor.c') 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