mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
Add remain:yes option for run/task oneshot commands
Similar to systemd's RemainAfterExit=yes. Prevents the task from
re-running on runlevel re-entry and ensures the post: script runs
when explicitly stopped or when leaving valid runlevels.
Useful for tasks that set up persistent state like firewall rules:
task [2345] remain:yes \
post:/usr/sbin/teardown-firewall \
/usr/sbin/setup-firewall -- Firewall setup
Not supported for bootstrap-only tasks (runlevel S only) since these
are deleted immediately after completion.
This commit is contained in:
@@ -69,6 +69,11 @@ confines of that the following options are available:
|
||||
* `respawn` -- bypasses the `restart` mechanism completely, allows
|
||||
endless restarts. Useful in many use-cases, but not what `service`
|
||||
was originally designed for so not the default behavior
|
||||
* `remain:yes` -- for `run` and `task` only. Prevents the task from
|
||||
re-running on runlevel re-entry and ensures the `post:` script runs
|
||||
when the task is explicitly stopped or leaves its valid runlevels.
|
||||
Similar to systemd's `RemainAfterExit=yes`. See [Task and Run](task-and-run.md)
|
||||
for more details
|
||||
* `oncrash:reboot` -- when all retries have failed, and the service
|
||||
has *crashed*, if this option is set the system is rebooted
|
||||
* `oncrash:script` -- similarly, but instead of rebooting, call the
|
||||
|
||||
@@ -44,3 +44,44 @@ redirects can be used:
|
||||
|
||||
Please note, `;`, `&&`, `||`, and similar are *not supported*. Any
|
||||
non-trivial constructs are better placed in a separate shell script.
|
||||
|
||||
|
||||
remain:yes
|
||||
----------
|
||||
|
||||
By default, a `run` or `task` will re-run each time its runlevel is
|
||||
entered, and its `post:` script does not run on completion.
|
||||
|
||||
With `remain:yes`, the task runs once and does not re-run on runlevel
|
||||
re-entry:
|
||||
|
||||
task [2345] remain:yes /usr/sbin/setup-firewall -- Firewall setup
|
||||
|
||||
This has the following effects:
|
||||
|
||||
* The task does not re-run on runlevel re-entry
|
||||
* The `post:` script runs when:
|
||||
- The task is explicitly stopped (`initctl stop NAME`)
|
||||
- The task leaves its valid runlevels (e.g., runlevel change)
|
||||
|
||||
This is useful for tasks that set up persistent state where:
|
||||
|
||||
* Cleanup should only happen on explicit stop or when leaving valid runlevels
|
||||
* The setup should not be re-run on every runlevel entry
|
||||
|
||||
**Example:** Setting up firewall rules with cleanup on shutdown:
|
||||
|
||||
```
|
||||
task [2345] remain:yes \
|
||||
post:/usr/sbin/teardown-firewall \
|
||||
/usr/sbin/setup-firewall -- Firewall setup
|
||||
```
|
||||
|
||||
The firewall rules are created once. The `post:` script runs when
|
||||
entering runlevel 0 (halt) or 6 (reboot), or on explicit stop.
|
||||
|
||||
> [!NOTE]
|
||||
> The `remain:yes` option is not supported for bootstrap-only tasks
|
||||
> (tasks with only runlevel S). Bootstrap tasks are deleted immediately
|
||||
> after completion, and their `post:` scripts never run. A warning is
|
||||
> logged if `remain:yes` is used on such tasks.
|
||||
|
||||
@@ -398,6 +398,20 @@ mechanism completely, allows endless restarts. Useful in many
|
||||
use-cases, but not what
|
||||
.Cm service
|
||||
was originally designed for so not the default behavior.
|
||||
.It Cm remain:yes
|
||||
for
|
||||
.Cm run
|
||||
and
|
||||
.Cm task
|
||||
only. Prevents the task from re-running on runlevel re-entry and
|
||||
ensures the
|
||||
.Cm post:
|
||||
script runs when the task is explicitly stopped or leaves its valid
|
||||
runlevels. Similar to systemd's
|
||||
.Cm RemainAfterExit=yes .
|
||||
.Pp
|
||||
.Sy Note:
|
||||
not supported for bootstrap-only tasks (runlevels [S] only).
|
||||
.It Cm oncrash:reboot
|
||||
when all retries have failed, and the service
|
||||
has
|
||||
|
||||
+52
-4
@@ -1798,7 +1798,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char *dev = NULL;
|
||||
int respawn = 0;
|
||||
int levels = 0;
|
||||
int forking = 0, manual = 0, nowarn = 0;
|
||||
int forking = 0, manual = 0, remain = 0, nowarn = 0;
|
||||
int restart_max = SVC_RESPAWN_MAX;
|
||||
int restart_tmo = 0;
|
||||
unsigned oncrash_action = SVC_ONCRASH_IGNORE;
|
||||
@@ -1866,6 +1866,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
forking = 1;
|
||||
else if (MATCH_CMD(cmd, "manual:yes", arg))
|
||||
manual = 1;
|
||||
else if (MATCH_CMD(cmd, "remain:yes", arg))
|
||||
remain = 1;
|
||||
else if (MATCH_CMD(cmd, "restart:", arg)) {
|
||||
if (MATCH_CMD(arg, "always", arg))
|
||||
restart_max = -1;
|
||||
@@ -2171,6 +2173,18 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
memset(svc->ifstmt, 0, sizeof(svc->ifstmt));
|
||||
svc->manual = manual;
|
||||
svc->nowarn = nowarn;
|
||||
|
||||
/*
|
||||
* remain:yes is not supported for bootstrap-only tasks. These
|
||||
* tasks are deleted immediately after completion and their post:
|
||||
* scripts never run. This is by design.
|
||||
*/
|
||||
if (remain && svc_is_runtask(svc) && !ISOTHER(levels, INIT_LEVEL)) {
|
||||
logit(LOG_WARNING, "%s: remain:yes ignored for bootstrap-only tasks",
|
||||
svc_ident(svc, NULL, 0));
|
||||
remain = 0;
|
||||
}
|
||||
svc->remain = remain;
|
||||
svc->respawn = respawn;
|
||||
svc->forking = forking;
|
||||
svc->restart_max = restart_max;
|
||||
@@ -2859,8 +2873,12 @@ restart:
|
||||
if (svc_is_removed(svc) && svc_has_cleanup(svc)) {
|
||||
svc_set_state(svc, SVC_CLEANUP_STATE);
|
||||
service_cleanup_script(svc);
|
||||
} else
|
||||
} else {
|
||||
/* Unblock remain tasks after post script so they can restart */
|
||||
if (svc_is_remain(svc))
|
||||
svc_unblock(svc);
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2876,9 +2894,16 @@ restart:
|
||||
break;
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
if (svc_is_changed(svc))
|
||||
/* Remain tasks: always run post script when stopped, even if config changed */
|
||||
if (svc_is_runtask(svc) && svc_is_remain(svc) && svc_is_stopped(svc)) {
|
||||
if (svc_has_post(svc)) {
|
||||
svc_set_state(svc, SVC_TEARDOWN_STATE);
|
||||
service_post_script(svc);
|
||||
} else
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
} else if (svc_is_changed(svc))
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
|
||||
else if (svc_is_runtask(svc) && svc_is_manual(svc) && enabled)
|
||||
svc_set_state(svc, SVC_WAITING_STATE);
|
||||
break;
|
||||
|
||||
@@ -3180,6 +3205,29 @@ void service_runtask_clean(void)
|
||||
if (!svc_is_runtask(svc))
|
||||
continue;
|
||||
|
||||
/* Remain tasks stay in DONE state if still valid in new runlevel */
|
||||
if (svc_is_remain(svc) && svc->state == SVC_DONE_STATE) {
|
||||
if (svc_in_runlevel(svc, runlevel) && !svc_is_changed(svc))
|
||||
continue; /* Keep once flag, stay in DONE */
|
||||
|
||||
/* Config changed or leaving runlevel: stop, run post, restart */
|
||||
svc->once = 0;
|
||||
svc_stop(svc);
|
||||
service_step(svc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* On reload (SIGHUP), only remain tasks (handled above) should
|
||||
* have their once flag cleared and be restarted. Regular run/task
|
||||
* that already ran in this runlevel must not run again.
|
||||
*
|
||||
* On runlevel change, continue below to reset once flag so tasks
|
||||
* can run again in the new runlevel.
|
||||
*/
|
||||
if (sm_in_reload())
|
||||
continue;
|
||||
|
||||
/* run/task declared with <!> */
|
||||
if (svc->sighup)
|
||||
svc->once = 1;
|
||||
|
||||
@@ -461,6 +461,9 @@ restart:
|
||||
/* First reload all *.conf in /etc/finit.d/ */
|
||||
conf_reload();
|
||||
|
||||
/* Handle remain tasks that need to run post script before restart */
|
||||
service_runtask_clean();
|
||||
|
||||
/*
|
||||
* Then, mark all affected service conditions as in-flux and
|
||||
* let all affected services move to WAITING/HALTED
|
||||
|
||||
@@ -344,6 +344,10 @@ svc_t *svc_stop_completed(void)
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->state == SVC_STOPPING_STATE && svc->pid > 1)
|
||||
return svc;
|
||||
|
||||
/* Also wait for remain tasks running their post script */
|
||||
if (svc_is_remain(svc) && svc->state == SVC_TEARDOWN_STATE && svc->pid > 1)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -139,6 +139,7 @@ typedef struct svc {
|
||||
svc_type_t type; /* Service, run, task, ... */
|
||||
char protect; /* Services like dbus-daemon & udev by Finit */
|
||||
char manual; /* run/task that require `initctl start foo` */
|
||||
char remain; /* run/task: stay in DONE state, run post: on stop */
|
||||
char nowarn; /* Skip or log warning if cmd missing or conflicts */
|
||||
const int dirty; /* 0: unmodified, 1: modified */
|
||||
const int removed;
|
||||
@@ -282,6 +283,7 @@ static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);}
|
||||
static inline int svc_is_forking (svc_t *svc) { return svc && svc->forking; }
|
||||
static inline int svc_is_manual (svc_t *svc) { return svc && svc->manual; }
|
||||
static inline int svc_is_remain (svc_t *svc) { return svc && svc->remain; }
|
||||
static inline int svc_is_noreload (svc_t *svc) { return svc && (0 == svc->sighup && 0 == svc->reload_script[0]); }
|
||||
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
|
||||
Reference in New Issue
Block a user