diff options
-rw-r--r-- | initd/Makemodule.am | 2 | ||||
-rw-r--r-- | initd/config.c | 131 | ||||
-rw-r--r-- | initd/init.h | 40 | ||||
-rw-r--r-- | initd/main.c | 104 |
4 files changed, 185 insertions, 92 deletions
diff --git a/initd/Makemodule.am b/initd/Makemodule.am index 9f4b872..6f6b523 100644 --- a/initd/Makemodule.am +++ b/initd/Makemodule.am @@ -1,4 +1,4 @@ -init_SOURCES = initd/main.c initd/init.h initd/runsvc.c +init_SOURCES = initd/main.c initd/init.h initd/runsvc.c initd/config.c init_CPPFLAGS = $(AM_CPPFLAGS) init_CFLAGS = $(AM_CFLAGS) init_LDFLAGS = $(AM_LDFLAGS) diff --git a/initd/config.c b/initd/config.c new file mode 100644 index 0000000..7f9f25c --- /dev/null +++ b/initd/config.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: ISC */ +#include "init.h" + +/* service configurations */ +static service_list_t cfg; + +/* 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 = 0; +static int target = TGT_BOOT; +static size_t singleshot = 0; + +int config_load(void) +{ + service_t *it; + size_t i, j; + + if (svcscan(SVCDIR, &cfg)) + return -1; + + /* 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) { + rt_count = 0; + del_svc_list(&cfg); + return -1; + } + + /* 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 */ + for (i = 0; i < queue_count[target]; ++i) + rt_data[queue_start[target] + i].state = STATE_QUEUED; + + return 0; +} + +svc_run_data_t *config_rt_data_by_pid(pid_t pid) +{ + size_t i; + + if (pid <= 1) + return NULL; + + for (i = 0; i < rt_count; ++i) { + if (rt_data[i].pid == pid) + return rt_data + i; + } + + return NULL; +} + +svc_run_data_t *config_dequeue(void) +{ + if (queue_idx >= queue_count[target]) + return NULL; + + return rt_data + queue_start[target] + queue_idx++; +} + +void config_set_target(int tgt) +{ + if (target == TGT_REBOOT || target == TGT_SHUTDOWN || target == tgt) + return; + + /* TODO: + - if there are services left in the queue of the current target: + - refuse unless the new target is shutdown/reboot? + - mark pending as "STATE_OFF" + - if the new target is reboot/shutdown + - enqueue the currently running services as "stop those"? + */ + + target = tgt; + queue_idx = 0; +} + +int config_get_current_target(void) +{ + return target; +} + +bool config_is_current_target_complete(void) +{ + return singleshot == 0 && queue_idx >= queue_count[target]; +} + +bool config_should_respawn(void) +{ + return target != TGT_REBOOT && target != TGT_SHUTDOWN; +} + +void config_singleshot_started(void) +{ + singleshot++; +} + +void config_singleshot_terminated(void) +{ + assert(singleshot > 0); + + singleshot--; +} diff --git a/initd/init.h b/initd/init.h index 2c2adb1..813dedb 100644 --- a/initd/init.h +++ b/initd/init.h @@ -10,6 +10,7 @@ #include <stdlib.h> #include <unistd.h> #include <endian.h> +#include <assert.h> #include <stdio.h> #include <errno.h> #include <poll.h> @@ -54,4 +55,43 @@ typedef struct { */ pid_t runsvc(service_t *svc); +/********** config.c **********/ + +/* load persistent configuration from disk */ +int config_load(void); + +svc_run_data_t *config_rt_data_by_pid(pid_t pid); + +/* + Get the next service that is waiting to be launched. Returns NULL if + the queue is empty. + */ +svc_run_data_t *config_dequeue(void); + +/* + Transition to a different target, if possible in the current state. + + The transition may be ignored, e.g. if we are currently in the reboot + or shutdown target. + */ +void config_set_target(int tgt); + +/* get the current run time target from the configuration manager */ +int config_get_current_target(void); + +/* + Find out if the current target is completed, i.e. there are no more services + left in the queue and no active single shot services. + */ +bool config_is_current_target_complete(void); + +/* Ask whether we should respawn services in the current target */ +bool config_should_respawn(void); + +/* notify the configuration manager that a single shot service started */ +void config_singleshot_started(void); + +/* notify the configuration manager that a single shot service terminated */ +void config_singleshot_terminated(void); + #endif /* INIT_H */ diff --git a/initd/main.c b/initd/main.c index dc2e631..c804105 100644 --- a/initd/main.c +++ b/initd/main.c @@ -1,24 +1,6 @@ /* SPDX-License-Identifier: ISC */ #include "init.h" -/* service configurations */ -static service_list_t cfg; - -/* 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 = 0; -static int target = TGT_BOOT; -static size_t singleshot = 0; - -/*****************************************************************************/ - static const char *status_str[] = { [STATE_OFF] = NULL, [STATE_QUEUED] = NULL, @@ -54,76 +36,23 @@ fail: return; } -static void supervisor_init(void) -{ - int status = STATE_FAILED; - 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) { - 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 */ - status = STATE_COMPLETED; - - 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); -} - static svc_run_data_t *wait_for_process(void) { svc_run_data_t *rt; int status; pid_t pid; - size_t i; do { pid = wait(&status); - if (pid == -1) - return NULL; - for (i = 0; i < rt_count && rt_data[i].pid != pid; ++i) - ; - } while (i >= rt_count); + rt = config_rt_data_by_pid(pid); + } while (rt == NULL); - rt = rt_data + i; rt->status = WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; rt->pid = -1; if (rt->svc->type == SVC_RESPAWN) { - if (target != TGT_REBOOT && target != TGT_SHUTDOWN) + if (config_should_respawn()) respawn(rt); } else { if (rt->status == EXIT_SUCCESS) { @@ -135,7 +64,7 @@ static svc_run_data_t *wait_for_process(void) print_status(rt->svc->desc, rt->state); if (rt->svc->type == SVC_ONCE) - singleshot -= 1; + config_singleshot_terminated(); } return rt; @@ -145,16 +74,10 @@ static void handle_signal(int signo) { switch (signo) { case SIGTERM: - if (target != TGT_REBOOT && target != TGT_SHUTDOWN) { - target = TGT_SHUTDOWN; - queue_idx = 0; - } + config_set_target(TGT_SHUTDOWN); break; case SIGINT: - if (target != TGT_REBOOT && target != TGT_SHUTDOWN) { - target = TGT_REBOOT; - queue_idx = 0; - } + config_set_target(TGT_REBOOT); break; case SIGHUP: break; @@ -165,10 +88,10 @@ static void handle_signal(int signo) static void check_target_completion(void) { - if (singleshot > 0 || queue_idx < queue_count[target]) + if (!config_is_current_target_complete()) return; - switch (target) { + switch (config_get_current_target()) { case TGT_BOOT: break; case TGT_SHUTDOWN: @@ -187,9 +110,9 @@ int main(void) svc_run_data_t *rt, *terminated; struct sigaction act; service_t *svc; - size_t count; - supervisor_init(); + if (config_load()) + return EXIT_FAILURE; memset(&act, 0, sizeof(act)); act.sa_handler = handle_signal; @@ -203,9 +126,9 @@ int main(void) perror("cannot disable CTRL+ALT+DEL"); for (;;) { - count = queue_count[target]; + rt = config_dequeue(); - if (queue_idx >= count) { + if (rt == NULL) { terminated = wait_for_process(); if (terminated != NULL) @@ -213,7 +136,6 @@ int main(void) continue; } - rt = rt_data + queue_start[target] + queue_idx++; svc = rt->svc; if (svc->flags & SVC_FLAG_HAS_EXEC) { @@ -235,7 +157,7 @@ int main(void) } break; case SVC_ONCE: - singleshot += 1; + config_singleshot_started(); break; } } |