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"