From 6ae1083c9992d432a4acce43930d6f30f0138521 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 24 Sep 2023 13:22:47 +0200 Subject: [PATCH] Add support for SysV-only scripts in runparts This patch extens the SysV compatibility support in Finit by adding support for limiting `runparts` to run only SNNfoo, or KNNfoo, style scripts from a directory. Additionally, by default `runparts` now runs entirely in the background without any progress. To enable progress, an optional argument has been added to the runparts command line. Signed-off-by: Joachim Wiberg --- doc/TODO.md | 10 ----- doc/config.md | 38 ++++++++--------- plugins/resolvconf.c | 2 +- src/conf.c | 22 +++++++++- src/finit.c | 1 - src/helpers.h | 2 +- src/plugin.c | 2 +- src/runparts.c | 18 +++++--- test/Makefile.am | 7 +++- test/runparts.sh | 4 +- test/skel/etc/rcS.d/S01abc.sh | 3 ++ test/skel/etc/rcS.d/S02def.sh | 1 + test/skel/etc/rcS.d/foo.sh | 3 ++ test/skel/etc/rcS.d/test.sh | 3 ++ .../usr/share/{runlevel => runparts}/ref.log | 0 test/skel/usr/share/runparts/sysv.log | 2 + test/sysvparts.sh | 42 +++++++++++++++++++ 17 files changed, 118 insertions(+), 42 deletions(-) create mode 100755 test/skel/etc/rcS.d/S01abc.sh create mode 120000 test/skel/etc/rcS.d/S02def.sh create mode 100755 test/skel/etc/rcS.d/foo.sh create mode 100755 test/skel/etc/rcS.d/test.sh rename test/skel/usr/share/{runlevel => runparts}/ref.log (100%) create mode 100644 test/skel/usr/share/runparts/sysv.log create mode 100755 test/sysvparts.sh diff --git a/doc/TODO.md b/doc/TODO.md index 05d33d80..c97281d1 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -9,16 +9,6 @@ value in the project turned out to be: > systems, container, and dedicated server applications. -Before Next Release -------------------- - -* When did runparts +arg break? -* Should we update the =runparts DIR= syntax to =runparts [progress] [debug] DIR=? -* Add SNN-only option top runparts, for Buildroot -* Calling `initctl runlevel` at bootstrap => N 10, what should it be? -* Reindent all shell scripts in true emacs shell-script-mode (4 chars) - - Misc TODOs ---------- diff --git a/doc/config.md b/doc/config.md index 539cfdd7..8d61e4bd 100644 --- a/doc/config.md +++ b/doc/config.md @@ -842,7 +842,7 @@ the above `kill:SEC` syntax. ### Run-parts Scripts -**Syntax:** `runparts ` +**Syntax:** `runparts [progress] [sysv] ` Call [run-parts(8)][] on `DIR` to run start scripts. All executable files, or scripts, in the directory are called, in alphabetic order. @@ -854,25 +854,23 @@ services, which Finit will then apply when changing runlevel from S to whatever the next runlevel is set to be (default 2). E.g., generate a `/etc/chrony.conf` and call `initctl enable chronyd`. +**Options:** + + - `progress`: display the progress of each script being executed + - `sysv`: run only SysV style scripts, i.e., `SNNfoo`, or `KNNbar`, + where `NN` is a number (0-99). + +If global debug mode is enabled, the `runparts` program is also called +with the debug flag. + **Limitations:** Scripts called from `runparts`, or hook scripts (see below), are limited -in what they can do and expect from Finit: - - 1. Finit is single threaded and all scripts executed from these two - mechanisms are limited to file system operations (early or late - hooks may not have write access!) and standard UNIX programs. - 2. Thus, it is *not possible* to call `initctl` to query status of - services since that calls Finit over the `/run/finit/socket`. - 3. Confusingly enough, there are some commands to `initctl` that are - available; `ls`, `enable`, `disable`, `touch`, `create`, `delete`, - and all the `cond` commands. Because all these are basically just - wrappers for manipulating files (again, write access for early/late - hooks may not be available). - -Also, similar to the `/etc/rc.local` shell script, make sure that all -your services and programs either terminate or start in the background -or you will block Finit. +in their interaction with Finit. Like the standalone `run` stanza and +the `/etc/rc.local` shell script, Finit waits for their completion +before continuing. None of them can issue commands to start, stop, or +restart other services. Also, ensure all your services and programs +either terminate or start in the background or you will block Finit. > **Note:** `runparts` scrips are only read and executed in runlevel S > ([bootstrap][]). See [hook scripts](plugins.md#hooks) for other ways @@ -1073,8 +1071,10 @@ sync(2) has been called, twice. SysV Init Compatibility ----------------------- -Finit comes with a few SysV Init compatibility features to ease the -transition from a serialized boot process. +It is not possible to run unmodified SysV init systems with Finit. This +was never the intention and is not the strength of Finit. However, it +comes with a few SysV Init compatibility features to ease the transition +from a serialized boot process. ### `runparts DIRECTORY` diff --git a/plugins/resolvconf.c b/plugins/resolvconf.c index 67d062a0..4ec89faa 100644 --- a/plugins/resolvconf.c +++ b/plugins/resolvconf.c @@ -45,7 +45,7 @@ static void setup(void *arg) touch("/etc/resolvconf/run/enable-updates"); chdir("/etc/resolvconf/run/interface"); - run_parts("/etc/resolvconf/update.d", "-i", NULL, 0); + run_parts("/etc/resolvconf/update.d", "-i", NULL, 0, 0); chdir("/"); } diff --git a/src/conf.c b/src/conf.c index da77e3b7..983648a9 100644 --- a/src/conf.c +++ b/src/conf.c @@ -82,6 +82,10 @@ char *fsck_mode = ""; char *fsck_repair = "-p"; #endif +char *runparts = NULL; +int runparts_progress; +int runparts_sysv; + char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */ struct conf_change { @@ -795,6 +799,15 @@ static int parse_static(char *line, int is_rcsd) if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (runparts) free(runparts); + runparts_progress = runparts_sysv = 0; + while (x) { + if (MATCH_CMD(x, "progress", x)) + runparts_progress = 1; + else if (MATCH_CMD(x, "sysv", x)) + runparts_sysv = 1; + else + break; + } runparts = strdup(strip_line(x)); return 0; } @@ -1480,7 +1493,14 @@ int conf_init(uev_ctx_t *ctx) */ if (runparts && fisdir(runparts) && !rescue) { char conf[sizeof(_PATH_RUNPARTS) + strlen(runparts) + 100]; - char *args = debug ? "-d" : "-p"; + char args[10] = { 0 }; + + if (debug) + strlcat(args, "-d ", sizeof(args)); + if (runparts_progress) + strlcat(args, "-p ", sizeof(args)); + if (runparts_sysv) + strlcat(args, "-s ", sizeof(args)); snprintf(conf, sizeof(conf), "[S] log:console %s %s %s" " -- Calling runparts %s in the background", diff --git a/src/finit.c b/src/finit.c index 1691effa..a8b027c2 100644 --- a/src/finit.c +++ b/src/finit.c @@ -75,7 +75,6 @@ char *fstab = NULL; char *sdown = NULL; char *network = NULL; char *hostname = NULL; -char *runparts = NULL; char *osheading = NULL; uev_ctx_t *ctx = NULL; /* Main loop context */ diff --git a/src/helpers.h b/src/helpers.h index e930dc65..fb2eb2f2 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -89,7 +89,7 @@ int exec_runtask (char *cmd, char *args[]); pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]); pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]); pid_t run_bg (char *cmd, char *args[]); -int run_parts (char *dir, char *cmd, const char *env[], int progress); +int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv); /* * Defaults to user "root" and group "wheel" (root) if: diff --git a/src/plugin.c b/src/plugin.c index cd80bdd9..29c88498 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -230,7 +230,7 @@ void plugin_script_run(hook_point_t no) } else env[2] = NULL; - run_parts(path, NULL, env, 0); + run_parts(path, NULL, env, 0, 0); } #else void plugin_script_run(hook_point_t no) diff --git a/src/runparts.c b/src/runparts.c index cd95f7fc..5af2c2d3 100644 --- a/src/runparts.c +++ b/src/runparts.c @@ -83,7 +83,7 @@ static void run_env(const char *env[]) } } -int run_parts(char *dir, char *cmd, const char *env[], int progress) +int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv) { size_t cmdlen = cmd ? strlen(cmd) : strlen("start"); struct dirent **d; @@ -127,6 +127,11 @@ int run_parts(char *dir, char *cmd, const char *env[], int progress) continue; } + if (sysv && name[0] != 'S' && name[0] != 'K') { + dbg("S-only flag set, skipping non-SysV script: %s", name); + continue; + } + /* If the callee didn't supply a run_parts() argument */ if (!cmd) { /* Check if Sservice or Kservice notation is used */ @@ -179,16 +184,16 @@ int run_parts(char *dir, char *cmd, const char *env[], int progress) #ifndef __FINIT__ static int usage(int rc) { - warnx("usage: runparts [-dh?] DIRECTORY"); + warnx("usage: runparts [-dhps?] DIRECTORY"); return rc; } int main(int argc, char *argv[]) { - int rc, c, progress = 0; + int rc, c, progress = 0, sysv = 0; char *dir; - while ((c = getopt(argc, argv, "dh?p")) != EOF) { + while ((c = getopt(argc, argv, "dh?ps")) != EOF) { switch(c) { case 'd': debug = 1; @@ -199,6 +204,9 @@ int main(int argc, char *argv[]) case 'p': progress = 1; break; + case 's': + sysv = 1; + break; default: return usage(1); } @@ -213,7 +221,7 @@ int main(int argc, char *argv[]) prctl(PR_SET_CHILD_SUBREAPER, 1); - rc = run_parts(dir, NULL, NULL, progress); + rc = run_parts(dir, NULL, NULL, progress, sysv); if (rc == -1) err(1, "failed run-parts %s", dir); diff --git a/test/Makefile.am b/test/Makefile.am index 4fd6dbeb..4413f94e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,9 +2,12 @@ SUBDIRS = lib src EXTRA_DIST = skel/bin/busybox-x86_64.sha256 skel/sbin/service.sh skel/etc/env \ skel/bin/pre.sh skel/bin/post.sh skel/bin/ready.sh skel/bin/slay \ - skel/sbin/fail.sh skel/usr/share/runlevel/ref.log \ + skel/sbin/fail.sh skel/usr/share/runparts/ref.log \ + skel/usr/share/runparts/sysv.log \ skel/etc/rc.d/foo.sh skel/etc/rc.d/S01abc.sh \ skel/etc/rc.d/test.sh skel/etc/rc.d/S02def.sh \ + skel/etc/rcS.d/foo.sh skel/etc/rcS.d/test.sh \ + skel/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \ skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \ skel/etc/inittab skel/etc/hostname skel/etc/fstab \ skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \ @@ -41,6 +44,7 @@ EXTRA_DIST += runlevel.sh EXTRA_DIST += run-restart-forever.sh EXTRA_DIST += run-task-tricks.sh EXTRA_DIST += runparts.sh +EXTRA_DIST += sysvparts.sh EXTRA_DIST += start-stop-service.sh EXTRA_DIST += start-stop-service-sub-config.sh EXTRA_DIST += start-kill-service.sh @@ -80,6 +84,7 @@ TESTS += rclocal.sh TESTS += ready-serv.sh TESTS += restart-self.sh TESTS += runlevel.sh +TESTS += sysvparts.sh TESTS += run-restart-forever.sh TESTS += run-task-tricks.sh TESTS += runparts.sh diff --git a/test/runparts.sh b/test/runparts.sh index 44d5f06c..6425bbcf 100755 --- a/test/runparts.sh +++ b/test/runparts.sh @@ -37,5 +37,5 @@ while true; do sleep 1 done -texec diff -u /usr/share/runlevel/ref.log /tmp/runparts.log -texec cmp /usr/share/runlevel/ref.log /tmp/runparts.log || fail "runparts in wrong order" +texec diff -u /usr/share/runparts/ref.log /tmp/runparts.log +texec cmp /usr/share/runparts/ref.log /tmp/runparts.log || fail "runparts in wrong order" diff --git a/test/skel/etc/rcS.d/S01abc.sh b/test/skel/etc/rcS.d/S01abc.sh new file mode 100755 index 00000000..bfe879a2 --- /dev/null +++ b/test/skel/etc/rcS.d/S01abc.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "$0 $*" >> /tmp/sysv.log diff --git a/test/skel/etc/rcS.d/S02def.sh b/test/skel/etc/rcS.d/S02def.sh new file mode 120000 index 00000000..21643413 --- /dev/null +++ b/test/skel/etc/rcS.d/S02def.sh @@ -0,0 +1 @@ +S01abc.sh \ No newline at end of file diff --git a/test/skel/etc/rcS.d/foo.sh b/test/skel/etc/rcS.d/foo.sh new file mode 100755 index 00000000..47c35711 --- /dev/null +++ b/test/skel/etc/rcS.d/foo.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "$0" >> /tmp/sysv.log diff --git a/test/skel/etc/rcS.d/test.sh b/test/skel/etc/rcS.d/test.sh new file mode 100755 index 00000000..47c35711 --- /dev/null +++ b/test/skel/etc/rcS.d/test.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "$0" >> /tmp/sysv.log diff --git a/test/skel/usr/share/runlevel/ref.log b/test/skel/usr/share/runparts/ref.log similarity index 100% rename from test/skel/usr/share/runlevel/ref.log rename to test/skel/usr/share/runparts/ref.log diff --git a/test/skel/usr/share/runparts/sysv.log b/test/skel/usr/share/runparts/sysv.log new file mode 100644 index 00000000..756a413c --- /dev/null +++ b/test/skel/usr/share/runparts/sysv.log @@ -0,0 +1,2 @@ +/etc/rcS.d/S01abc.sh start +/etc/rcS.d/S02def.sh start diff --git a/test/sysvparts.sh b/test/sysvparts.sh new file mode 100755 index 00000000..a4675f6e --- /dev/null +++ b/test/sysvparts.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Verifies SysV runparts behavior: to run all scripts in a directory at +# the end of system bootstrap, right before calling /etc/rc.local: +# +# - only SNNfoo or KNNfoo executable scripts should be called +# - always in alphabetical order +# - SNNfoo should be called with a 'start' argument +# - KNNfoo should be called with a 'stop' argument +# + +TEST_DIR=$(dirname "$0") +#DEBUG=1 + +# shellcheck disable=SC2034 +BOOTSTRAP="runparts sysv /etc/rcS.d" + +# shellcheck source=/dev/null +. "$TEST_DIR/lib/setup.sh" + +test_setup() +{ + say "Test start $(date)" + } + +test_teardown() +{ + say "Test done $(date)" +} + +while true; do + lvl=$(texec initctl runlevel) + say "Current runlevel $lvl" + if [ "$lvl" = "N 2" ]; then + break; + fi + sleep 1 +done + +texec cat /tmp/sysv.log +texec diff -u /usr/share/runparts/sysv.log /tmp/sysv.log +texec cmp /usr/share/runparts/sysv.log /tmp/sysv.log || fail "runparts sysv in wrong order"