/* 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 bool waiting = false; /*****************************************************************************/ 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] || waiting) return; switch (target) { case TGT_BOOT: break; case TGT_SHUTDOWN: for (;;) reboot(RB_POWER_OFF); break; case TGT_REBOOT: for (;;) reboot(RB_AUTOBOOT); break; } } 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; } else if (svc->type == SVC_WAIT) { waiting = false; } check_target_completion(); } } 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; } 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); } bool supervisor_process_queues(void) { sigset_t mask, old_mask; svc_run_data_t *rt; service_t *svc; 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; } else { rt->state = STATE_RUNNING; switch (svc->type) { case SVC_WAIT: waiting = true; 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(); out_unblock: sigprocmask(SIG_SETMASK, &old_mask, NULL); return ret; }