summaryrefslogtreecommitdiff
path: root/initd/supervisor.c
diff options
context:
space:
mode:
Diffstat (limited to 'initd/supervisor.c')
-rw-r--r--initd/supervisor.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/initd/supervisor.c b/initd/supervisor.c
new file mode 100644
index 0000000..f439f6d
--- /dev/null
+++ b/initd/supervisor.c
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * Copyright (C) 2018 - David Oberhollenzer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "init.h"
+
+static service_list_t cfg;
+
+static int target = -1;
+static service_t *running = NULL;
+static service_t *terminated = NULL;
+static service_t *queue = NULL;
+static int singleshot = 0;
+static bool waiting = false;
+
+static int start_service(service_t *svc)
+{
+ svc->pid = runsvc(svc);
+ if (svc->pid == -1) {
+ print_status(svc->desc, STATUS_FAIL, false);
+ delsvc(svc);
+ return -1;
+ }
+
+ svc->next = running;
+ running = svc;
+ return 0;
+}
+
+static void handle_terminated_service(service_t *svc)
+{
+ switch (svc->type) {
+ case SVC_RESPAWN:
+ if (target == TGT_REBOOT || target == TGT_SHUTDOWN)
+ break;
+
+ if (svc->rspwn_limit > 0) {
+ svc->rspwn_limit -= 1;
+
+ if (svc->rspwn_limit == 0) {
+ print_status(svc->desc, STATUS_FAIL, false);
+ break;
+ }
+ }
+
+ 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);
+ 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);
+ break;
+ }
+ delsvc(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;
+ }
+
+ if (svc == NULL)
+ return;
+
+ if (prev != NULL) {
+ prev->next = svc->next;
+ } else {
+ running = svc->next;
+ }
+
+ svc->status = status;
+ svc->next = terminated;
+ terminated = svc;
+}
+
+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 != 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;
+}
+
+void supervisor_init(void)
+{
+ int status = STATUS_OK;
+
+ if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY))
+ status = STATUS_FAIL;
+
+ target = TGT_BOOT;
+ queue = cfg.targets[TGT_BOOT];
+ cfg.targets[TGT_BOOT] = NULL;
+
+ print_status("reading configuration from " SVCDIR, status, false);
+}
+
+bool supervisor_process_queues(void)
+{
+ 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 (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;
+ }
+
+ if (singleshot == 0 && queue == NULL && !waiting)
+ target_completed(target);
+ return true;
+}