aboutsummaryrefslogtreecommitdiff
path: root/initd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'initd/main.c')
-rw-r--r--initd/main.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/initd/main.c b/initd/main.c
index 446553c..e40fc32 100644
--- a/initd/main.c
+++ b/initd/main.c
@@ -1,6 +1,246 @@
/* 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,
+ [STATE_RUNNING] ="\033[22;33m UP \033[0m",
+ [STATE_COMPLETED] = "\033[22;32mDONE\033[0m",
+ [STATE_FAILED] ="\033[22;31mFAIL\033[0m",
+};
+
+static void print_status(const char *msg, int type)
+{
+ printf("[%s] %s\n", status_str[type], msg);
+ fflush(stdout);
+}
+
+static void respawn(svc_run_data_t *rt)
+{
+ if (rt->svc->rspwn_limit > 0) {
+ rt->rspwn_count += 1;
+
+ if (rt->rspwn_count >= rt->svc->rspwn_limit)
+ goto fail;
+ }
+
+ rt->pid = runsvc(rt->svc);
+ if (rt->pid == -1)
+ goto fail;
+
+ rt->state = STATE_RUNNING;
+ return;
+fail:
+ rt->state = STATE_FAILED;
+ print_status(rt->svc->desc, rt->state);
+ return;
+}
+
+static void check_target_completion(void)
+{
+ if (singleshot > 0 || queue_idx < queue_count[target])
+ return;
+
+ switch (target) {
+ case TGT_BOOT:
+ break;
+ case TGT_SHUTDOWN:
+ for (;;)
+ reboot(RB_POWER_OFF);
+ break;
+ case TGT_REBOOT:
+ for (;;)
+ reboot(RB_AUTOBOOT);
+ break;
+ }
+}
+
+static void supervisor_handle_exited(pid_t pid, int status)
+{
+ svc_run_data_t *rt;
+ service_t *svc;
+ size_t i;
+
+ for (i = 0; i < rt_count; ++i) {
+ if (rt_data[i].pid == pid)
+ break;
+ }
+
+ if (i >= rt_count)
+ return;
+
+ rt = rt_data + i;
+ svc = rt->svc;
+ rt->status = status;
+ rt->pid = -1;
+
+ if (svc->type == SVC_RESPAWN) {
+ if (target != TGT_REBOOT && target != TGT_SHUTDOWN)
+ respawn(rt);
+ } else {
+ if (rt->status == EXIT_SUCCESS) {
+ rt->state = STATE_COMPLETED;
+ } else {
+ rt->state = STATE_FAILED;
+ }
+
+ print_status(svc->desc, rt->state);
+
+ if (svc->type == SVC_ONCE)
+ singleshot -= 1;
+
+ check_target_completion();
+ }
+}
+
+static void supervisor_set_target(int next)
+{
+ if (target == TGT_REBOOT || target == TGT_SHUTDOWN || next == target)
+ return;
+
+ if (queue_idx < queue_count[target]) {
+ if (next != TGT_REBOOT && next != TGT_SHUTDOWN)
+ return;
+ }
+
+ target = next;
+ queue_idx = 0;
+}
+
+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 pid_t wait_for_process(int *status)
+{
+ pid_t pid = wait(status);
+
+ if (pid == -1 || !WIFEXITED(*status)) {
+ *status = EXIT_FAILURE;
+ } else {
+ *status = WEXITSTATUS(*status);
+ }
+ return pid;
+}
+
+static void supervisor_process_queues(void)
+{
+ svc_run_data_t *rt;
+ service_t *svc;
+ size_t count;
+ int status;
+ pid_t pid;
+
+ count = queue_count[target];
+
+ if (queue_idx >= count) {
+ pid = wait_for_process(&status);
+ supervisor_handle_exited(pid, status);
+ return;
+ }
+
+ rt = rt_data + queue_start[target] + queue_idx++;
+ svc = rt->svc;
+
+ if (svc->flags & SVC_FLAG_HAS_EXEC) {
+ rt->pid = runsvc(rt->svc);
+
+ if (rt->pid == -1) {
+ rt->state = STATE_FAILED;
+ print_status(svc->desc, rt->state);
+ } else {
+ rt->state = STATE_RUNNING;
+ print_status(svc->desc, rt->state);
+
+ switch (svc->type) {
+ case SVC_WAIT:
+ for (;;) {
+ pid = wait_for_process(&status);
+ if (pid == rt->pid)
+ break;
+ supervisor_handle_exited(pid, status);
+ }
+
+ rt->state = status == EXIT_SUCCESS ?
+ STATE_COMPLETED : STATE_FAILED;
+ print_status(svc->desc, rt->state);
+ break;
+ case SVC_ONCE:
+ singleshot += 1;
+ break;
+ }
+ }
+ } else {
+ rt->status = EXIT_SUCCESS;
+ rt->state = STATE_COMPLETED;
+ print_status(svc->desc, rt->state);
+ }
+
+ check_target_completion();
+}
+
static void handle_signal(int signo)
{
switch (signo) {