Skip to content

Commit 3add682

Browse files
committed
Wait for post:script and cleanup:script before returning OK
This change introduces two new states for the big Finit state machine: runlevel-clean and reload-clean. Here Finit now waits for any post or cleanup script to finish before returning OK to the initctl command. Additionally, the service state machine has been updated to ensure a run/task/sysv/service calls any post or cleanup script before they are removed. A new 'dead' state for svc_t is introduced which any removed svc_t ends up in now instead of becing collected from 'halted' state. Signed-off-by: Joachim Wiberg <[email protected]> Add cleanup:script support, runs at service removal Signed-off-by: Joachim Wiberg <[email protected]>
1 parent abac316 commit 3add682

File tree

7 files changed

+124
-26
lines changed

7 files changed

+124
-26
lines changed

doc/svc-machine.png

181 KB
Loading

doc/svc-machine.svg

Lines changed: 2 additions & 2 deletions
Loading

src/service.c

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2483,16 +2483,13 @@ int service_step(svc_t *svc)
24832483
condstr(cond_get_agg(svc->cond)));
24842484

24852485
switch (svc->state) {
2486-
case SVC_CLEANUP_STATE:
2487-
if (!svc->pid)
2488-
svc_set_state(svc, SVC_HALTED_STATE);
2489-
break;
2490-
24912486
case SVC_HALTED_STATE:
24922487
if (enabled)
24932488
svc_set_state(svc, SVC_WAITING_STATE);
24942489
else {
2495-
if (svc_is_conflict(svc)) {
2490+
if (svc_is_removed(svc)) {
2491+
svc_set_state(svc, SVC_DEAD_STATE);
2492+
} else if (svc_is_conflict(svc)) {
24962493
#if 0
24972494
logit(svc->nowarn ? LOG_DEBUG : LOG_INFO,
24982495
"%s in conflict with %s, checking again ...",
@@ -2504,6 +2501,30 @@ int service_step(svc_t *svc)
25042501
}
25052502
break;
25062503

2504+
case SVC_TEARDOWN_STATE:
2505+
if (!svc->pid) {
2506+
dbg("%s: post script done.", svc_ident(svc, NULL, 0));
2507+
service_timeout_cancel(svc);
2508+
2509+
if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
2510+
svc_set_state(svc, SVC_CLEANUP_STATE);
2511+
service_cleanup_script(svc);
2512+
} else
2513+
svc_set_state(svc, SVC_HALTED_STATE);
2514+
}
2515+
break;
2516+
2517+
case SVC_CLEANUP_STATE:
2518+
if (!svc->pid) {
2519+
dbg("%s: cleanup script done.", svc_ident(svc, NULL, 0));
2520+
svc_set_state(svc, SVC_HALTED_STATE);
2521+
}
2522+
break;
2523+
2524+
case SVC_DEAD_STATE:
2525+
/* End of the line ☠ */
2526+
break;
2527+
25072528
case SVC_DONE_STATE:
25082529
if (svc_is_changed(svc))
25092530
svc_set_state(svc, SVC_HALTED_STATE);
@@ -2528,6 +2549,9 @@ int service_step(svc_t *svc)
25282549
if (svc_has_post(svc)) {
25292550
svc_set_state(svc, SVC_TEARDOWN_STATE);
25302551
service_post_script(svc);
2552+
} else if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
2553+
svc_set_state(svc, SVC_CLEANUP_STATE);
2554+
service_cleanup_script(svc);
25312555
} else
25322556
svc_set_state(svc, SVC_HALTED_STATE);
25332557
break;
@@ -2546,16 +2570,6 @@ int service_step(svc_t *svc)
25462570
}
25472571
break;
25482572

2549-
case SVC_TEARDOWN_STATE:
2550-
if (!svc->pid) {
2551-
if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
2552-
svc_set_state(svc, SVC_CLEANUP_STATE);
2553-
service_cleanup_script(svc);
2554-
} else
2555-
svc_set_state(svc, SVC_HALTED_STATE);
2556-
}
2557-
break;
2558-
25592573
case SVC_SETUP_STATE:
25602574
if (!svc->pid) {
25612575
int rc = WEXITSTATUS(svc->status);

src/sm.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ static char *sm_status(sm_state_t state)
107107
case SM_BOOTSTRAP_STATE:
108108
return "bootstrap";
109109

110+
case SM_BOOTSTRAP_WAIT_STATE:
111+
return "bootstrap/wait";
112+
110113
case SM_RUNNING_STATE:
111114
return "running";
112115

@@ -116,15 +119,20 @@ static char *sm_status(sm_state_t state)
116119
case SM_RUNLEVEL_WAIT_STATE:
117120
return "runlevel/wait";
118121

122+
case SM_RUNLEVEL_CLEAN_STATE:
123+
return "runlevel/clean";
124+
119125
case SM_RELOAD_CHANGE_STATE:
120126
return "reload/change";
121127

122128
case SM_RELOAD_WAIT_STATE:
123129
return "reload/wait";
124130

125-
default:
126-
return "unknown";
131+
case SM_RELOAD_CLEAN_STATE:
132+
return "reload/clean";
127133
}
134+
135+
return "unknown";
128136
}
129137

130138
static char sm_runlevel(int lvl)
@@ -319,6 +327,22 @@ void sm_step(sm_t *sm)
319327
sm->in_teardown = 0;
320328
service_step_all(SVC_TYPE_ANY);
321329

