diff --git a/doc/plugins.md b/doc/plugins.md index 6505418b..2e6ed4a2 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -104,7 +104,23 @@ Hooks ----- In the below listings, the first label is the hook point for a C plugin, -the second is the condition name and hook script path. +the second is the condition name and hook script path. A hook script is +a plain shell script, or program, that does a very small dedicated job +at the below hook points. + +**Example:** + + $ mkdir -p /libexec/finit/hook/sys/down + $ cat </libexec/finit/hook/sys/foo.sh + #!/bin/sh + echo 'I run just before the reboot() syscall at shutdown/reboot' + echo 'I have access to /dev since devtmpfs is exempt from umount' + exit 0 + EOF + $ chmod +x /libexec/finit/hook/sys/foo.sh + +> **Note:** to use hook scripts, even for pre-bootstrap and pre-shutdown +> tasks, you must build with `configure --enable-hook-scripts-plugin`. ### Bootstrap Hooks @@ -150,6 +166,15 @@ the second is the condition name and hook script path. * `HOOK_SHUTDOWN`, `hook/sys/shutdown`: Called at shutdown/reboot, right before all services are sent `SIGTERM` +* `HOOK_SVC_DN`, `hook/svc/down`: In shutdown/reboot, all services and + non-reserved processes have been killed. **Note:** only hook scripts + can run here! + +* `HOOK_SYSTEM_DN`, `hook/sys/down`: In shutdown/reboot, called after + all non-reserved file systems have been unmounted, just before Finit + tells the kernel to reboot or shut down. **Note:** only hook scripts + can run here! + Plugins like `tty.so` extend finit by acting on events, they are called I/O plugins and are called from the finit main loop when `poll()` detects an event. See the source code for `plugins/*.c` for more help diff --git a/plugins/hook-scripts.c b/plugins/hook-scripts.c index afd3382f..d67d25d0 100644 --- a/plugins/hook-scripts.c +++ b/plugins/hook-scripts.c @@ -26,18 +26,11 @@ #include "plugin.h" #include "config.h" -#define CHOOSE(x, y) y -static const char *hscript_paths[] = HOOK_TYPES; -#undef CHOOSE +extern void plugin_script_run(hook_point_t no); -static void hscript_run_parts(hook_point_t hook) +static void hscript_run_parts(hook_point_t no) { - char path[CMD_SIZE] = ""; - - strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path)); - strlcat(path, hscript_paths[hook] + 4, sizeof(path)); - - run_parts(path, NULL); + plugin_script_run(no); } static void hscript_banner(void *arg) @@ -96,6 +89,14 @@ static plugin_t plugin = { .hook[HOOK_SVC_UP] = { .cb = hscript_svc_up }, .hook[HOOK_SYSTEM_UP] = { .cb = hscript_system_up }, .hook[HOOK_SHUTDOWN] = { .cb = hscript_shutdown }, +/* + * We have exited here, so these are handled a bit differently by Finit, + * when hook scripts are enabled, just to provide the user with the same + * interface: + * + * .hook[HOOK_SVC_DN] = { .cb = hscript_svc_down }, + * .hook[HOOK_SYS_DN] = { .cb = hscript_system_down }, + */ }; PLUGIN_INIT(plugin_init) diff --git a/src/plugin.c b/src/plugin.c index eb1f0b9c..e0222493 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -195,11 +195,40 @@ int plugin_exists(hook_point_t no) return 0; } +#ifdef HAVE_HOOK_SCRIPTS_PLUGIN +#define CHOOSE(x, y) y +static const char *hscript_paths[] = HOOK_TYPES; +#undef CHOOSE + +void plugin_script_run(hook_point_t no) +{ + char path[CMD_SIZE] = ""; + + strlcat(path, PLUGIN_HOOK_SCRIPTS_PATH, sizeof(path)); + strlcat(path, hscript_paths[no] + 4, sizeof(path)); + + run_parts(path, NULL); +} +#else +void plugin_script_run(hook_point_t no) +{ + (void)no; +} +#endif + /* Some hooks are called with a fixed argument */ void plugin_run_hook(hook_point_t no, void *arg) { plugin_t *p, *tmp; + if (!cond_is_available()) { +#ifdef HAVE_HOOK_SCRIPTS_PLUGIN + warnx("conditions not available, calling script script based hooks only!"); + plugin_script_run(no); +#endif + return; + } + PLUGIN_ITERATOR(p, tmp) { if (p->hook[no].cb) { dbg("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg ?: "NIL"); diff --git a/src/plugin.h b/src/plugin.h index f528fd1f..9fe13262 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -82,6 +82,8 @@ \ /* Shutdown hooks, runlevel [06] */ \ CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \ + CHOOSE(HOOK_SVC_DN, "hook/svc/down"), \ + CHOOSE(HOOK_SYS_DN, "hook/sys/down"), \ CHOOSE(HOOK_MAX_NUM, "nop") \ } diff --git a/src/sig.c b/src/sig.c index 3de33e5f..e71c87dd 100644 --- a/src/sig.c +++ b/src/sig.c @@ -329,6 +329,9 @@ void do_shutdown(shutop_t op) while (waitpid(-1, NULL, WNOHANG) > 0) ; + /* All services and (non-critical) processes have stopped. */ + plugin_run_hooks(HOOK_SVC_DN); + if (in_cont) { if (osheading) logit(LOG_CONSOLE | LOG_NOTICE, "%s, shutting down container.", osheading); @@ -374,6 +377,9 @@ void do_shutdown(shutop_t op) /* Call mdadm to mark any RAID array(s) as clean before halting. */ mdadm_wait(); + /* Last chance to run scripts (all .so plugins have exited already) */ + plugin_run_hooks(HOOK_SYS_DN); + /* Reboot via watchdog or kernel, or shutdown? */ if (op == SHUT_REBOOT) { if (wdog && wdog->pid > 1) {