diff --git a/src/api.c b/src/api.c index a9fb0745..eb5633ab 100644 --- a/src/api.c +++ b/src/api.c @@ -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: diff --git a/src/cgroup.c b/src/cgroup.c index 0c7e3155..2044f1c7 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -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 } diff --git a/src/cond-w.c b/src/cond-w.c index faf72e2f..4ec119d2 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -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); diff --git a/src/conf.c b/src/conf.c index 6f277b2a..3906edc4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -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 } diff --git a/src/finit.c b/src/finit.c index 3df136a3..bed9cb50 100644 --- a/src/finit.c +++ b/src/finit.c @@ -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 diff --git a/src/service.c b/src/service.c index 81469cfa..e3080b99 100644 --- a/src/service.c +++ b/src/service.c @@ -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); } diff --git a/src/service.h b/src/service.h index 4059c6b3..100a73e2 100644 --- a/src/service.h +++ b/src/service.h @@ -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); diff --git a/src/sig.c b/src/sig.c index 080ea305..e534319f 100644 --- a/src/sig.c +++ b/src/sig.c @@ -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); } /* diff --git a/src/sm.c b/src/sm.c index 1f6d3ddb..a9513004 100644 --- a/src/sm.c +++ b/src/sm.c @@ -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 diff --git a/src/sm.h b/src/sm.h index a6a43a27..15e7bc1f 100644 --- a/src/sm.h +++ b/src/sm.h @@ -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_ */