330+
sm->state = SM_RUNLEVEL_CLEAN_STATE;
331+
break;
332+
333+
case SM_RUNLEVEL_CLEAN_STATE:
334+
/*
335+
* Wait for post:script or cleanup:script to be collected,
336+
* which moves the svc to HALTED or DEAD state. We will
337+
* be called by the service_monitor() on collect.
338+
*/
339+
svc = svc_clean_completed();
340+
if (svc) {
341+
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
342+
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
343+
break;
344+
}
345+
322346
/* Cleanup stale services */
323347
svc_clean_dynamic(service_unregister);
324348

@@ -372,6 +396,22 @@ void sm_step(sm_t *sm)
372396
dbg("Starting services after reconf ...");
373397
service_step_all(SVC_TYPE_ANY);
374398

399+
sm->state = SM_RELOAD_CLEAN_STATE;
400+
break;
401+
402+
case SM_RELOAD_CLEAN_STATE:
403+
/*
404+
* Wait for post:script or cleanup:script to be collected,
405+
* which moves the svc to HALTED or DEAD state. We will
406+
* be called by the service_monitor() on collect.
407+
*/
408+
svc = svc_clean_completed();
409+
if (svc) {
410+
dbg("Waiting to collect post/cleanup script for %s, cmd %s(%d) ...",
411+
svc_ident(svc, NULL, 0), svc->cmd, svc->pid);
412+
break;
413+
}
414+
375415
/* Cleanup stale services */
376416
svc_clean_dynamic(service_unregister);
377417

src/sm.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ typedef enum {
2929
SM_BOOTSTRAP_WAIT_STATE, /* Waiting for bootstrap to complete */
3030
SM_RUNNING_STATE, /* Normal state, services running */
3131
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occurred */
32-
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
32+
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped processes to halt */
33+
SM_RUNLEVEL_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
3334
SM_RELOAD_CHANGE_STATE, /* A reload event has occurred */
34-
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */
35+
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped processes to halt */
36+
SM_RELOAD_CLEAN_STATE, /* Wait for post:scripts and cleanup:scripts */
3537
} sm_state_t;
3638

3739
typedef struct sm {

src/svc.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,43 @@ svc_t *svc_stop_completed(void)
348348
return NULL;
349349
}
350350

351+
352+
/**
353+
* svc_clean_completed - Have post: and cleanup: scripts finished?
354+
*
355+
* Called late in runlevel and reload transitions to check if all post: and
356+
* cleanup:scripts have completed. A removed service should end up in the
357+
* DEAD state, while a service with a post:script ends up in HALTED.
358+
*
359+
* Returns:
360+
* %NULL if post: and cleanup: scripts have run, otherwise a pointer to
361+
* the first found svc_t waiting for post: or cleanup:script.
362+
*/
363+
svc_t *svc_clean_completed(void)
364+
{
365+
svc_t *svc, *iter = NULL;
366+
367+
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
368+
if (svc_enabled(svc))
369+
continue;
370+
371+
if (svc_is_runtask(svc) && svc->state == SVC_DONE_STATE)
372+
continue;
373+
374+
if (svc_is_removed(svc)) {
375+
if (svc->state == SVC_DEAD_STATE)
376+
continue; /* any cleanup:script done */
377+
} else {
378+
if (svc->state == SVC_HALTED_STATE)
379+
continue; /* any post:script done */
380+
}
381+
382+
return svc;
383+
}
384+
385+
return NULL;
386+
}
387+
351388
/**
352389
* svc_find - Find a service object by its full path name
353390
* @name: Full path name, e.g., /sbin/syslogd
@@ -580,7 +617,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
580617
svc_t *svc, *iter = NULL;
581618

582619
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
583-
if (svc->removed && cb)
620+
if (svc_is_removed(svc) && cb)
584621
cb(svc);
585622
}
586623
}

src/svc.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ typedef enum {
5858
typedef enum {
5959
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
6060
SVC_DONE_STATE, /* Task/Run job has been run */
61+
SVC_DEAD_STATE, /* Process is dead and scheduled for removal */
62+
SVC_CLEANUP_STATE, /* Running cleanup: script */
63+
SVC_TEARDOWN_STATE, /* Running post: script */
6164
SVC_STOPPING_STATE, /* Waiting to collect the child process */
6265
SVC_SETUP_STATE, /* Running pre: script */
63-
SVC_TEARDOWN_STATE, /* Running post: script */
64-
SVC_CLEANUP_STATE, /* Running cleanup: script */
6566
SVC_PAUSED_STATE, /* Condition is in flux, process SIGSTOPed */
6667
SVC_WAITING_STATE, /* Enabled but condition not satisfied */
6768
SVC_STARTING_STATE, /* Conditions OK and pre: script done, start */
@@ -247,6 +248,7 @@ void svc_foreach (int (*cb)(svc_t *));
247248
void svc_foreach_type (int types, int (*cb)(svc_t *));
248249

249250
svc_t *svc_stop_completed (void);
251+
svc_t *svc_clean_completed (void);
250252

251253
void svc_mark (svc_t *svc);
252254
void svc_mark_dynamic (void);
@@ -405,7 +407,7 @@ static inline char *svc_status(svc_t *svc)
405407
return "setup";
406408

407409
case SVC_CLEANUP_STATE:
408-
return "setup";
410+
return "cleanup";
409411

410412
case SVC_PAUSED_STATE:
411413
return "paused";
@@ -419,6 +421,9 @@ static inline char *svc_status(svc_t *svc)
419421
case SVC_RUNNING_STATE:
420422
return "running";
421423

424+
case SVC_DEAD_STATE:
425+
return "dead";
426+
422427
default:
423428
return "UNKNOWN";
424429
}

0 commit comments

Comments
 (0)