Refactor state machine control functions

This refactor should have been done years ago when we first introduced
the big state machine.  The old functions service_runvel() and the oddly
named service_reload_dynamic() are actually state machine control fns,
so let relocate them.

Also in this commit, remove the global variable 'sm' and rename a few of
the critical functions to better names.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2025-07-06 03:13:56 +02:00
1 parent 39f4aa3f2c
commit 1b9e69414e
10 files changed
+135 -138

No files matched your search

+7 -6
View File
@@ -47,6 +47,7 @@
#include "private.h" #include "private.h"
#include "schedule.h" #include "schedule.h"
#include "service.h" #include "service.h"
#include "sm.h"
#include "sig.h" #include "sig.h"
#include "util.h" #include "util.h"
@@ -275,19 +276,19 @@ static int do_reboot(int cmd, int timeout, char *buf, size_t len)
case INIT_CMD_REBOOT: case INIT_CMD_REBOOT:
dbg("reboot"); dbg("reboot");
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); sm_runlevel(6);
break; break;
case INIT_CMD_HALT: case INIT_CMD_HALT:
dbg("halt"); dbg("halt");
halt = SHUT_HALT; halt = SHUT_HALT;
service_runlevel(0); sm_runlevel(0);
break; break;
case INIT_CMD_POWEROFF: case INIT_CMD_POWEROFF:
dbg("poweroff"); dbg("poweroff");
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); sm_runlevel(0);
break; break;
case INIT_CMD_SUSPEND: case INIT_CMD_SUSPEND:
@@ -316,7 +317,7 @@ typedef struct {
} ev_t; } ev_t;
ev_t ev_list[] = { ev_t ev_list[] = {
{ "RELOAD", service_reload_dynamic }, { "RELOAD", sm_reload },
{ NULL, NULL } { NULL, NULL }
}; };
@@ -437,7 +438,7 @@ static void api_cb(uev_t *w, void *arg, int events)
if (runlevel == INIT_LEVEL) if (runlevel == INIT_LEVEL)
cfglevel = lvl; cfglevel = lvl;
else else
service_runlevel(lvl); sm_runlevel(lvl);
break; break;
default: default:
@@ -453,7 +454,7 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
dbg("reload"); dbg("reload");
service_reload_dynamic(); sm_reload();
break; break;
case INIT_CMD_START_SVC: case INIT_CMD_START_SVC:
+1 -1
View File
@@ -303,7 +303,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events)
#ifdef AUTO_RELOAD #ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); sm_reload();
#endif #endif
} }
+1 -1
View File
@@ -218,7 +218,7 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
err(1, "Failed removing condition %s", fpath); err(1, "Failed removing condition %s", fpath);
cond = ptr + strlen(COND_BASE) + 1; cond = ptr + strlen(COND_BASE) + 1;
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
cond_clear_noupdate(cond); cond_clear_noupdate(cond);
else else
cond_update(cond); cond_update(cond);
+1 -1
View File
@@ -1577,7 +1577,7 @@ static void conf_cb(uev_t *w, void *arg, int events)
#ifdef AUTO_RELOAD #ifdef AUTO_RELOAD
if (conf_any_change()) if (conf_any_change())
service_reload_dynamic(); sm_reload();
#endif #endif
} }
+2 -2
View File
@@ -745,8 +745,8 @@ int main(int argc, char *argv[])
* Initialize state machine and start all bootstrap tasks * Initialize state machine and start all bootstrap tasks
* NOTE: no network available! * NOTE: no network available!
*/ */
sm_init(&sm); sm_init();
sm_step(&sm); sm_step();
/* /*
* Enter main loop to monitor /dev/initctl and services * Enter main loop to monitor /dev/initctl and services
+7 -38
View File
@@ -1182,38 +1182,6 @@ static int service_restart(svc_t *svc)
return rc; return rc;
} }
/**
* service_reload_dynamic - Called on SIGHUP, 'init q' or 'initctl reload'
*
* This function is called when Finit has received SIGHUP to reload
* .conf files in /etc/finit.d. It is responsible for starting,
* stopping and reloading (forwarding SIGHUP) to processes affected.
*/
void service_reload_dynamic(void)
{
sm_set_reload(&sm);
sm_step(&sm);
}
/**
* service_runlevel - Change to a new runlevel
* @newlevel: New runlevel to activate
*
* Stops all services not in @newlevel and starts, or lets continue to run,
* those in @newlevel. Also updates @prevlevel and active @runlevel.
*/
void service_runlevel(int newlevel)
{
if (!rescue && (runlevel == 1 || runlevel == INIT_LEVEL) && !IS_RESERVED_RUNLEVEL(newlevel))
networking(1);
sm_set_runlevel(&sm, newlevel);
sm_step(&sm);
if (!rescue && IS_RESERVED_RUNLEVEL(runlevel))
networking(0);
}
/* /*
* Parse run/task/service arguments with support for quoted strings, both * Parse run/task/service arguments with support for quoted strings, both
* single and double quotes to allow arguments containing spaces. * single and double quotes to allow arguments containing spaces.
@@ -2137,7 +2105,7 @@ cont:
dbg("collected bootstrap task %s(%d), removing.", svc_ident(svc, NULL, 0), lost); dbg("collected bootstrap task %s(%d), removing.", svc_ident(svc, NULL, 0), lost);
} }
sm_step(&sm); sm_step();
} }
static void svc_mark_affected(char *cond) static void svc_mark_affected(char *cond)
@@ -2518,7 +2486,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) || if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) ||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE)) (old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
; /* only paused during reload, don't clear conds. */ ; /* only paused during reload, don't clear conds. */
else if (sm_is_in_teardown(&sm)) else if (sm_in_reload())
cond_clear_noupdate(cond); cond_clear_noupdate(cond);
else else
cond_clear(cond); cond_clear(cond);
@@ -2713,7 +2681,7 @@ restart:
svc_set_state(svc, SVC_HALTED_STATE); svc_set_state(svc, SVC_HALTED_STATE);
} else if (cond_get_agg(svc->cond) == COND_ON) { } else if (cond_get_agg(svc->cond) == COND_ON) {
/* wait until all processes have been stopped before continuing... */ /* wait until all processes have been stopped before continuing... */
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
break; break;
/* Don't start if it conflicts with something else already started */ /* Don't start if it conflicts with something else already started */
@@ -2808,15 +2776,16 @@ restart:
case COND_ON: case COND_ON:
if (svc_is_changed(svc)) { if (svc_is_changed(svc)) {
if (svc_nohup(svc) || !svc_is_daemon(svc)) if (svc_nohup(svc) || !svc_is_daemon(svc)) {
service_stop(svc); service_stop(svc);
else { } else {
/* /*
* wait until all processes have been * wait until all processes have been
* stopped before continuing... * stopped before continuing...
*/ */
if (sm_is_in_teardown(&sm)) if (sm_in_reload())
break; break;
service_restart(svc); service_restart(svc);
} }
-2
View File
@@ -27,12 +27,10 @@
#include "svc.h" #include "svc.h"
void service_runlevel (int newlevel);
int service_register (int type, char *line, struct rlimit rlimit[], char *file); int service_register (int type, char *line, struct rlimit rlimit[], char *file);
void service_unregister (svc_t *svc); void service_unregister (svc_t *svc);
void service_runtask_clean (void); void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void); void service_update_rdeps (void);
void service_mark_unavail (void); void service_mark_unavail (void);
+4 -3
View File
@@ -94,6 +94,7 @@
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "service.h" #include "service.h"
#include "sm.h"
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
@@ -444,7 +445,7 @@ static void sighup_cb(uev_t *w, void *arg, int events)
api_init(w->ctx); api_init(w->ctx);
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */ /* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic(); sm_reload();
} }
/* /*
@@ -528,7 +529,7 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
} }
halt = SHUT_OFF; halt = SHUT_OFF;
service_runlevel(0); sm_runlevel(0);
} }
/* /*
@@ -551,7 +552,7 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
} }
halt = SHUT_REBOOT; halt = SHUT_REBOOT;
service_runlevel(6); sm_runlevel(6);
} }
/* /*
+107 -59
View File
@@ -38,7 +38,24 @@
#include "sm.h" #include "sm.h"
#include "utmp-api.h" #include "utmp-api.h"
sm_t sm; typedef enum {
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
SM_RUNNING_STATE, /* Normal state, services running */
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RUNLEVEL_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
} sm_state_t;
static struct {
sm_state_t state; /* Running, Changed, Waiting, ... */
int newlevel; /* Set to new runlevel on change, -1 otherwise */
int reload; /* Set on reload event, else 0 */
int in_reload; /* Set when waiting for all processes to be halted */
} sm;
#ifndef FINIT_NOLOGIN_PATH #ifndef FINIT_NOLOGIN_PATH
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */ #define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
@@ -90,7 +107,7 @@ static void sm_check_bootstrap(void *work)
} else } else
dbg("Change to default runlevel(%d), starting all services ...", cfglevel); dbg("Change to default runlevel(%d), starting all services ...", cfglevel);
service_runlevel(level); sm_runlevel(level);
/* Clean up bootstrap-only tasks/services that never started */ /* Clean up bootstrap-only tasks/services that never started */
dbg("Clean up all bootstrap-only tasks/services ..."); dbg("Clean up all bootstrap-only tasks/services ...");
@@ -135,7 +152,7 @@ static char *sm_status(sm_state_t state)
return "unknown"; return "unknown";
} }
static char sm_runlevel(int lvl) static char sm_rl2ch(int lvl)
{ {
if (lvl == INIT_LEVEL) if (lvl == INIT_LEVEL)
return 'S'; return 'S';
@@ -158,60 +175,39 @@ static void nologin(void)
erase(FINIT_NOLOGIN_PATH); erase(FINIT_NOLOGIN_PATH);
} }
void sm_init(sm_t *sm) void sm_init(void)
{ {
static struct wq work = { static struct wq work = {
.cb = sm_check_bootstrap, .cb = sm_check_bootstrap,
.delay = 1000 .delay = 1000
}; };
sm->state = SM_BOOTSTRAP_STATE; sm.state = SM_BOOTSTRAP_STATE;
sm->newlevel = -1; sm.newlevel = -1;
sm->reload = 0; sm.reload = 0;
sm->in_teardown = 0; sm.in_reload = 0;
dbg("Starting bootstrap finalize timer ..."); dbg("Starting bootstrap finalize timer ...");
schedule_work(&work); schedule_work(&work);
} }
void sm_set_runlevel(sm_t *sm, int newlevel) void sm_step(void)
{
sm->newlevel = newlevel;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
}
void sm_set_reload(sm_t *sm)
{
sm->reload = 1;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
}
int sm_is_in_teardown(sm_t *sm)
{
return sm->in_teardown;
}
void sm_step(sm_t *sm)
{ {
sm_state_t old_state; sm_state_t old_state;
svc_t *svc; svc_t *svc;
restart: restart:
old_state = sm->state; old_state = sm.state;
dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d", dbg("state: %s, runlevel: %c, newlevel: %d, teardown: %d, reload: %d",
sm_status(sm->state), sm_runlevel(runlevel), sm->newlevel, sm->in_teardown, sm->reload); sm_status(sm.state), sm_rl2ch(runlevel), sm.newlevel, sm.in_reload, sm.reload);
switch (sm->state) { switch (sm.state) {
case SM_BOOTSTRAP_STATE: case SM_BOOTSTRAP_STATE:
dbg("Bootstrapping all services in runlevel S from %s", finit_conf); dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV); service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV);
sm->state = SM_BOOTSTRAP_WAIT_STATE; sm.state = SM_BOOTSTRAP_WAIT_STATE;
break; break;
/* /*
@@ -232,7 +228,7 @@ restart:
/* Allow runparts to start */ /* Allow runparts to start */
cond_set_oneshot("int/bootstrap"); cond_set_oneshot("int/bootstrap");
if (sm->newlevel == -1) if (sm.newlevel == -1)
break; break;
/* Hooks that should run at the very end */ /* Hooks that should run at the very end */
@@ -246,7 +242,7 @@ restart:
/* System bootstrapped, launch TTYs et al */ /* System bootstrapped, launch TTYs et al */
bootstrap = 0; bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN); service_step_all(SVC_TYPE_RESPAWN);
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
case SM_RUNNING_STATE: case SM_RUNNING_STATE:
@@ -254,26 +250,26 @@ restart:
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
/* runlevel changed? */ /* runlevel changed? */
if (sm->newlevel >= 0 && sm->newlevel <= 9) { if (sm.newlevel >= 0 && sm.newlevel <= 9) {
if (runlevel == sm->newlevel) { if (runlevel == sm.newlevel) {
sm->newlevel = -1; sm.newlevel = -1;
break; break;
} }
sm->state = SM_RUNLEVEL_CHANGE_STATE; sm.state = SM_RUNLEVEL_CHANGE_STATE;
break; break;
} }
/* reload ? */ /* reload ? */
if (sm->reload) { if (sm.reload) {
sm->reload = 0; sm.reload = 0;
sm->state = SM_RELOAD_CHANGE_STATE; sm.state = SM_RELOAD_CHANGE_STATE;
} }
break; break;
case SM_RUNLEVEL_CHANGE_STATE: case SM_RUNLEVEL_CHANGE_STATE:
prevlevel = runlevel; prevlevel = runlevel;
runlevel = sm->newlevel; runlevel = sm.newlevel;
sm->newlevel = -1; sm.newlevel = -1;
/* Restore terse mode and run hooks before shutdown */ /* Restore terse mode and run hooks before shutdown */
if (runlevel == 0 || runlevel == 6) { if (runlevel == 0 || runlevel == 6) {
@@ -282,11 +278,11 @@ restart:
plugin_run_hooks(HOOK_SHUTDOWN); plugin_run_hooks(HOOK_SHUTDOWN);
} }
dbg("Setting new runlevel --> %c <-- previous %c", sm_runlevel(runlevel), sm_runlevel(prevlevel)); dbg("Setting new runlevel --> %c <-- previous %c", sm_rl2ch(runlevel), sm_rl2ch(prevlevel));
if (osheading) if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_runlevel(runlevel)); logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %c", osheading, sm_rl2ch(runlevel));
else else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_runlevel(runlevel)); logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %c", sm_rl2ch(runlevel));
runlevel_set(prevlevel, runlevel); runlevel_set(prevlevel, runlevel);
/* Disable login in single-user mode as well as shutdown/reboot */ /* Disable login in single-user mode as well as shutdown/reboot */
@@ -302,10 +298,10 @@ restart:
service_runtask_clean(); service_runtask_clean();
dbg("Stopping services not allowed in new runlevel ..."); dbg("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1; sm.in_reload = 1;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_WAIT_STATE; sm.state = SM_RUNLEVEL_WAIT_STATE;
break; break;
case SM_RUNLEVEL_WAIT_STATE: case SM_RUNLEVEL_WAIT_STATE:
@@ -324,10 +320,10 @@ restart:
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
dbg("Starting services new to this runlevel ..."); dbg("Starting services new to this runlevel ...");
sm->in_teardown = 0; sm.in_reload = 0;
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_CLEAN_STATE; sm.state = SM_RUNLEVEL_CLEAN_STATE;
break; break;
case SM_RUNLEVEL_CLEAN_STATE: case SM_RUNLEVEL_CLEAN_STATE:
@@ -361,7 +357,7 @@ restart:
if (runlevel == 0 || runlevel == 6) if (runlevel == 0 || runlevel == 6)
do_shutdown(halt); do_shutdown(halt);
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
case SM_RELOAD_CHANGE_STATE: case SM_RELOAD_CHANGE_STATE:
@@ -373,11 +369,11 @@ restart:
* let all affected services move to WAITING/HALTED * let all affected services move to WAITING/HALTED
*/ */
dbg("Stopping services not allowed after reconf ..."); dbg("Stopping services not allowed after reconf ...");
sm->in_teardown = 1; sm.in_reload = 1;
cond_reload(); cond_reload();
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_WAIT_STATE; sm.state = SM_RELOAD_WAIT_STATE;
break; break;
case SM_RELOAD_WAIT_STATE: case SM_RELOAD_WAIT_STATE:
@@ -391,12 +387,12 @@ restart:
break; break;
} }
sm->in_teardown = 0; sm.in_reload = 0;
dbg("Starting services after reconf ..."); dbg("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_CLEAN_STATE; sm.state = SM_RELOAD_CLEAN_STATE;
break; break;
case SM_RELOAD_CLEAN_STATE: case SM_RELOAD_CLEAN_STATE:
@@ -422,14 +418,66 @@ restart:
service_notify_reconf(); service_notify_reconf();
dbg("Reconfiguration done"); dbg("Reconfiguration done");
sm->state = SM_RUNNING_STATE; sm.state = SM_RUNNING_STATE;
break; break;
} }
if (sm->state != old_state) if (sm.state != old_state)
goto restart; goto restart;
} }
/**
* sm_in_reload - System currently in reload or runlevel change?
*
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int sm_in_reload(void)
{
return sm.in_reload;
}
/**
* sm_reload - Called on SIGHUP, 'init q' or 'initctl reload'
*
* This function is called when Finit has received SIGHUP to reload
* .conf files in /etc/finit.d. It is responsible for starting,
* stopping and reloading (forwarding SIGHUP) to processes affected.
*/
void sm_reload(void)
{
sm.reload = 1;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
sm_step();
}
/**
* sm_runlevel - Change to a new runlevel
* @newlevel: New runlevel to activate
*
* Stops all services not in @newlevel and starts, or lets continue to run,
* those in @newlevel. Also updates @prevlevel and active @runlevel.
*/
void sm_runlevel(int newlevel)
{
if (!rescue && (runlevel == 1 || runlevel == INIT_LEVEL) && !IS_RESERVED_RUNLEVEL(newlevel))
networking(1);
sm.newlevel = newlevel;
dbg("Flushing pending .conf file events ...");
conf_flush_events();
sm_step();
if (!rescue && IS_RESERVED_RUNLEVEL(runlevel))
networking(0);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+5 -25
View File
@@ -24,32 +24,12 @@
#ifndef FINIT_SM_H_ #ifndef FINIT_SM_H_
#define FINIT_SM_H_ #define FINIT_SM_H_
typedef enum { void sm_init (void);
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */ void sm_step (void);
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
SM_RUNNING_STATE, /* Normal state, services running */
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RUNLEVEL_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped processes to halt */
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
} sm_state_t;
typedef struct sm { int sm_in_reload (void);
sm_state_t state; /* Running, Changed, Waiting, ... */ void sm_reload (void);
int newlevel; /* Set on runlevel change to new runlevel, -1 if not change */ void sm_runlevel (int newlevel);
int reload; /* Set on reload event, else 0 */
int in_teardown; /* Set when waiting for all processes to be halted */
} sm_t;
extern sm_t sm;
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_ */ #endif /* FINIT_SM_H_ */