diff --git a/doc/svc-machine.png b/doc/svc-machine.png index 02c84e7c..498aa70e 100644 Binary files a/doc/svc-machine.png and b/doc/svc-machine.png differ diff --git a/doc/svc-machine.svg b/doc/svc-machine.svg index 2bcb5492..47b2a871 100644 --- a/doc/svc-machine.svg +++ b/doc/svc-machine.svg @@ -1,4 +1,4 @@ - + -
STOPPING
STOPPING
HALTED
HALTED
WAITING
WAITING
PAUSED
PAUSED
RUNNING
RUNNING
CLEANUP
CLEANUP
SETUP
SETUP
post:
post:
~C
~C
-C
-C
+C
+C
-E
-E
+C and pre:script
+C and pre:script
-E or -C
-E or -C
+C
+C
start
start
+E
+E
E - Enabled
C - Condition(s)
P - PID file ready
R - Readiness
pre: - pre-script
post: - post-script
E - Enabled...
DONE
DONE
run/task
run/ta...
manual:yes and
initctl (re)start
manual:yes and...
STARTING
STARTING
pre:script done
pre:script done
initctl reload

SIGSTOP while figuring out if process should be in this .conf generation
initctl reload...
initctl reload

Enabled and READY but waiting for a condition, e.g. another process.
initctl reload...
ready:script
ready:script
+R
 or +P
+R...
When service signals 'ready' we call the ready script and then emit the <svc> condtion to trigger other run/task/services
When service signals 're...
NULL conditions are also conditions ...
NULL conditions are...
Text is not SVG - cannot display
\ No newline at end of file +
STOPPING
HALTED
WAITING
PAUSED
RUNNING
TEARDOWN
SETUP
post:script runs first if both exist
~C
-C
+C
-E
+C and pre:script
-E or -C
+C
new run/task/sysv/service
+E
E - Enabled
C - Condition(s)
P - PID file ready
R - Readiness
pre: - pre-script
post: - post-script
DONE
run/task
STARTING
pre:script done
initctl reload

SIGSTOP while figuring out if process should be in this .conf generation
initctl reload

Enabled and READY but waiting for a condition, e.g. another process.
ready:script
+R
 or +P
