aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--initd/Makemodule.am2
-rw-r--r--initd/config.c131
-rw-r--r--initd/init.h40
-rw-r--r--initd/main.c104
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;
}
}