From 364f3bbafbd4b69bc43315f16ec2bafee7e5184e Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Mon, 16 May 2016 14:30:03 +0200 Subject: [PATCH 1/6] finit: add finit state machine Signed-off-by: Jonas Johansson --- Makefile | 4 ++-- finit.c | 7 ++++-- private.h | 1 - service.c | 12 ---------- sm.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sm.h | 46 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 sm.c create mode 100644 sm.h diff --git a/Makefile b/Makefile index 5282ec03..49efa087 100644 --- a/Makefile +++ b/Makefile @@ -34,10 +34,10 @@ ARCHTOOL = `which git-archive-all` ARCHIVE = $(PKG).tar ARCHIVEZ = ../$(ARCHIVE).xz EXEC = finit initctl reboot -HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h cond.h +HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h cond.h sm.h DISTFILES = LICENSE README ChangeLog finit.conf services OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \ - svc.o service.o plugin.o tty.o inetd.o cond.o cond-w.o + svc.o service.o plugin.o tty.o inetd.o cond.o cond-w.o sm.o DEPLIBS = TOPDIR = $(shell pwd) -include config.mk diff --git a/finit.c b/finit.c index 67c936b2..71338f49 100644 --- a/finit.c +++ b/finit.c @@ -39,6 +39,7 @@ #include "tty.h" #include "libite/lite.h" #include "inetd.h" +#include "sm.h" int debug = 0; int quiet = KERNEL_QUIET; /* Delayed disable of verbose mode. */ @@ -53,6 +54,7 @@ char *hostname = NULL; char *rcsd = FINIT_RCSD; char *runparts = NULL; char *console = NULL; +sm_t sm; uev_ctx_t *ctx = NULL; /* Main loop context */ @@ -176,9 +178,10 @@ int main(int argc, char* argv[]) plugin_run_hooks(HOOK_BASEFS_UP); /* - * Start all bootstrap tasks, no network available! + * Initalize finit state machine and start all bootstrap tasks, no network available! */ - service_bootstrap(); + sm_init(&sm); + sm_step(&sm); /* * Network stuff diff --git a/private.h b/private.h index 9f3bde86..60565cc4 100644 --- a/private.h +++ b/private.h @@ -33,7 +33,6 @@ uev_ctx_t *ctx; /* Main loop context */ int api_init (uev_ctx_t *ctx); int client (int argc, char *argv[]); -void service_bootstrap(void); void service_monitor (pid_t lost); void plugin_run_hooks (hook_point_t no); diff --git a/service.c b/service.c index c47634df..e84b9797 100644 --- a/service.c +++ b/service.c @@ -47,18 +47,6 @@ static int in_teardown = 0, in_dyn_teardown = 0; static svc_t *find_inetd_svc (char *path, char *service, char *proto); #endif -/** - * service_bootstrap - Start bootstrap services and tasks - * - * System startup, runlevel S, where only services, tasks and - * run commands absolutely essential to bootstrap are located. - */ -void service_bootstrap(void) -{ - _d("Bootstrapping all services in runlevel S from %s", FINIT_CONF); - service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE); -} - /** * service_enabled - Should the service run? * @svc: Pointer to &svc_t object diff --git a/sm.c b/sm.c new file mode 100644 index 00000000..5c35d159 --- /dev/null +++ b/sm.c @@ -0,0 +1,66 @@ +/* Finit state machine + * + * Copyright (c) 2016 xxx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" /* Generated by configure script */ + +#include + +#include "finit.h" +#include "helpers.h" +#include "service.h" +#include "sm.h" + +void sm_init(sm_t *sm) +{ + sm->state = SM_BOOTSTRAP_STATE; +} + +void sm_step(sm_t *sm) +{ + sm_state_t old_state; + +restart: + old_state = sm->state; + + switch(sm->state) { + case SM_BOOTSTRAP_STATE: + _d("Bootstrapping all services in runlevel S from %s", FINIT_CONF); + service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE); + sm->state = SM_RUNNING_STATE; + break; + + case SM_RUNNING_STATE: + break; + } + + if (sm->state != old_state) { + goto restart; + } +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/sm.h b/sm.h new file mode 100644 index 00000000..2450b809 --- /dev/null +++ b/sm.h @@ -0,0 +1,46 @@ +/* Finit state machine + * + * Copyright (c) 2016 xxx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef FINIT_SM_H_ +#define FINIT_SM_H_ + +typedef enum { + SM_BOOTSTRAP_STATE = 0, + SM_RUNNING_STATE, +} sm_state_t; + +typedef struct sm { + sm_state_t state; +} sm_t; + +void sm_init(sm_t *sm); +void sm_step(sm_t *sm); + +#endif /* FINIT_SM_H_ */ + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ From 60df2043acc912555b0f239fb4c14fcfa1943c5a Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Tue, 17 May 2016 16:40:21 +0200 Subject: [PATCH 2/6] sm: handle runlevel change Signed-off-by: Jonas Johansson --- finit.h | 2 ++ service.c | 57 +++------------------------------- service.h | 2 ++ sm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sm.h | 4 +++ 5 files changed, 105 insertions(+), 52 deletions(-) diff --git a/finit.h b/finit.h index 664bb679..c67b11a5 100644 --- a/finit.h +++ b/finit.h @@ -34,6 +34,7 @@ #include #include +#include "sm.h" #include "libite/lite.h" #define CMD_SIZE 256 @@ -78,6 +79,7 @@ extern char *username; extern char *runparts; extern char *console; extern char *__progname; +extern sm_t sm; #endif /* FINIT_H_ */ diff --git a/service.c b/service.c index e84b9797..4a8b0eb9 100644 --- a/service.c +++ b/service.c @@ -38,6 +38,7 @@ #include "tty.h" #include "service.h" #include "inetd.h" +#include "sm.h" #define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */ @@ -73,7 +74,7 @@ int service_enabled(svc_t *svc) * Returns: * 1, if all stopped services have been collected. 0 otherwise. */ -static int service_stop_is_done(void) +int service_stop_is_done(void) { svc_t *svc; @@ -392,34 +393,8 @@ void service_reload_dynamic(void) */ static void service_runlevel_finish(void) { - /* Prev runlevel services stopped, call hooks before starting new runlevel ... */ - _d("All services have been stoppped, calling runlevel change hooks ..."); - plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ - - _d("Starting services services new to this runlevel ..."); in_teardown = 0; - service_step_all(SVC_TYPE_ANY); - - /* Cleanup stale services */ - svc_clean_dynamic(service_unregister); - - if (0 == runlevel) { - do_shutdown(SIGUSR2); - return; - } - if (6 == runlevel) { - do_shutdown(SIGUSR1); - return; - } - - if (runlevel == 1) - touch("/etc/nologin"); /* Disable login in single-user mode */ - else - erase("/etc/nologin"); - - /* No TTYs run at bootstrap, they have a delayed start. */ - if (prevlevel > 0) - tty_runlevel(runlevel); + sm_step(&sm); } /** @@ -431,31 +406,9 @@ static void service_runlevel_finish(void) */ void service_runlevel(int newlevel) { - if (runlevel == newlevel) - return; - - if (newlevel < 0 || newlevel > 9) - return; - - prevlevel = runlevel; - runlevel = newlevel; - - _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); - runlevel_set(prevlevel, newlevel); - - /* Make sure to (re)load all *.conf in /etc/finit.d/ */ - conf_reload_dynamic(); - - _d("Stopping services services not allowed in new runlevel ..."); in_teardown = 1; - service_step_all(SVC_TYPE_ANY); - - /* Need to wait for any services to stop? If so, exit early - * and perform second stage from service_monitor later. */ - if (!service_stop_is_done()) - return; - - service_runlevel_finish(); + sm_set_runlevel(&sm, newlevel); + sm_step(&sm); } /** diff --git a/service.h b/service.h index 7e68f225..f042cf95 100644 --- a/service.h +++ b/service.h @@ -37,6 +37,8 @@ void service_reload_dynamic (void); void service_step(svc_t *svc); void service_step_all(int types); +int service_stop_is_done(void); + #endif /* FINIT_SERVICE_H_ */ /** diff --git a/sm.c b/sm.c index 5c35d159..8aad6853 100644 --- a/sm.c +++ b/sm.c @@ -26,13 +26,39 @@ #include #include "finit.h" +#include "conf.h" #include "helpers.h" +#include "private.h" #include "service.h" +#include "sig.h" +#include "tty.h" #include "sm.h" void sm_init(sm_t *sm) { sm->state = SM_BOOTSTRAP_STATE; + sm->newlevel = -1; +} + +void sm_set_runlevel(sm_t *sm, int newlevel) +{ + sm->newlevel = newlevel; +} + +char *sm_status(sm_t *sm) +{ + switch (sm->state) { + case SM_BOOTSTRAP_STATE: + return "bootstrap"; + case SM_RUNNING_STATE: + return "running"; + case SM_RUNLEVEL_CHANGE_STATE: + return "runlevel/change"; + case SM_RUNLEVEL_WAIT_STOP_STATE: + return "runlevel/wait"; + default: + return "unknown"; + } } void sm_step(sm_t *sm) @@ -42,6 +68,8 @@ void sm_step(sm_t *sm) restart: old_state = sm->state; + _d("state: %s", sm_status(sm)); + switch(sm->state) { case SM_BOOTSTRAP_STATE: _d("Bootstrapping all services in runlevel S from %s", FINIT_CONF); @@ -50,6 +78,70 @@ restart: break; case SM_RUNNING_STATE: + /* runlevel changed? */ + if (sm->newlevel >= 0 && sm->newlevel <= 9) { + if (runlevel == sm->newlevel) { + sm->newlevel = -1; + break; + } + sm->state = SM_RUNLEVEL_CHANGE_STATE; + break; + } + + case SM_RUNLEVEL_CHANGE_STATE: + prevlevel = runlevel; + runlevel = sm->newlevel; + sm->newlevel = -1; + + _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); + runlevel_set(prevlevel, runlevel); + + /* Make sure to (re)load all *.conf in /etc/finit.d/ */ + conf_reload_dynamic(); + + _d("Stopping services services not allowed in new runlevel ..."); + service_step_all(SVC_TYPE_ANY); + + sm->state = SM_RUNLEVEL_WAIT_STOP_STATE; + break; + + case SM_RUNLEVEL_WAIT_STOP_STATE: + /* Need to wait for any services to stop? If so, exit early + * and perform second stage from service_monitor later. */ + if (!service_stop_is_done()) + break; + + /* Prev runlevel services stopped, call hooks before starting new runlevel ... */ + _d("All services have been stoppped, calling runlevel change hooks ..."); + plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ + + _d("Starting services services new to this runlevel ..."); + service_step_all(SVC_TYPE_ANY); + + /* Cleanup stale services */ + svc_clean_dynamic(service_unregister); + + if (0 == runlevel) { + do_shutdown(SIGUSR2); + sm->state = SM_RUNNING_STATE; + break; + } + if (6 == runlevel) { + do_shutdown(SIGUSR1); + sm->state = SM_RUNNING_STATE; + break; + } + + if (runlevel == 1) + touch("/etc/nologin"); /* Disable login in single-user mode */ + else + erase("/etc/nologin"); + + /* No TTYs run at bootstrap, they have a delayed start. */ + if (prevlevel > 0) + tty_runlevel(runlevel); + + sm->state = SM_RUNNING_STATE; break; } diff --git a/sm.h b/sm.h index 2450b809..61544352 100644 --- a/sm.h +++ b/sm.h @@ -27,14 +27,18 @@ typedef enum { SM_BOOTSTRAP_STATE = 0, SM_RUNNING_STATE, + SM_RUNLEVEL_CHANGE_STATE, + SM_RUNLEVEL_WAIT_STOP_STATE, } sm_state_t; typedef struct sm { sm_state_t state; + int newlevel; } sm_t; void sm_init(sm_t *sm); void sm_step(sm_t *sm); +void sm_set_runlevel(sm_t *sm, int newlevel); #endif /* FINIT_SM_H_ */ From 29315090f99b5f30cb23ebe6504eb9503aa5df50 Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Wed, 18 May 2016 10:24:37 +0200 Subject: [PATCH 3/6] sm: handle reload event Signed-off-by: Jonas Johansson --- service.c | 31 +++-------------------------- sm.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++---- sm.h | 12 +++++++---- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/service.c b/service.c index 4a8b0eb9..5e22e27f 100644 --- a/service.c +++ b/service.c @@ -342,18 +342,7 @@ static int service_restart(svc_t *svc) static void service_reload_dynamic_finish(void) { in_dyn_teardown = 0; - - /* Cleanup stale services */ - svc_clean_dynamic(service_unregister); - - _d("Starting services after reconf ..."); - service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); - - _d("Calling reconf hooks ..."); - plugin_run_hooks(HOOK_SVC_RECONF); - - service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); - _d("Reconfiguration done"); + sm_step(&sm); } /** @@ -365,23 +354,9 @@ static void service_reload_dynamic_finish(void) */ void service_reload_dynamic(void) { - /* First reload all *.conf in /etc/finit.d/ */ - conf_reload_dynamic(); - - /* Then, mark all affected service conditions as in-flux and - * let all affected services move to WAITING/HALTED */ - _d("Stopping services services not allowed after reconf ..."); in_dyn_teardown = 1; - cond_reload(); - service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); - - /* Need to wait for any services to stop? If so, exit early - * and perform second stage from service_monitor later. */ - if (!service_stop_is_done()) - return; - - /* Otherwise, kick all svcs again right away */ - service_reload_dynamic_finish(); + sm_set_reload(&sm); + sm_step(&sm); } /** diff --git a/sm.c b/sm.c index 8aad6853..222f9a92 100644 --- a/sm.c +++ b/sm.c @@ -26,6 +26,7 @@ #include #include "finit.h" +#include "cond.h" #include "conf.h" #include "helpers.h" #include "private.h" @@ -38,6 +39,7 @@ void sm_init(sm_t *sm) { sm->state = SM_BOOTSTRAP_STATE; sm->newlevel = -1; + sm->reload = 0; } void sm_set_runlevel(sm_t *sm, int newlevel) @@ -45,6 +47,11 @@ void sm_set_runlevel(sm_t *sm, int newlevel) sm->newlevel = newlevel; } +void sm_set_reload(sm_t *sm) +{ + sm->reload = 1; +} + char *sm_status(sm_t *sm) { switch (sm->state) { @@ -54,8 +61,12 @@ char *sm_status(sm_t *sm) return "running"; case SM_RUNLEVEL_CHANGE_STATE: return "runlevel/change"; - case SM_RUNLEVEL_WAIT_STOP_STATE: + case SM_RUNLEVEL_WAIT_STATE: return "runlevel/wait"; + case SM_RELOAD_CHANGE_STATE: + return "reload/change"; + case SM_RELOAD_WAIT_STATE: + return "reload/wait"; default: return "unknown"; } @@ -70,7 +81,7 @@ restart: _d("state: %s", sm_status(sm)); - switch(sm->state) { + switch (sm->state) { case SM_BOOTSTRAP_STATE: _d("Bootstrapping all services in runlevel S from %s", FINIT_CONF); service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE); @@ -87,6 +98,12 @@ restart: sm->state = SM_RUNLEVEL_CHANGE_STATE; break; } + /* reload ? */ + if (sm->reload) { + sm->reload = 0; + sm->state = SM_RELOAD_CHANGE_STATE; + } + break; case SM_RUNLEVEL_CHANGE_STATE: prevlevel = runlevel; @@ -102,10 +119,10 @@ restart: _d("Stopping services services not allowed in new runlevel ..."); service_step_all(SVC_TYPE_ANY); - sm->state = SM_RUNLEVEL_WAIT_STOP_STATE; + sm->state = SM_RUNLEVEL_WAIT_STATE; break; - case SM_RUNLEVEL_WAIT_STOP_STATE: + case SM_RUNLEVEL_WAIT_STATE: /* Need to wait for any services to stop? If so, exit early * and perform second stage from service_monitor later. */ if (!service_stop_is_done()) @@ -143,6 +160,40 @@ restart: sm->state = SM_RUNNING_STATE; break; + + case SM_RELOAD_CHANGE_STATE: + /* First reload all *.conf in /etc/finit.d/ */ + conf_reload_dynamic(); + + /* Then, mark all affected service conditions as in-flux and + * let all affected services move to WAITING/HALTED */ + _d("Stopping services services not allowed after reconf ..."); + cond_reload(); + service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); + + sm->state = SM_RELOAD_WAIT_STATE; + break; + + case SM_RELOAD_WAIT_STATE: + /* Need to wait for any services to stop? If so, exit early + * and perform second stage from service_monitor later. */ + if (!service_stop_is_done()) + break; + + /* Cleanup stale services */ + svc_clean_dynamic(service_unregister); + + _d("Starting services after reconf ..."); + service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); + + _d("Calling reconf hooks ..."); + plugin_run_hooks(HOOK_SVC_RECONF); + + service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); + _d("Reconfiguration done"); + + sm->state = SM_RUNNING_STATE; + break; } if (sm->state != old_state) { diff --git a/sm.h b/sm.h index 61544352..9f158ffa 100644 --- a/sm.h +++ b/sm.h @@ -28,17 +28,21 @@ typedef enum { SM_BOOTSTRAP_STATE = 0, SM_RUNNING_STATE, SM_RUNLEVEL_CHANGE_STATE, - SM_RUNLEVEL_WAIT_STOP_STATE, + SM_RUNLEVEL_WAIT_STATE, + SM_RELOAD_CHANGE_STATE, + SM_RELOAD_WAIT_STATE, } sm_state_t; typedef struct sm { sm_state_t state; int newlevel; + int reload; } sm_t; -void sm_init(sm_t *sm); -void sm_step(sm_t *sm); -void sm_set_runlevel(sm_t *sm, int newlevel); +void sm_init(sm_t *sm); +void sm_step(sm_t *sm); +void sm_set_runlevel(sm_t *sm, int newlevel); +void sm_set_reload(sm_t *sm); #endif /* FINIT_SM_H_ */ From c1f725340d82dfc56f7b2e02b5310ac8ecce6f95 Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Thu, 19 May 2016 13:39:56 +0200 Subject: [PATCH 4/6] plugin/pidfile: also notify on modified pidfiles Signed-off-by: Jonas Johansson --- plugins/pidfile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/pidfile.c b/plugins/pidfile.c index f9db3126..497a5fe4 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -47,7 +47,7 @@ static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events)) _d("%s: match %s", basename, svc->cmd); snprintf(cond, sizeof(cond), "svc%s", svc->cmd); - if (ev->mask & (IN_CREATE | IN_ATTRIB)) { + if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) { svc_started(svc); cond_set(cond); } else if (ev->mask & IN_DELETE) @@ -81,7 +81,7 @@ static void pidfile_init(void *arg) struct context *ctx = arg; ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN, - IN_CREATE | IN_ATTRIB | IN_DELETE); + IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY); if (ctx->wd < 0) { _pe("inotify_add_watch()"); close(ctx->fd); From c5fad8e6593bebd74c1001cd309364ec9d6ae4e2 Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Thu, 19 May 2016 14:33:14 +0200 Subject: [PATCH 5/6] services: do not start services before all stopped processes has been halted Signed-off-by: Jonas Johansson --- service.c | 63 +++++++------------------------------------------------ sm.c | 32 ++++++++++++++++++---------- sm.h | 20 ++++++++++-------- 3 files changed, 40 insertions(+), 75 deletions(-) diff --git a/service.c b/service.c index 5e22e27f..09360376 100644 --- a/service.c +++ b/service.c @@ -42,8 +42,6 @@ #define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */ -static int in_teardown = 0, in_dyn_teardown = 0; - #ifndef INETD_DISABLED static svc_t *find_inetd_svc (char *path, char *service, char *proto); #endif @@ -332,19 +330,6 @@ static int service_restart(svc_t *svc) return err; } -/** - * service_reload_dynamic_finish - Finish dynamic service reload - * - * Second stage of dynamic reload. Called either directly from first - * stage if no services had to be stopped, or later from - * service_monitor once all stopped services have been collected. - */ -static void service_reload_dynamic_finish(void) -{ - in_dyn_teardown = 0; - sm_step(&sm); -} - /** * service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload' * @@ -354,24 +339,10 @@ static void service_reload_dynamic_finish(void) */ void service_reload_dynamic(void) { - in_dyn_teardown = 1; sm_set_reload(&sm); sm_step(&sm); } -/** - * service_runlevel_finish - Finish runlevel change - * - * Second stage of runlevel change. Called either directly from first - * stage if no services had to be stopped, or later from - * service_monitor once all stopped services have been collected. - */ -static void service_runlevel_finish(void) -{ - in_teardown = 0; - sm_step(&sm); -} - /** * service_runlevel - Change to a new runlevel * @newlevel: New runlevel to activate @@ -381,7 +352,6 @@ static void service_runlevel_finish(void) */ void service_runlevel(int newlevel) { - in_teardown = 1; sm_set_runlevel(&sm, newlevel); sm_step(&sm); } @@ -627,29 +597,6 @@ void service_unregister(svc_t *svc) svc_del(svc); } -/** - * service_teardown_finish - Complete runlevel change or dynamic reload - * - * If any runlevel change or dynamic service reload is in progress and - * all services that had to be stopped have been collected, run the - * corresponding second stage. - */ -static void service_teardown_finish(void) -{ - if (!(in_teardown || in_dyn_teardown)) - return; - - if (!service_stop_is_done()) - return; - - if (in_teardown) - service_runlevel_finish(); - - if (in_dyn_teardown) - service_reload_dynamic_finish(); -} - - void service_monitor(pid_t lost) { svc_t *svc; @@ -676,8 +623,7 @@ void service_monitor(pid_t lost) svc->pid = 0; service_step(svc); - /* Check if we're still collecting stopped dynamic services */ - service_teardown_finish(); + sm_step(&sm); } static void svc_set_state(svc_t *svc, svc_state_t new) @@ -757,6 +703,10 @@ restart: break; } + /* wait until all processes has been stopped before continuing... */ + if (sm_is_in_teardown(&sm)) + break; + err = service_start(svc); if (err) { (*restart_counter)++; @@ -813,6 +763,9 @@ restart: case COND_ON: if (svc_is_changed(svc)) { if (svc->sighup) { + /* wait until all processes has been stopped before continuing... */ + if (sm_is_in_teardown(&sm)) + break; service_restart(svc); } else { service_stop(svc); diff --git a/sm.c b/sm.c index 222f9a92..288473cf 100644 --- a/sm.c +++ b/sm.c @@ -40,19 +40,10 @@ void sm_init(sm_t *sm) sm->state = SM_BOOTSTRAP_STATE; sm->newlevel = -1; sm->reload = 0; + sm->in_teardown = 0; } -void sm_set_runlevel(sm_t *sm, int newlevel) -{ - sm->newlevel = newlevel; -} - -void sm_set_reload(sm_t *sm) -{ - sm->reload = 1; -} - -char *sm_status(sm_t *sm) +static char *sm_status(sm_t *sm) { switch (sm->state) { case SM_BOOTSTRAP_STATE: @@ -72,6 +63,21 @@ char *sm_status(sm_t *sm) } } +void sm_set_runlevel(sm_t *sm, int newlevel) +{ + sm->newlevel = newlevel; +} + +void sm_set_reload(sm_t *sm) +{ + sm->reload = 1; +} + +int sm_is_in_teardown(sm_t *sm) +{ + return sm->in_teardown; +} + void sm_step(sm_t *sm) { sm_state_t old_state; @@ -117,6 +123,7 @@ restart: conf_reload_dynamic(); _d("Stopping services services not allowed in new runlevel ..."); + sm->in_teardown = 1; service_step_all(SVC_TYPE_ANY); sm->state = SM_RUNLEVEL_WAIT_STATE; @@ -133,6 +140,7 @@ restart: plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ _d("Starting services services new to this runlevel ..."); + sm->in_teardown = 0; service_step_all(SVC_TYPE_ANY); /* Cleanup stale services */ @@ -168,6 +176,7 @@ restart: /* Then, mark all affected service conditions as in-flux and * let all affected services move to WAITING/HALTED */ _d("Stopping services services not allowed after reconf ..."); + sm->in_teardown = 1; cond_reload(); service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD); @@ -180,6 +189,7 @@ restart: if (!service_stop_is_done()) break; + sm->in_teardown = 0; /* Cleanup stale services */ svc_clean_dynamic(service_unregister); diff --git a/sm.h b/sm.h index 9f158ffa..9bb8d584 100644 --- a/sm.h +++ b/sm.h @@ -25,24 +25,26 @@ #define FINIT_SM_H_ typedef enum { - SM_BOOTSTRAP_STATE = 0, - SM_RUNNING_STATE, - SM_RUNLEVEL_CHANGE_STATE, - SM_RUNLEVEL_WAIT_STATE, - SM_RELOAD_CHANGE_STATE, - SM_RELOAD_WAIT_STATE, + SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */ + SM_RUNNING_STATE, /* Normal state, services running */ + SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occured */ + SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */ + SM_RELOAD_CHANGE_STATE, /* A reload event has occured */ + SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */ } sm_state_t; typedef struct sm { - sm_state_t state; - int newlevel; - int reload; + sm_state_t state; /* Running, Changed, Waiting, ... */ + int newlevel; /* Set on runlevel change to new runlevel, -1 if not change */ + int reload; /* Set on reload event, else 0 */ + int in_teardown; /* Set when waiting for all processes to be halted */ } sm_t; void sm_init(sm_t *sm); void sm_step(sm_t *sm); void sm_set_runlevel(sm_t *sm, int newlevel); void sm_set_reload(sm_t *sm); +int sm_is_in_teardown(sm_t *sm); #endif /* FINIT_SM_H_ */ From ad2bafbfe8bc2f0b4817edf6988d8791ede9be5b Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Fri, 20 May 2016 09:08:38 +0200 Subject: [PATCH 6/6] services: remove .pid file for (careless) died processes Signed-off-by: Jonas Johansson --- service.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service.c b/service.c index 09360376..47254cca 100644 --- a/service.c +++ b/service.c @@ -600,6 +600,7 @@ void service_unregister(svc_t *svc) void service_monitor(pid_t lost) { svc_t *svc; + char pidfile[MAX_ARG_LEN]; if (fexist(SYNC_SHUTDOWN) || lost <= 1) return; @@ -619,6 +620,10 @@ void service_monitor(pid_t lost) _d("collected %s(%d)", svc->cmd, lost); + /* Remove pid file (in case service is careless) */ + snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd)); + remove(pidfile); + /* No longer running, update books. */ svc->pid = 0; service_step(svc);