Skip to content

Commit

Permalink
Fix #369: allow runlevel change using initctl during bootstrap
Browse files Browse the repository at this point in the history
This change opens up the runlevel change API from bootstrap.  The twist
is that the change is only queued, i.e., the call `initctl runlevel 9`
during bootstrap only changes the configured runlevel to go to after
bootstrap has completed.

Effectively, this change allows overriding the `runlevel` directive in
/etc/finit.conf without having to change the file.

Signed-off-by: Joachim Wiberg <[email protected]>
  • Loading branch information
troglobit committed Oct 3, 2023
1 parent 338521a commit 2e73563
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 11 deletions.
21 changes: 13 additions & 8 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,19 @@ everything is OK.
task [S] /lib/console-setup/console-setup.sh
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS

After runlevel S, Finit proceeds to runlevel 2. (This can be changed in
`/etc/finit.conf` using the `runlevel N` directive.) Before starting
the services in runlevel 2, Finit first stops everything that is not
allowed to run in 2, and then brings up networking. Networking is
expected to be available in all runlevels except: S, 1 (single user
level), 6, and 0. Networking is enabled either by the `network script`
directive, or if you have an `/etc/network/interfaces` file, Finit calls
`ifup -a` -- at the very least the loopback interface is brought up.
After runlevel S, Finit proceeds to runlevel 2. This can be changed in
`/etc/finit.conf` using the `runlevel N` directive, or by a script
running in runlevel S that calls, e.g., `initctl runlevel 9`. The
latter is useful if startup scripts detect problems outside of Finit's
control, e.g., critical services/devices missing or hardware problems.

Before starting the services in runlevel 2, Finit first stops everything
that is not allowed to run in 2, and then brings up networking.
Networking is expected to be available in all runlevels except: S, 1
(single user level), 6, and 0. Networking is enabled either by the
`network script` directive, or if you have an `/etc/network/interfaces`
file, Finit calls `ifup -a` -- at the very least the loopback interface
is brought up.

> **Note:** when moving from runlevel S to 2, all run/task/services that
> were constrained to runlevel S only are dropped from bookkeeping. So
Expand Down
5 changes: 5 additions & 0 deletions man/initctl.8
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ Show top-like listing based on cgroups.
List installed plugins.
.It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot.
.Pp
If called at boot (runlevel S) to set the runlevel, Finit only schedules
the change, effectively overriding the configured runlevel from
.Pa /etc/finit.conf .
Useful to trigger a fail-safe mode, or similar.
.It Nm Ar reboot
Reboot system, default if
.Cm reboot
Expand Down
18 changes: 15 additions & 3 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ static void api_cb(uev_t *w, void *arg, int events)
}

switch (rq.cmd) {
case INIT_CMD_RUNLVL:
case INIT_CMD_RELOAD:
case INIT_CMD_START_SVC:
case INIT_CMD_RESTART_SVC:
Expand All @@ -378,7 +377,7 @@ static void api_cb(uev_t *w, void *arg, int events)
case INIT_CMD_POWEROFF:
case INIT_CMD_SUSPEND:
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("Unsupported command in runlevel S and 6.");
warnx("Unsupported command in runlevel S and 6/0.");
goto leave;
}
default:
Expand All @@ -387,6 +386,14 @@ static void api_cb(uev_t *w, void *arg, int events)

switch (rq.cmd) {
case INIT_CMD_RUNLVL:
/* Allow changing cfglevel in runlevel S */
if (IS_RESERVED_RUNLEVEL(runlevel)) {
if (runlevel != INIT_LEVEL) {
warnx("Cannot abort runlevel 6/0.");
break;
}
}

switch (rq.runlevel) {
case 's':
case 'S':
Expand All @@ -400,7 +407,12 @@ static void api_cb(uev_t *w, void *arg, int events)
halt = SHUT_OFF;
if (lvl == 6)
halt = SHUT_REBOOT;
service_runlevel(lvl);

/* User requested change in next runlevel */
if (runlevel == INIT_LEVEL)
cfglevel = lvl;
else
service_runlevel(lvl);
break;

default:
Expand Down

0 comments on commit 2e73563

Please sign in to comment.