From b53c7e687942743aca6151ac4d8d160721ad18d4 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 16 Aug 2026 12:18:28 +0200 Subject: [PATCH] dbus: mirror legacy runlevel edge cases in Manager1.SetRunlevel The bus method called sm_runlevel() unconditionally. In runlevel 0 and 6 that aborts an in-flight shutdown, which INIT_CMD_RUNLVL refuses with a warning, and during bootstrap it switches immediately where the legacy path defers via cfglevel to the end of runlevel S. Port both. A bad runlevel argument still returns InvalidArgs, where the legacy protocol acks silently: a typed interface rejects garbage. Signed-off-by: Joachim Wiberg --- src/api.c | 23 +++-------------------- src/dbus.c | 4 +--- src/sm.c | 24 ++++++++++++++++++++++++ src/sm.h | 1 + 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/api.c b/src/api.c index be14e215..a7f3f4d8 100644 --- a/src/api.c +++ b/src/api.c @@ -233,6 +233,7 @@ static void bypass_shutdown(void *unused) do_shutdown(halt); } + /* * Handle switch_root API command. * Parses data: "newroot\0newinit\0" @@ -370,8 +371,8 @@ static void api_cb(uev_t *w, void *arg, int events) { static svc_t *iter = NULL; struct init_request rq; - int sd, lvl; svc_t *svc; + int sd; (void)arg; @@ -446,14 +447,6 @@ 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': @@ -462,17 +455,7 @@ static void api_cb(uev_t *w, void *arg, int events) case '0'...'9': dbg("Setting new runlevel %c", rq.runlevel); - lvl = rq.runlevel - '0'; - if (lvl == 0) - halt = SHUT_OFF; - if (lvl == 6) - halt = SHUT_REBOOT; - - /* User requested change in next runlevel */ - if (runlevel == INIT_LEVEL) - cfglevel = lvl; - else - sm_runlevel(lvl); + sm_request_runlevel(rq.runlevel - '0'); break; default: diff --git a/src/dbus.c b/src/dbus.c index 138bb4a6..a5236240 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -454,9 +454,7 @@ static int manager_set_runlevel(link_call_t *call, void *userdata) "org.freedesktop.DBus.Error.InvalidArgs", "runlevel must be 0-9 (excluding internal levels)"); - if (lvl == 0) halt = SHUT_OFF; - if (lvl == 6) halt = SHUT_REBOOT; - sm_runlevel((int)lvl); + sm_request_runlevel((int)lvl); (void)link_call_reply(call); return 0; diff --git a/src/sm.c b/src/sm.c index 44d0972d..35a46e3e 100644 --- a/src/sm.c +++ b/src/sm.c @@ -94,6 +94,30 @@ static void shutdown_wdt_cb(uev_t *w, void *arg, int events) do_shutdown(halt); } +/* + * User-requested runlevel change, shared by the legacy API and the + * D-Bus SetRunlevel method. Refused in runlevel 0/6, deferred via + * cfglevel during bootstrap. + */ +void sm_request_runlevel(int lvl) +{ + if (IS_RESERVED_RUNLEVEL(runlevel) && runlevel != INIT_LEVEL) { + warnx("Cannot abort runlevel 6/0."); + return; + } + + if (lvl == 0) + halt = SHUT_OFF; + if (lvl == 6) + halt = SHUT_REBOOT; + + /* Runlevel S: user requested change in next runlevel */ + if (runlevel == INIT_LEVEL) + cfglevel = lvl; + else + sm_runlevel(lvl); +} + /* * Console input callback - handles Ctrl-C during bootstrap wait */ diff --git a/src/sm.h b/src/sm.h index 3c00f572..f2873861 100644 --- a/src/sm.h +++ b/src/sm.h @@ -35,6 +35,7 @@ void sm_step (void); int sm_in_reload (void); void sm_reload (void); void sm_runlevel (int newlevel); +void sm_request_runlevel(int lvl); #endif /* FINIT_SM_H_ */