From ab723d2f3186f2702c71aa58eb72fc451233097c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sat, 30 Dec 2017 20:07:38 +0100 Subject: [PATCH] Add support for a `rescue` (recovery) mode from kernel cmdline This patch adds a rescue (revovery) mode to Finit. Simply add `rescue` to the kernel cmdline at boot and the following steps at bootstrap will be skipped: - Load of services/run/task/etc in /etc/finit.conf + /etc/finit.d/*.conf - fsck of filesystems in /etc/fstab - Remount of / to read-write - Mount of all filesystems in /etc/fstab - swapon - A few plugin hooks: - HOOK_ROOTFS_UP - HOOK_MOUNT_ERROR - HOOK_BASEFS_UP - `runparts DIR` - /etc/rc.local Instead of loading /etc/finit.conf and /etc/finit.d/*.conf Finit loads the file /lib/finit/rescue.conf, which can be overridden by the operator if needed. If this file is removed (or lost), Finit falls back to start a simple root shell, without any login. Signed-off-by: Joachim Nilsson --- configure.ac | 3 +++ doc/TODO.md | 3 --- src/Makefile.am | 2 ++ src/conf.c | 20 ++++++++++++++++++++ src/finit.c | 49 +++++++++++++++++++++++++++++-------------------- src/finit.h | 1 + src/rescue.conf | 3 +++ 7 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 src/rescue.conf diff --git a/configure.ac b/configure.ac index 161a44d6..afdf8259 100644 --- a/configure.ac +++ b/configure.ac @@ -217,6 +217,9 @@ AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins") AC_SUBST(plugin_path) AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path]) +AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf") +AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf]) + # Generate all files AC_OUTPUT diff --git a/doc/TODO.md b/doc/TODO.md index c8fd0b28..d6bf8aa0 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -21,9 +21,6 @@ can also be used. General ------- -* Add support for `rescue` on the kernel command line, start rescue mode - if enabled with `configure` script. Could mean skip `/etc/finit.conf` - et al and jump straight to a single getty login on console. * Add support for timed shutdown in Finit, including cancelled shutdown. * SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add API restart to SIGHUP callback for the same functionality in Finit. diff --git a/src/Makefile.am b/src/Makefile.am index 341e82ba..e75bc9f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -58,6 +58,7 @@ REBOOT_LNKS = shutdown halt poweroff suspend # Hook in install to add finit and reboot symlink(s) install-exec-hook: + @$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir) @for file in $(INIT_LNKS); do \ lnk=$(DESTDIR)$(sbindir)/$$file; \ if [ -e $$lnk ]; then \ @@ -80,6 +81,7 @@ install-exec-hook: fi uninstall-hook: + @$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf @for file in $(INIT_LNKS); do \ lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \ if [ "$$lnk" != "finit" ]; then \ diff --git a/src/conf.c b/src/conf.c index 94b5d1a5..2912769f 100644 --- a/src/conf.c +++ b/src/conf.c @@ -78,6 +78,9 @@ void conf_parse_cmdline(void) /* Catches finit_debug (deprecated), --debug, and debug */ if (strstr(tok, "debug")) dbg = 1; + + if (strstr(tok, "rescue") || strstr(tok, "recover")) + rescue = 1; } fclose(fp); @@ -551,6 +554,19 @@ int conf_reload(void) svc_mark_dynamic(); tty_mark(); + if (rescue) { + int rc; + char line[80] = "tty [12345] @console noclear nologin"; + + /* If rescue.conf is missing, fall back to a root shell */ + rc = parse_conf(RESCUE_CONF); + if (rc) + tty_register(line, global_rlimit, NULL); + + print(rc, "Entering rescue mode"); + goto done; + } + /* First, read /etc/finit.conf */ parse_conf(FINIT_CONF); @@ -795,6 +811,10 @@ int conf_monitor(uev_ctx_t *ctx) { int rc = 0; + /* Skip second run, when called from finit.c in rescue mode */ + if (ctx && rescue) + return 0; + /* * If only one watcher fails, that's OK. A user may have only * one of /etc/finit.conf or /etc/finit.d in use, and may also diff --git a/src/finit.c b/src/finit.c index 30f636ae..7c23c4b6 100644 --- a/src/finit.c +++ b/src/finit.c @@ -53,6 +53,7 @@ int wdogpid = 0; /* No watchdog by default */ int runlevel = 0; /* Bootstrap 'S' */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int prevlevel = -1; +int rescue = 0; /* rescue mode from kernel cmdline */ char *sdown = NULL; char *network = NULL; char *hostname = NULL; @@ -262,18 +263,20 @@ static void finalize(void) /* * Network stuff */ - _d("Setting up networking ..."); - networking(); - umask(022); + if (!rescue) { + _d("Setting up networking ..."); + networking(); - /* Hooks that rely on loopback, or basic networking being up. */ - _d("Calling all network up hooks ..."); - plugin_run_hooks(HOOK_NETWORK_UP); + /* Hooks that rely on loopback, or basic networking being up. */ + _d("Calling all network up hooks ..."); + plugin_run_hooks(HOOK_NETWORK_UP); + } + umask(022); /* * Run startup scripts in the runparts directory, if any. */ - if (runparts && fisdir(runparts)) { + if (runparts && fisdir(runparts) && !rescue) { _d("Running startup scripts in %s ...", runparts); run_parts(runparts, NULL); if (conf_any_change()) @@ -296,7 +299,7 @@ static void finalize(void) service_step_all(SVC_TYPE_ANY); /* Convenient SysV compat for when you just don't care ... */ - if (!access(FINIT_RC_LOCAL, X_OK)) { + if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) { run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL); if (conf_any_change()) service_reload_dynamic(); @@ -385,7 +388,7 @@ int main(int argc, char* argv[]) /* * Check file filesystems in /etc/fstab */ - for (int pass = 1; pass < 10; pass++) { + for (int pass = 1; pass < 10 && !rescue; pass++) { if (fsck(pass)) break; } @@ -479,12 +482,14 @@ int main(int argc, char* argv[]) /* * Mount filesystems */ + if (!rescue) { #ifdef REMOUNT_ROOTFS - run("mount -n -o remount,rw /"); + run("mount -n -o remount,rw /"); #endif #ifdef SYSROOT - mount(SYSROOT, "/", NULL, MS_MOVE, NULL); + mount(SYSROOT, "/", NULL, MS_MOVE, NULL); #endif + } /* Debian has this little script to copy generated rules while the system was read-only */ if (fexist("/lib/udev/udev-finish")) @@ -493,15 +498,17 @@ int main(int argc, char* argv[]) /* Bootstrap conditions, needed for hooks */ cond_init(); - _d("Root FS up, calling hooks ..."); - plugin_run_hooks(HOOK_ROOTFS_UP); + if (!rescue) { + _d("Root FS up, calling hooks ..."); + plugin_run_hooks(HOOK_ROOTFS_UP); - umask(0); - if (run_interactive("mount -na", "Mounting filesystems")) - plugin_run_hooks(HOOK_MOUNT_ERROR); + umask(0); + if (run_interactive("mount -na", "Mounting filesystems")) + plugin_run_hooks(HOOK_MOUNT_ERROR); - run("swapon -ea"); - umask(0022); + run("swapon -ea"); + umask(0022); + } /* Base FS up, enable standard SysV init signals */ sig_setup(&loop); @@ -512,8 +519,10 @@ int main(int argc, char* argv[]) */ conf_monitor(&loop); - _d("Base FS up, calling hooks ..."); - plugin_run_hooks(HOOK_BASEFS_UP); + if (!rescue) { + _d("Base FS up, calling hooks ..."); + plugin_run_hooks(HOOK_BASEFS_UP); + } /* * Initalize state machine and start all bootstrap tasks diff --git a/src/finit.h b/src/finit.h index f60f4ef8..f61e57b4 100644 --- a/src/finit.h +++ b/src/finit.h @@ -96,6 +96,7 @@ extern int wdogpid; extern int runlevel; extern int cfglevel; extern int prevlevel; +extern int rescue; extern char *rcsd; extern char *sdown; extern char *network; diff --git a/src/rescue.conf b/src/rescue.conf new file mode 100644 index 00000000..9769f55d --- /dev/null +++ b/src/rescue.conf @@ -0,0 +1,3 @@ +runlevel 1 +hostname rescue +tty [12345] @console noclear nologin