/* 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 svc_run_data_t *waiting = NULL; 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; } void config_set_waiting(svc_run_data_t *rt) { assert(waiting == NULL); waiting = rt; } bool config_is_waiting(void) { if (waiting != NULL) { if (waiting->state == STATE_RUNNING) return true; waiting = NULL; } return false; } 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 (config_is_waiting()) return NULL; 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; waiting = NULL; } bool config_should_respawn(void) { return target != TGT_REBOOT && target != TGT_SHUTDOWN; }