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
2025-07-06 03:13:56 +02:00
parent 39f4aa3f2c
commit 1b9e69414e
10 changed files with 135 additions and 138 deletions
+7 -6
View File
@@ -47,6 +47,7 @@
#include "private.h"
#include "schedule.h"
#include "service.h"
#include "sm.h"
#include "sig.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:
dbg("reboot");
halt = SHUT_REBOOT;
service_runlevel(6);
sm_runlevel(6);
break;
case INIT_CMD_HALT:
dbg("halt");
halt = SHUT_HALT;
service_runlevel(0);
sm_runlevel(0);
break;
case INIT_CMD_POWEROFF:
dbg("poweroff");
halt = SHUT_OFF;
service_runlevel(0);
sm_runlevel(0);
break;
case INIT_CMD_SUSPEND:
@@ -316,7 +317,7 @@ typedef struct {
} ev_t;
ev_t ev_list[] = {
{ "RELOAD", service_reload_dynamic },
{ "RELOAD", sm_reload },
{ NULL, NULL }
};
@@ -437,7 +438,7 @@ static void api_cb(uev_t *w, void *arg, int events)
if (runlevel == INIT_LEVEL)
cfglevel = lvl;
else
service_runlevel(lvl);
sm_runlevel(lvl);
break;
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' */
dbg("reload");
service_reload_dynamic();
sm_reload();
break;
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
if (conf_any_change())
service_reload_dynamic();
sm_reload();
#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);
cond = ptr + strlen(COND_BASE) + 1;
if (sm_is_in_teardown(&sm))
if (sm_in_reload())
cond_clear_noupdate(cond);
else
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
if (conf_any_change())
service_reload_dynamic();
sm_reload();
#endif
}
+2 -2
View File
@@ -745,8 +745,8 @@ int main(int argc, char *argv[])
* Initialize state machine and start all bootstrap tasks
* NOTE: no network available!
*/
sm_init(&sm);
sm_step(&sm);
sm_init();
sm_step();
/*
* 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;
}
/**
* 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
* 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);
}
sm_step(&sm);
sm_step();
}
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) ||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
; /* only paused during reload, don't clear conds. */
else if (sm_is_in_teardown(&sm))
else if (sm_in_reload())
cond_clear_noupdate(cond);
else
cond_clear(cond);
@@ -2713,7 +2681,7 @@ restart:
svc_set_state(svc, SVC_HALTED_STATE);
} else if (cond_get_agg(svc->cond) == COND_ON) {
/* wait until all processes have been stopped before continuing... */
if (sm_is_in_teardown(&sm))
if (sm_in_reload())
break;
/* Don't start if it conflicts with something else already started */
@@ -2808,15 +2776,16 @@ restart:
case COND_ON:
if (svc_is_changed(svc)) {
if (svc_nohup(svc) || !svc_is_daemon(svc))
if (svc_nohup(svc) || !svc_is_daemon(svc)) {
service_stop(svc);
else {
} else {
/*
* wait until all processes have been
* stopped before continuing...
*/
if (sm_is_in_teardown(&sm))
if (sm_in_reload())
break;
service_restart(svc);
}
-2
View File
@@ -27,12 +27,10 @@
#include "svc.h"
void service_runlevel (int newlevel);
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
void service_unregister (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void);
void service_mark_unavail (void);
+4 -3
View File
@@ -94,6 +94,7 @@
#include "private.h"
#include "sig.h"
#include "service.h"
#include "sm.h"
#include "util.h"
#include "utmp-api.h"
@@ -444,7 +445,7 @@ static void sighup_cb(uev_t *w, void *arg, int events)
api_init(w->ctx);
/* 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;
service_runlevel(0);
sm_runlevel(0);
}
/*
@@ -551,7 +552,7 @@ static void sigterm_cb(uev_t *w, void *arg, int events)
}
halt = SHUT_REBOOT;
service_runlevel(6);
sm_runlevel(6);
}
/*
+107 -59
View File
@@ -38,7 +38,24 @@
#include "sm.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
#define FINIT_NOLOGIN_PATH _PATH_NOLOGIN /* Stop user logging in. */
@@ -90,7 +107,7 @@ static void sm_check_bootstrap(void *work)
} else
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 */
dbg("Clean up all bootstrap-only tasks/services ...");
@@ -135,7 +152,7 @@ static char *sm_status(sm_state_t state)
return "unknown";
}
static char sm_runlevel(int lvl)
static char sm_rl2ch(int lvl)
{
if (lvl == INIT_LEVEL)
return 'S';
@@ -158,60 +175,39 @@ static void nologin(void)
erase(FINIT_NOLOGIN_PATH);
}
void sm_init(sm_t *sm)
void sm_init(void)
{
static struct wq work = {
.cb = sm_check_bootstrap,
.delay = 1000
};
sm->state = SM_BOOTSTRAP_STATE;
sm->newlevel = -1;
sm->reload = 0;
sm->in_teardown = 0;
sm.state = SM_BOOTSTRAP_STATE;
sm.newlevel = -1;
sm.reload = 0;
sm.in_reload = 0;
dbg("Starting bootstrap finalize timer ...");
schedule_work(&work);
}
void sm_set_runlevel(sm_t *sm, int newlevel)
{
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)
void sm_step(void)
{
sm_state_t old_state;
svc_t *svc;
restart:
old_state = sm->state;
old_state = sm.state;
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:
dbg("Bootstrapping all services in runlevel S from %s", finit_conf);
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;
/*
@@ -232,7 +228,7 @@ restart:
/* Allow runparts to start */
cond_set_oneshot("int/bootstrap");
if (sm->newlevel == -1)
if (sm.newlevel == -1)
break;
/* Hooks that should run at the very end */
@@ -246,7 +242,7 @@ restart:
/* System bootstrapped, launch TTYs et al */
bootstrap = 0;
service_step_all(SVC_TYPE_RESPAWN);
sm->state = SM_RUNNING_STATE;
sm.state = SM_RUNNING_STATE;
break;
case SM_RUNNING_STATE:
@@ -254,26 +250,26 @@ restart:
service_step_all(SVC_TYPE_ANY);
/* runlevel changed? */
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
if (runlevel == sm->newlevel) {
sm->newlevel = -1;
if (sm.newlevel >= 0 && sm.newlevel <= 9) {
if (runlevel == sm.newlevel) {
sm.newlevel = -1;
break;
}
sm->state = SM_RUNLEVEL_CHANGE_STATE;
sm.state = SM_RUNLEVEL_CHANGE_STATE;
break;
}
/* reload ? */
if (sm->reload) {
sm->reload = 0;
sm->state = SM_RELOAD_CHANGE_STATE;
if (sm.reload) {
sm.reload = 0;
sm.state = SM_RELOAD_CHANGE_STATE;
}
break;
case SM_RUNLEVEL_CHANGE_STATE:
prevlevel = runlevel;
runlevel = sm->newlevel;
sm->newlevel = -1;
runlevel = sm.newlevel;
sm.newlevel = -1;
/* Restore terse mode and run hooks before shutdown */
if (runlevel == 0 || runlevel == 6) {
@@ -282,11 +278,11 @@ restart:
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)
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
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);
/* Disable login in single-user mode as well as shutdown/reboot */
@@ -302,10 +298,10 @@ restart:
service_runtask_clean();
dbg("Stopping services not allowed in new runlevel ...");
sm->in_teardown = 1;
sm.in_reload = 1;
service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_WAIT_STATE;
sm.state = SM_RUNLEVEL_WAIT_STATE;
break;
case SM_RUNLEVEL_WAIT_STATE:
@@ -324,10 +320,10 @@ restart:
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
dbg("Starting services new to this runlevel ...");
sm->in_teardown = 0;
sm.in_reload = 0;
service_step_all(SVC_TYPE_ANY);
sm->state = SM_RUNLEVEL_CLEAN_STATE;
sm.state = SM_RUNLEVEL_CLEAN_STATE;
break;
case SM_RUNLEVEL_CLEAN_STATE:
@@ -361,7 +357,7 @@ restart:
if (runlevel == 0 || runlevel == 6)
do_shutdown(halt);
sm->state = SM_RUNNING_STATE;
sm.state = SM_RUNNING_STATE;
break;
case SM_RELOAD_CHANGE_STATE:
@@ -373,11 +369,11 @@ restart:
* let all affected services move to WAITING/HALTED
*/
dbg("Stopping services not allowed after reconf ...");
sm->in_teardown = 1;
sm.in_reload = 1;
cond_reload();
service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_WAIT_STATE;
sm.state = SM_RELOAD_WAIT_STATE;
break;
case SM_RELOAD_WAIT_STATE:
@@ -391,12 +387,12 @@ restart:
break;
}
sm->in_teardown = 0;
sm.in_reload = 0;
dbg("Starting services after reconf ...");
service_step_all(SVC_TYPE_ANY);
sm->state = SM_RELOAD_CLEAN_STATE;
sm.state = SM_RELOAD_CLEAN_STATE;
break;
case SM_RELOAD_CLEAN_STATE:
@@ -422,14 +418,66 @@ restart:
service_notify_reconf();
dbg("Reconfiguration done");
sm->state = SM_RUNNING_STATE;
sm.state = SM_RUNNING_STATE;
break;
}
if (sm->state != old_state)
if (sm.state != old_state)
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:
* indent-tabs-mode: t
+5 -25
View File
@@ -24,32 +24,12 @@
#ifndef FINIT_SM_H_
#define FINIT_SM_H_
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;
void sm_init (void);
void sm_step (void);
typedef struct sm {
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;
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);
int sm_in_reload (void);
void sm_reload (void);
void sm_runlevel (int newlevel);
#endif /* FINIT_SM_H_ */