From ec74e5bbee30381b1624ac2247e74fa29676c6e6 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 24 Aug 2018 16:51:15 +0200 Subject: [PATCH] initd: restructure and merge service supervision code Signed-off-by: David Oberhollenzer --- initd/Makemodule.am | 2 +- initd/init.h | 40 +++++----- initd/main.c | 153 +++++++++---------------------------- initd/runsvc.c | 15 ---- initd/supervisor.c | 182 ++++++++++++++++++++++++++++++++++++++++++++ initd/svclist.c | 60 --------------- 6 files changed, 238 insertions(+), 214 deletions(-) create mode 100644 initd/supervisor.c delete mode 100644 initd/svclist.c diff --git a/initd/Makemodule.am b/initd/Makemodule.am index f740885..54ae43d 100644 --- a/initd/Makemodule.am +++ b/initd/Makemodule.am @@ -1,5 +1,5 @@ init_SOURCES = initd/main.c initd/init.h initd/signal_linux.c initd/runsvc.c -init_SOURCES += initd/status.c initd/svclist.c +init_SOURCES += initd/status.c initd/supervisor.c init_CPPFLAGS = $(AM_CPPFLAGS) init_CFLAGS = $(AM_CFLAGS) init_LDFLAGS = $(AM_LDFLAGS) diff --git a/initd/init.h b/initd/init.h index 2c39489..7d525a2 100644 --- a/initd/init.h +++ b/initd/init.h @@ -18,6 +18,16 @@ #ifndef INIT_H #define INIT_H +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -36,6 +46,10 @@ enum { STATUS_STARTED, }; +/********** main.c **********/ + +void target_completed(int target); + /********** runsvc.c **********/ /* @@ -45,12 +59,6 @@ enum { */ pid_t runsvc(service_t *svc); -/* - Start a service using runsvc, but wait until the child process - terminats and return its exit status. -*/ -int runsvc_wait(service_t *svc); - /********** status.c **********/ /* @@ -64,25 +72,15 @@ int runsvc_wait(service_t *svc); */ void print_status(const char *msg, int type, bool update); -/********** svclist.c **********/ +/********** supervisor.c **********/ -/* - Returns true if the list of running services contains - single shot processes. -*/ -bool svclist_have_singleshot(void); +void supervisor_handle_exited(pid_t pid, int status); -/* Add a service to the list of running services */ -void svclist_add(service_t *svc); +void supervisor_set_target(int next); -/* - Remove a service, identifierd by PID, from the list of - running services. +void supervisor_init(void); - Returns the service identified by the PID or NULL if there - is no such service. -*/ -service_t *svclist_remove(pid_t pid); +bool supervisor_process_queues(void); /********** signal_.c **********/ diff --git a/initd/main.c b/initd/main.c index 2cacf12..78e8635 100644 --- a/initd/main.c +++ b/initd/main.c @@ -15,60 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "init.h" -static service_list_t cfg; +static int ti_sock = -1; +static int sigfd = -1; -static int target = TGT_BOOT; /* runlevel we are targetting */ -static int runlevel = -1; /* runlevel we are currently on */ - -static void handle_exited(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; - } - } - - svc->pid = runsvc(svc); - if (svc->pid == -1) { - print_status(svc->desc, STATUS_FAIL, false); - break; - } - - svclist_add(svc); - return; - case SVC_ONCE: - print_status(svc->desc, - svc->status == EXIT_SUCCESS ? - STATUS_OK : STATUS_FAIL, false); - break; - } - delsvc(svc); -} - -static void handle_signal(int sigfd) +static void handle_signal(void) { struct signalfd_siginfo info; - service_t *svc; int status; pid_t pid; @@ -83,10 +37,7 @@ static void handle_signal(int sigfd) status = WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; - svc = svclist_remove(pid); - - if (svc != NULL) - handle_exited(svc); + supervisor_handle_exited(pid, status); } break; case SIGINT: @@ -95,41 +46,6 @@ static void handle_signal(int sigfd) } } -static void start_runlevel(int level) -{ - service_t *svc; - int status; - - while (cfg.targets[level] != NULL) { - svc = cfg.targets[level]; - cfg.targets[level] = svc->next; - - if (svc->type == SVC_WAIT) { - print_status(svc->desc, STATUS_WAIT, false); - - status = runsvc_wait(svc); - - print_status(svc->desc, - status == EXIT_SUCCESS ? - STATUS_OK : STATUS_FAIL, - true); - delsvc(svc); - } else { - svc->pid = runsvc(svc); - if (svc->pid == -1) { - print_status(svc->desc, STATUS_FAIL, false); - delsvc(svc); - continue; - } - - if (svc->type == SVC_RESPAWN) - print_status(svc->desc, STATUS_STARTED, false); - - svclist_add(svc); - } - } -} - static int read_msg(int fd, ti_msg_t *msg) { ssize_t ret; @@ -151,7 +67,7 @@ retry: return 0; } -static void handle_tellinit(int ti_sock) +static void handle_tellinit(void) { ti_msg_t msg; int fd; @@ -167,19 +83,31 @@ static void handle_tellinit(int ti_sock) switch (msg.type) { case TI_SHUTDOWN: - target = TGT_SHUTDOWN; + supervisor_set_target(TGT_SHUTDOWN); break; case TI_REBOOT: - target = TGT_REBOOT; + supervisor_set_target(TGT_REBOOT); break; } close(fd); } +void target_completed(int target) +{ + switch (target) { + case TGT_BOOT: + if (ti_sock == -1) + ti_sock = mksock(INITSOCK, SOCK_FLAG_ROOT_ONLY); + break; + default: + break; + } +} + int main(void) { - int ti_sock = -1, sfd, ret, count; + int ret, count; struct pollfd pfd[2]; if (getpid() != 1) { @@ -187,44 +115,35 @@ int main(void) return EXIT_FAILURE; } - if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY)) { - fputs("Error reading service list from " SVCDIR "\n" - "Trying to continue anyway\n", stderr); - } + supervisor_init(); - sfd = sigsetup(); - if (sfd < 0) + sigfd = sigsetup(); + if (sigfd < 0) return -1; - memset(pfd, 0, sizeof(pfd)); - pfd[0].fd = sfd; - pfd[0].events = POLLIN; - count = 1; - for (;;) { - if (!svclist_have_singleshot() && target != runlevel) { - start_runlevel(target); - runlevel = target; + while (supervisor_process_queues()) + ; - if (target == TGT_BOOT && ti_sock == -1) { - ti_sock = mksock(INITSOCK, SOCK_FLAG_ROOT_ONLY); - if (ti_sock != -1) { - pfd[1].fd = ti_sock; - pfd[1].events = POLLIN; - count = 2; - } - } - continue; + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = sigfd; + pfd[0].events = POLLIN; + count = 1; + + if (ti_sock != -1) { + pfd[1].fd = ti_sock; + pfd[1].events = POLLIN; + count = 2; } ret = poll(pfd, count, -1); if (ret > 0) { if (pfd[0].revents & POLLIN) - handle_signal(sfd); + handle_signal(); if (ti_sock != -1 && pfd[1].revents & POLLIN) - handle_tellinit(ti_sock); + handle_tellinit(); } } diff --git a/initd/runsvc.c b/initd/runsvc.c index aba83b1..1b23cb6 100644 --- a/initd/runsvc.c +++ b/initd/runsvc.c @@ -49,18 +49,3 @@ pid_t runsvc(service_t *svc) return pid; } - -int runsvc_wait(service_t *svc) -{ - pid_t ret, pid = runsvc(svc); - int status; - - if (pid == -1) - return EXIT_FAILURE; - - do { - ret = waitpid(pid, &status, 0); - } while (ret != pid); - - return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; -} 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 . + */ +#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; +} diff --git a/initd/svclist.c b/initd/svclist.c deleted file mode 100644 index 4ed897f..0000000 --- a/initd/svclist.c +++ /dev/null @@ -1,60 +0,0 @@ -/* 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 . - */ -#include "init.h" - -static service_t *running = NULL; /* currently supervised services */ -static int singleshot = 0; /* active singleshot services */ - -bool svclist_have_singleshot(void) -{ - return singleshot > 0; -} - -void svclist_add(service_t *svc) -{ - svc->next = running; - running = svc; - - if (svc->type == SVC_ONCE) - singleshot += 1; -} - -service_t *svclist_remove(pid_t pid) -{ - service_t *prev = NULL, *svc = running; - - while (svc != NULL) { - if (svc->pid == pid) { - if (prev != NULL) { - prev->next = svc->next; - } else { - running = svc->next; - } - svc->next = NULL; - - if (svc->type == SVC_ONCE) - singleshot -= 1; - break; - } - - prev = svc; - svc = svc->next; - } - - return svc; -}