aboutsummaryrefslogtreecommitdiff
path: root/initd
diff options
context:
space:
mode:
authorDavid Oberhollenzer <goliath@infraroot.at>2020-05-08 01:50:49 +0200
committerDavid Oberhollenzer <goliath@infraroot.at>2020-05-08 02:08:00 +0200
commit62133a68b7a1aecf65765f7137ef8d6e0eed913e (patch)
treef271dc8b24c4db4f7b054b633c4fa6f6e2157d5c /initd
parent13aa3840cc94ce37ef1e63c093c0f71ac84e90fd (diff)
Cleanup: complete redesign of the initd supervisor
- Cleanly seperate service description parsed from file from the actual run-time data. - Remove the use of the signalfd and make asyncronous calls into the supervisor from signal context work. Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
Diffstat (limited to 'initd')
-rw-r--r--initd/init.h19
-rw-r--r--initd/main.c73
-rw-r--r--initd/supervisor.c291
3 files changed, 188 insertions, 195 deletions
diff --git a/initd/init.h b/initd/init.h
index 67d5fc4..9e18b96 100644
--- a/initd/init.h
+++ b/initd/init.h
@@ -15,7 +15,6 @@
#include <poll.h>
#include <linux/reboot.h>
-#include <sys/signalfd.h>
#include <sys/reboot.h>
#include <stdbool.h>
#include <signal.h>
@@ -33,6 +32,24 @@ enum {
STATUS_STARTED,
};
+/* service run time data */
+
+enum {
+ STATE_OFF,
+ STATE_RUNNING,
+ STATE_QUEUED,
+ STATE_COMPLETED,
+ STATE_FAILED,
+};
+
+typedef struct {
+ service_t *svc; /* the underlying service description */
+ int state; /* what STATE_* the service is currently in */
+ int rspwn_count; /* services respawn counter */
+ int status; /* if exited, process exit status */
+ pid_t pid; /* if still running, the pid */
+} svc_run_data_t;
+
/********** main.c **********/
void target_completed(int target);
diff --git a/initd/main.c b/initd/main.c
index 5a4c37a..caa2a1e 100644
--- a/initd/main.c
+++ b/initd/main.c
@@ -1,20 +1,12 @@
/* SPDX-License-Identifier: ISC */
#include "init.h"
-static int sigfd = -1;
-
-static void handle_signal(void)
+static void handle_signal(int signo)
{
- struct signalfd_siginfo info;
int status;
pid_t pid;
- if (read(sigfd, &info, sizeof(info)) != sizeof(info)) {
- perror("read on signal fd");
- return;
- }
-
- switch (info.ssi_signo) {
+ switch (signo) {
case SIGCHLD:
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
status = WIFEXITED(status) ? WEXITSTATUS(status) :
@@ -52,66 +44,29 @@ void target_completed(int target)
}
}
-static int sigsetup(void)
+int main(void)
{
- sigset_t mask;
- int sfd;
+ struct sigaction act;
- sigfillset(&mask);
- if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
- perror("sigprocmask");
- return -1;
- }
+ supervisor_init();
- sfd = signalfd(-1, &mask, SFD_CLOEXEC);
- if (sfd == -1) {
- perror("signalfd");
- return -1;
- }
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = handle_signal;
+
+ sigaction(SIGCHLD, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGUSR1, &act, NULL);
if (reboot(LINUX_REBOOT_CMD_CAD_OFF))
perror("cannot disable CTRL+ALT+DEL");
- return sfd;
-}
-
-int main(void)
-{
- int i, ret, count;
- struct pollfd pfd[2];
-
- if (getpid() != 1) {
- fputs("init does not have pid 1, terminating!\n", stderr);
- return EXIT_FAILURE;
- }
-
- supervisor_init();
-
- sigfd = sigsetup();
- if (sigfd < 0)
- return -1;
-
for (;;) {
while (supervisor_process_queues())
;
- memset(pfd, 0, sizeof(pfd));
- count = 0;
-
- pfd[count].fd = sigfd;
- pfd[count].events = POLLIN;
- ++count;
-
- ret = poll(pfd, count, -1);
- if (ret <= 0)
- continue;
-
- for (i = 0; i < count; ++i) {
- if (pfd[i].revents & POLLIN) {
- if (pfd[i].fd == sigfd)
- handle_signal();
- }
- }
+ pause();
}
return EXIT_SUCCESS;
diff --git a/initd/supervisor.c b/initd/supervisor.c
index 32a23f2..8de61c9 100644
--- a/initd/supervisor.c
+++ b/initd/supervisor.c
@@ -1,193 +1,214 @@
/* SPDX-License-Identifier: ISC */
#include "init.h"
+/* service configurations */
static service_list_t cfg;
-static int service_id = 1;
+/* 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;
+
static int target = -1;
-static service_t *running = NULL;
-static service_t *terminated = NULL;
-static service_t *queue = NULL;
-static service_t *completed = NULL;
-static service_t *failed = NULL;
-static int singleshot = 0;
+static size_t singleshot = 0;
static bool waiting = false;
-static int start_service(service_t *svc)
+/*****************************************************************************/
+
+static svc_run_data_t *run_time_data_from_pid(pid_t pid)
{
- if (svc->id < 1)
- svc->id = service_id++;
-
- svc->pid = runsvc(svc);
- if (svc->pid == -1) {
- print_status(svc->desc, STATUS_FAIL, false);
- svc->next = completed;
- completed = svc;
- return -1;
+ size_t i;
+
+ for (i = 0; i < rt_count; ++i) {
+ if (rt_data[i].pid == pid)
+ return rt_data + i;
}
- svc->next = running;
- running = svc;
- return 0;
+ return NULL;
}
-static void handle_terminated_service(service_t *svc)
+static void respawn(svc_run_data_t *rt)
{
- switch (svc->type) {
- case SVC_RESPAWN:
- if (target == TGT_REBOOT || target == TGT_SHUTDOWN)
- break;
+ if (rt->svc->rspwn_limit > 0) {
+ rt->rspwn_count += 1;
- if (svc->flags & SVC_FLAG_ADMIN_STOPPED)
- break;
+ if (rt->rspwn_count >= rt->svc->rspwn_limit)
+ goto fail;
+ }
- if (svc->rspwn_limit > 0) {
- svc->rspwn_count += 1;
+ rt->pid = runsvc(rt->svc);
+ if (rt->pid == -1)
+ goto fail;
- if (svc->rspwn_count >= svc->rspwn_limit) {
- print_status(svc->desc, STATUS_FAIL, false);
- goto out_failure;
- }
- }
-
- start_service(svc);
- return;
- case SVC_WAIT:
- waiting = false;
- print_status(svc->desc,
- svc->status == EXIT_SUCCESS ?
- STATUS_OK : STATUS_FAIL, true);
- if (singleshot == 0 && queue == NULL)
- target_completed(target);
- if (svc->status != EXIT_SUCCESS)
- goto out_failure;
- break;
- case SVC_ONCE:
- singleshot -= 1;
- print_status(svc->desc,
- svc->status == EXIT_SUCCESS ?
- STATUS_OK : STATUS_FAIL, false);
- if (singleshot == 0 && queue == NULL && !waiting)
- target_completed(target);
- if (svc->status != EXIT_SUCCESS)
- goto out_failure;
- break;
- }
- svc->next = completed;
- completed = svc;
+ rt->state = STATE_RUNNING;
+ return;
+fail:
+ print_status(rt->svc->desc, STATUS_FAIL, false);
+ rt->state = STATE_FAILED;
return;
-out_failure:
- svc->next = failed;
- failed = svc;
}
void supervisor_handle_exited(pid_t pid, int status)
{
- service_t *prev = NULL, *svc = running;
-
- while (svc != NULL && svc->pid != pid) {
- prev = svc;
- svc = svc->next;
- }
+ svc_run_data_t *rt = run_time_data_from_pid(pid);
+ service_t *svc;
- if (svc == NULL)
+ if (rt == NULL)
return;
- if (prev != NULL) {
- prev->next = svc->next;
+ svc = rt->svc;
+ rt->status = status;
+ rt->pid = -1;
+
+ if (svc->type == SVC_RESPAWN) {
+ if (target != TGT_REBOOT && target != TGT_SHUTDOWN)
+ respawn(rt);
} else {
- running = svc->next;
- }
+ if (rt->status == EXIT_SUCCESS) {
+ rt->state = STATE_COMPLETED;
+ print_status(svc->desc, STATUS_OK,
+ svc->type == SVC_WAIT);
+ } else {
+ rt->state = STATE_FAILED;
+ print_status(svc->desc, STATUS_FAIL,
+ svc->type == SVC_WAIT);
+ }
+
+ waiting = false;
+ if (svc->type == SVC_ONCE)
+ singleshot -= 1;
- svc->status = status;
- svc->next = terminated;
- terminated = svc;
+ if (singleshot == 0 && queue_idx >= queue_count[target] && !waiting)
+ target_completed(target);
+ }
}
void supervisor_set_target(int next)
{
- service_t *svc;
-
if (target == TGT_REBOOT || target == TGT_SHUTDOWN || next == target)
return;
- if (next == TGT_REBOOT || next == TGT_SHUTDOWN) {
- while (queue != NULL) {
- svc = queue;
- queue = queue->next;
- delsvc(svc);
- }
+ if (queue_idx < queue_count[target]) {
+ if (next != TGT_REBOOT && next != TGT_SHUTDOWN)
+ return;
}
- if (queue != NULL) {
- for (svc = queue; svc->next != NULL; svc = svc->next)
- ;
- svc->next = cfg.targets[next];
- } else {
- queue = cfg.targets[next];
- }
-
- cfg.targets[next] = NULL;
target = next;
+ queue_idx = 0;
}
void supervisor_init(void)
{
- int status = STATUS_OK;
+ int status = STATUS_FAIL;
+ 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) {
status = STATUS_FAIL;
+ 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 */
+ singleshot = 0;
+ queue_idx = 0;
+ waiting = false;
target = TGT_BOOT;
- queue = cfg.targets[TGT_BOOT];
- cfg.targets[TGT_BOOT] = NULL;
+ status = STATUS_OK;
+ 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, false);
}
bool supervisor_process_queues(void)
{
+ sigset_t mask, old_mask;
+ svc_run_data_t *rt;
service_t *svc;
-
- if (terminated != NULL) {
- svc = terminated;
- terminated = terminated->next;
-
- handle_terminated_service(svc);
- return true;
- }
-
- if (waiting || queue == NULL)
- return false;
-
- svc = queue;
- queue = queue->next;
-
- if (!(svc->flags & SVC_FLAG_HAS_EXEC)) {
+ 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;
+ print_status(rt->svc->desc, STATUS_FAIL, false);
+ } else {
+ rt->state = STATE_RUNNING;
+
+ switch (svc->type) {
+ case SVC_WAIT:
+ print_status(svc->desc, STATUS_WAIT, false);
+ waiting = true;
+ break;
+ case SVC_RESPAWN:
+ print_status(svc->desc, STATUS_STARTED, false);
+ break;
+ case SVC_ONCE:
+ singleshot += 1;
+ break;
+ }
+ }
+ } else {
print_status(svc->desc, STATUS_OK, false);
- svc->status = EXIT_SUCCESS;
- svc->next = completed;
- completed = svc;
- goto out;
+ rt->status = EXIT_SUCCESS;
+ rt->state = STATE_COMPLETED;
}
- if (start_service(svc) != 0)
- return true;
-
- switch (svc->type) {
- case SVC_WAIT:
- print_status(svc->desc, STATUS_WAIT, false);
- waiting = true;
- break;
- case SVC_RESPAWN:
- print_status(svc->desc, STATUS_STARTED, false);
- break;
- case SVC_ONCE:
- singleshot += 1;
- break;
- }
-out:
- if (singleshot == 0 && queue == NULL && !waiting)
+ if (singleshot == 0 && !waiting && queue_idx >= count)
target_completed(target);
- return true;
+out_unblock:
+ sigprocmask(SIG_SETMASK, &old_mask, NULL);
+ return ret;
}