When service signals 'ready' we call the ready script and then emit the <svc> condtion to trigger other run/task/services
NULL conditions are also conditions ...
cleanup:script
Wait here until the PID has been reaped
CLEANUP
removed
scripts done
* manual + (re)start
* runlevel change
* removed
only if removed
\ No newline at end of file diff --git a/src/service.c b/src/service.c index f39b2b0a..d23a28a4 100644 --- a/src/service.c +++ b/src/service.c @@ -2483,16 +2483,13 @@ restart: condstr(cond_get_agg(svc->cond))); switch (svc->state) { - case SVC_CLEANUP_STATE: - if (!svc->pid) - svc_set_state(svc, SVC_HALTED_STATE); - break; - case SVC_HALTED_STATE: if (enabled) svc_set_state(svc, SVC_WAITING_STATE); else { - if (svc_is_conflict(svc)) { + if (svc_is_removed(svc)) { + svc_set_state(svc, SVC_DEAD_STATE); + } else if (svc_is_conflict(svc)) { #if 0 logit(svc->nowarn ? LOG_DEBUG : LOG_INFO, "%s in conflict with %s, checking again ...", @@ -2504,6 +2501,30 @@ restart: } break; + case SVC_TEARDOWN_STATE: + if (!svc->pid) { + dbg("%s: post script done.", svc_ident(svc, NULL, 0)); + service_timeout_cancel(svc); + + if (svc_is_removed(svc) && svc_has_cleanup(svc)) { + svc_set_state(svc, SVC_CLEANUP_STATE); + service_cleanup_script(svc); + } else + svc_set_state(svc, SVC_HALTED_STATE); + } + break; + + case SVC_CLEANUP_STATE: + if (!svc->pid) { + dbg("%s: cleanup script done.", svc_ident(svc, NULL, 0)); + svc_set_state(svc, SVC_HALTED_STATE); + } + break; + + case SVC_DEAD_STATE: + /* End of the line ☠ */ + break; + case SVC_DONE_STATE: if (svc_is_changed(svc)) svc_set_state(svc, SVC_HALTED_STATE); @@ -2528,6 +2549,9 @@ restart: if (svc_has_post(svc)) { svc_set_state(svc, SVC_TEARDOWN_STATE); service_post_script(svc); + } else if (svc_is_removed(svc) && svc_has_cleanup(svc)) { + svc_set_state(svc, SVC_CLEANUP_STATE); + service_cleanup_script(svc); } else svc_set_state(svc, SVC_HALTED_STATE); break; @@ -2546,16 +2570,6 @@ restart: } break; - case SVC_TEARDOWN_STATE: - if (!svc->pid) { - if (svc_is_removed(svc) && svc_has_cleanup(svc)) { - svc_set_state(svc, SVC_CLEANUP_STATE); - service_cleanup_script(svc); - } else - svc_set_state(svc, SVC_HALTED_STATE); - } - break; - case SVC_SETUP_STATE: if (!svc->pid) { int rc = WEXITSTATUS(svc->status); diff --git a/src/sm.c b/src/sm.c index 7ce99f48..c962b95e 100644 --- a/src/sm.c +++ b/src/sm.c @@ -107,6 +107,9 @@ static char *sm_status(sm_state_t state) case SM_BOOTSTRAP_STATE: return "bootstrap"; + case SM_BOOTSTRAP_WAIT_STATE: + return "bootstrap/wait"; + case SM_RUNNING_STATE: return "running"; @@ -116,15 +119,20 @@ static char *sm_status(sm_state_t state) case SM_RUNLEVEL_WAIT_STATE: return "runlevel/wait"; + case SM_RUNLEVEL_CLEAN_STATE: + return "runlevel/clean"; + case SM_RELOAD_CHANGE_STATE: return "reload/change"; case SM_RELOAD_WAIT_STATE: return "reload/wait"; - default: - return "unknown"; + case SM_RELOAD_CLEAN_STATE: + return "reload/clean"; } + + return "unknown"; } static char sm_runlevel(int lvl) @@ -319,6 +327,22 @@ restart: sm->in_teardown = 0; service_step_all(SVC_TYPE_ANY); + sm->state = SM_RUNLEVEL_CLEAN_STATE; + break; + + case SM_RUNLEVEL_CLEAN_STATE: + /* + * Wait for post:script or cleanup:script to be collected, + * which moves the svc to HALTED or DEAD state. We will + * be called by the service_monitor() on collect. + */ + svc = svc_clean_completed(); + if (svc) { + dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...", + svc_ident(svc, NULL, 0), svc->cmd, svc->pid); + break; + } + /* Cleanup stale services */ svc_clean_dynamic(service_unregister); @@ -372,6 +396,22 @@ restart: dbg("Starting services after reconf ..."); service_step_all(SVC_TYPE_ANY); + sm->state = SM_RELOAD_CLEAN_STATE; + break; + + case SM_RELOAD_CLEAN_STATE: + /* + * Wait for post:script or cleanup:script to be collected, + * which moves the svc to HALTED or DEAD state. We will + * be called by the service_monitor() on collect. + */ + svc = svc_clean_completed(); + if (svc) { + dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...", + svc_ident(svc, NULL, 0), svc->cmd, svc->pid); + break; + } + /* Cleanup stale services */ svc_clean_dynamic(service_unregister); diff --git a/src/sm.h b/src/sm.h index 86c5c643..a6a43a27 100644 --- a/src/sm.h +++ b/src/sm.h @@ -29,9 +29,11 @@ typedef enum { 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 runlevel processes to be halted */ + 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 reload processes to be halted */ + 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 { diff --git a/src/svc.c b/src/svc.c index c1706570..7b114de5 100644 --- a/src/svc.c +++ b/src/svc.c @@ -348,6 +348,43 @@ svc_t *svc_stop_completed(void) return NULL; } + +/** + * svc_clean_completed - Have post: and cleanup: scripts finished? + * + * Called late in runlevel and reload transitions to check if all post: and + * cleanup:scripts have completed. A removed service should end up in the + * DEAD state, while a service with a post:script ends up in HALTED. + * + * Returns: + * %NULL if post: and cleanup: scripts have run, otherwise a pointer to + * the first found svc_t waiting for post: or cleanup:script. + */ +svc_t *svc_clean_completed(void) +{ + svc_t *svc, *iter = NULL; + + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { + if (svc_enabled(svc)) + continue; + + if (svc_is_runtask(svc) && svc->state == SVC_DONE_STATE) + continue; + + if (svc_is_removed(svc)) { + if (svc->state == SVC_DEAD_STATE) + continue; /* any cleanup:script done */ + } else { + if (svc->state == SVC_HALTED_STATE) + continue; /* any post:script done */ + } + + return svc; + } + + return NULL; +} + /** * svc_find - Find a service object by its full path name * @name: Full path name, e.g., /sbin/syslogd @@ -580,7 +617,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *)) svc_t *svc, *iter = NULL; for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { - if (svc->removed && cb) + if (svc_is_removed(svc) && cb) cb(svc); } } diff --git a/src/svc.h b/src/svc.h index 44947c33..c6e131d9 100644 --- a/src/svc.h +++ b/src/svc.h @@ -58,10 +58,11 @@ typedef enum { typedef enum { SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */ SVC_DONE_STATE, /* Task/Run job has been run */ + SVC_DEAD_STATE, /* Process is dead and scheduled for removal */ + SVC_CLEANUP_STATE, /* Running cleanup: script */ + SVC_TEARDOWN_STATE, /* Running post: script */ SVC_STOPPING_STATE, /* Waiting to collect the child process */ SVC_SETUP_STATE, /* Running pre: script */ - SVC_TEARDOWN_STATE, /* Running post: script */ - SVC_CLEANUP_STATE, /* Running cleanup: script */ SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */ SVC_WAITING_STATE, /* Enabled but condition not satisfied */ SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */ @@ -247,6 +248,7 @@ void svc_foreach (int (*cb)(svc_t *)); void svc_foreach_type (int types, int (*cb)(svc_t *)); svc_t *svc_stop_completed (void); +svc_t *svc_clean_completed (void); void svc_mark (svc_t *svc); void svc_mark_dynamic (void); @@ -405,7 +407,7 @@ static inline char *svc_status(svc_t *svc) return "setup"; case SVC_CLEANUP_STATE: - return "setup"; + return "cleanup"; case SVC_PAUSED_STATE: return "paused"; @@ -419,6 +421,9 @@ static inline char *svc_status(svc_t *svc) case SVC_RUNNING_STATE: return "running"; + case SVC_DEAD_STATE: + return "dead"; + default: return "UNKNOWN"; }