mirror of
https://github.com/troglobit/finit.git
synced 2026-10-13 10:14:47 +00:00
Add cleanup:script support, runs at service removal
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+14
-8
@@ -899,19 +899,20 @@ e.g., `halt:SIGPWR`. To change the delay between the stop signal and
|
||||
KILL, use the option `kill:<1-60>`, e.g., `kill:10` to wait 10 seconds
|
||||
before sending `SIGKILL`.
|
||||
|
||||
Services, including the `sysv` variant, support pre/post/ready scripts:
|
||||
Services, including the `sysv` variant, support pre/post/ready and
|
||||
cleanup scripts:
|
||||
|
||||
* `pre:[0-3600,]script`
|
||||
* `post:[0-3600,]script`
|
||||
* `ready:[0-3600,]script`
|
||||
* `pre:[0-3600,]script` -- called before the sysv/service is stated
|
||||
* `post:[0-3600,]script` -- called after the sysv/service has stopped
|
||||
* `ready:[0-3600,]script` -- called when the sysv/service is ready
|
||||
* `cleanup:[0-3600,]script` -- called when run/task/sysv/service is removed
|
||||
|
||||
The optional number (0-3600) is the timeout before Finit kills the
|
||||
script, it defaults to the kill delay value and can be disabled by
|
||||
setting it to zero. These scripts run as the same `@USER:GROUP` as the
|
||||
service itself, with any `env:file` sourced. The scripts must use an
|
||||
absolute path, but are executed from the `$HOME` of the given user. The
|
||||
scripts are not called with any argument, but get a set of environment
|
||||
variables:
|
||||
service itself, with any `env:file` sourced. The scripts are executed
|
||||
from the `$HOME` of the given user. The scripts are not called with any
|
||||
argument, but get a set of environment variables:
|
||||
|
||||
* `SERVICE_IDENT=foo:1`
|
||||
* `SERVICE_NAME=foo`
|
||||
@@ -925,6 +926,11 @@ variables:
|
||||
the program, if it exited normally, or the signal name (`HUP`,
|
||||
`TERM`, etc.) if it exited due to signal
|
||||
|
||||
When a run/task/sys/service is removed (disable + reload) it is first
|
||||
stopped and then removed from the runlevel. The `post:script` always
|
||||
runs when the process has stopped, and the `cleanup:script` runs when
|
||||
the the stanza has been removed from the runlevel.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> These script actions are intended for setup, cleanup, and readiness
|
||||
> notification. It is up to the user to ensure the scripts terminate.
|
||||
|
||||
+65
-8
@@ -1524,6 +1524,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
char *id = NULL, *env = NULL, *cgroup = NULL;
|
||||
char *pre_script = NULL, *post_script = NULL;
|
||||
char *ready_script = NULL, *conflict = NULL;
|
||||
char *cleanup_script = NULL;
|
||||
char ident[MAX_IDENT_LEN];
|
||||
char *ifstmt = NULL;
|
||||
char *notify = NULL;
|
||||
@@ -1630,6 +1631,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
post_script = arg;
|
||||
else if (MATCH_CMD(cmd, "ready:", arg))
|
||||
ready_script = arg;
|
||||
else if (MATCH_CMD(cmd, "cleanup:", arg))
|
||||
cleanup_script = arg;
|
||||
else if (MATCH_CMD(cmd, "env:", arg))
|
||||
env = arg;
|
||||
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
||||
@@ -1817,6 +1820,11 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
parse_script(svc, "ready", ready_script, &svc->ready_tmo, svc->ready_script, sizeof(svc->ready_script));
|
||||
else
|
||||
memset(svc->ready_script, 0, sizeof(svc->ready_script));
|
||||
if (cleanup_script)
|
||||
parse_script(svc, "cleanup", cleanup_script, &svc->cleanup_tmo, svc->cleanup_script, sizeof(svc->cleanup_script));
|
||||
else
|
||||
memset(svc->cleanup_script, 0, sizeof(svc->cleanup_script));
|
||||
|
||||
if (!svc_is_tty(svc)) {
|
||||
if (log)
|
||||
parse_log(svc, log);
|
||||
@@ -1916,8 +1924,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when cleaning up lingering (stopped) services
|
||||
* after a .conf reload.
|
||||
* This function is called at the end of a runlevel change or reload
|
||||
* command to delete all remnants of removed services.
|
||||
*
|
||||
* We need to ensure we properly stop the service before removing it,
|
||||
* including stopping any pending restart or SIGKILL timers before we
|
||||
@@ -1962,10 +1970,14 @@ void service_monitor(pid_t lost, int status)
|
||||
/* If the setup phase fails, drive svc to crashed. */
|
||||
svc->status = status;
|
||||
/* fallthrough */
|
||||
case SVC_TEARDOWN_STATE:
|
||||
case SVC_CLEANUP_STATE:
|
||||
dbg("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
|
||||
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
|
||||
lost, ok, sig, rc);
|
||||
(svc->state == SVC_TEARDOWN_STATE
|
||||
? svc->post_script
|
||||
: (svc->state == SVC_CLEANUP_STATE
|
||||
? svc->cleanup_script
|
||||
: svc->pre_script)), lost, ok, sig, rc);
|
||||
/* Kill all children in the same proess group, e.g. logit */
|
||||
dbg("Killing lingering children in same process group ...");
|
||||
kill(-svc->pid, SIGKILL);
|
||||
@@ -2245,6 +2257,41 @@ void service_ready_script(svc_t *svc)
|
||||
service_script_add(svc, pid, svc->ready_tmo);
|
||||
}
|
||||
|
||||
static void service_cleanup_script(svc_t *svc)
|
||||
{
|
||||
svc->pid = service_fork(svc);
|
||||
if (svc->pid < 0) {
|
||||
err(1, "Failed forking off %s cleanup:script %s", svc_ident(svc, NULL, 0), svc->cleanup_script);
|
||||
return;
|
||||
}
|
||||
|
||||
if (svc->pid == 0) {
|
||||
char buf[CMD_SIZE];
|
||||
char *argv[4] = {
|
||||
"sh",
|
||||
"-ac",
|
||||
buf,
|
||||
NULL
|
||||
};
|
||||
char *env_file;
|
||||
|
||||
redirect(svc);
|
||||
|
||||
env_file = svc_getenv(svc);
|
||||
if (env_file)
|
||||
snprintf(buf, sizeof(buf), ". %s; exec %s", env_file, svc->cleanup_script);
|
||||
else
|
||||
strlcpy(buf, svc->cleanup_script, sizeof(buf));
|
||||
|
||||
set_pre_post_envs(svc, "cleanup");
|
||||
execvp(_PATH_BSHELL, argv);
|
||||
_exit(EX_OSERR);
|
||||
}
|
||||
|
||||
dbg("%s: cleanup:script %s started as PID %d", svc_ident(svc, NULL, 0), svc->cleanup_script, svc->pid);
|
||||
service_timeout_after(svc, svc->cleanup_tmo, service_kill_script);
|
||||
}
|
||||
|
||||
static void service_retry(svc_t *svc)
|
||||
{
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
@@ -2431,6 +2478,11 @@ 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);
|
||||
@@ -2469,7 +2521,7 @@ restart:
|
||||
case SVC_TYPE_SYSV:
|
||||
case SVC_TYPE_TTY:
|
||||
if (svc_has_post(svc)) {
|
||||
svc_set_state(svc, SVC_CLEANUP_STATE);
|
||||
svc_set_state(svc, SVC_TEARDOWN_STATE);
|
||||
service_post_script(svc);
|
||||
} else
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
@@ -2489,9 +2541,14 @@ restart:
|
||||
}
|
||||
break;
|
||||
|
||||
case SVC_CLEANUP_STATE:
|
||||
if (!svc->pid)
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
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:
|
||||
|
||||
@@ -59,8 +59,9 @@ typedef enum {
|
||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||
SVC_CLEANUP_STATE, /* Running post: script */
|
||||
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 */
|
||||
@@ -196,13 +197,19 @@ typedef struct svc {
|
||||
char conflict[MAX_ARG_LEN];
|
||||
char desc[MAX_STR_LEN];
|
||||
char env[MAX_CMD_LEN];
|
||||
|
||||
char pre_script[MAX_CMD_LEN];
|
||||
int pre_tmo;
|
||||
|
||||
char post_script[MAX_CMD_LEN];
|
||||
int post_tmo;
|
||||
|
||||
char ready_script[MAX_CMD_LEN];
|
||||
int ready_tmo;
|
||||
|
||||
char cleanup_script[MAX_CMD_LEN];
|
||||
int cleanup_tmo;
|
||||
|
||||
/*
|
||||
* Used to forcefully kill services that won't shutdown on
|
||||
* termination and to delay restarts of crashing services.
|
||||
@@ -270,6 +277,7 @@ static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && s
|
||||
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
|
||||
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
|
||||
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
|
||||
static inline int svc_has_cleanup (svc_t *svc) { return svc->cleanup_script[0]; }
|
||||
|
||||
static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
|
||||
@@ -390,12 +398,15 @@ static inline char *svc_status(svc_t *svc)
|
||||
return "stopping";
|
||||
}
|
||||
|
||||
case SVC_CLEANUP_STATE:
|
||||
return "cleanup";
|
||||
case SVC_TEARDOWN_STATE:
|
||||
return "teardown";
|
||||
|
||||
case SVC_SETUP_STATE:
|
||||
return "setup";
|
||||
|
||||
case SVC_CLEANUP_STATE:
|
||||
return "setup";
|
||||
|
||||
case SVC_PAUSED_STATE:
|
||||
return "paused";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user