From 60df2043acc912555b0f239fb4c14fcfa1943c5a Mon Sep 17 00:00:00 2001 From: Jonas Johansson Date: Tue, 17 May 2016 16:40:21 +0200 Subject: [PATCH] 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_ */