dbus: honor the shutdown timeout in the reboot family

initctl -t N reboot arms an emergency bypass timer over the legacy
socket, but the bus methods took no argument, so the timeout was
silently dropped whenever D-Bus was up.

Reboot, Halt, and Poweroff now take a timeout in seconds, 0 for
none, armed via the same shutdown_bypass() the legacy path uses.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-08-16 22:03:38 +02:00
parent 8b61aaea1a
commit 62b55d5b43
6 changed files with 55 additions and 24 deletions
+4 -4
View File
@@ -95,7 +95,7 @@ and the service registry.
| `SetDebug` | — | — | yes | Toggle Finit's runtime debug flag |
| `Signal` | `su` | — | yes | Send signal `u` (1–31) to services matching identity `s` |
| `Suspend` | — | — | yes | `sync()` + suspend-to-RAM |
| `Reboot`, `Halt`, `Poweroff` | — | — | yes | Trigger the corresponding shutdown sequence |
| `Reboot`, `Halt`, `Poweroff` | `u` | — | yes | Trigger the corresponding shutdown sequence; the argument arms the emergency fallback timer (seconds, 0 = none) |
### Properties
@@ -250,9 +250,9 @@ the following subcommands use the bus first:
| `initctl restart` | `Manager1.Restart(svc)` |
| `initctl reload` | `Manager1.Reload()` |
| `initctl reload S` | `Service1.Reload()` (per-svc) |
| `initctl reboot` | `Manager1.Reboot()` |
| `initctl halt` | `Manager1.Halt()` |
| `initctl poweroff` | `Manager1.Poweroff()` |
| `initctl reboot` | `Manager1.Reboot(timeout)` |
| `initctl halt` | `Manager1.Halt(timeout)` |
| `initctl poweroff` | `Manager1.Poweroff(timeout)` |
| `initctl suspend` | `Manager1.Suspend()` |
| `initctl debug` | `Manager1.SetDebug()` |
| `initctl signal` | `Manager1.Signal(svc, signo)` |
+1 -14
View File
@@ -222,16 +222,6 @@ static svc_t *do_find_byc(char *buf, size_t len)
return svc_find_by_cond(input);
}
static void bypass_shutdown(void *);
struct wq emergency = { .cb = bypass_shutdown };
static void bypass_shutdown(void *unused)
{
(void)unused;
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt);
}
/*
@@ -298,10 +288,7 @@ static int do_reboot(int cmd, int timeout, char *buf, size_t len)
return 255;
}
if (timeout > 0) {
emergency.delay = timeout * 1000;
schedule_work(&emergency);
}
shutdown_bypass(timeout);
switch (cmd) {
case INIT_CMD_REBOOT:
+10 -3
View File
@@ -462,11 +462,18 @@ static int manager_set_runlevel(link_call_t *call, void *userdata)
static int dbus_shutdown(link_call_t *call, shutop_t target, int level)
{
uint32_t timeout;
if (link_call_read_u32(call, &timeout) < 0)
return link_call_reply_error(call,
"org.freedesktop.DBus.Error.InvalidArgs",
"expected (u)");
if (IS_RESERVED_RUNLEVEL(runlevel))
return link_call_reply_error(call,
"org.finit.Error.WrongRunlevel",
"Already in shutdown");
halt = target;
shutdown_bypass((int)timeout);
sm_runlevel(level);
(void)link_call_reply(call);
return 0;
@@ -623,11 +630,11 @@ static const link_method_t manager_methods[] = {
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_reload },
{ .name = "SetRunlevel", .in_sig = "u", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_set_runlevel },
{ .name = "Reboot", .in_sig = "", .out_sig = "",
{ .name = "Reboot", .in_sig = "u", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_reboot },
{ .name = "Poweroff", .in_sig = "", .out_sig = "",
{ .name = "Poweroff", .in_sig = "u", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_poweroff },
{ .name = "Halt", .in_sig = "", .out_sig = "",
{ .name = "Halt", .in_sig = "u", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_halt },
{ .name = "Suspend", .in_sig = "", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_suspend },
+16 -3
View File
@@ -1067,13 +1067,26 @@ static int do_cmd(int cmd)
#ifdef HAVE_DBUS
static int do_reboot_dbus(const char *method)
{
int rc = try_dbus_manager(method, "", NULL);
link_client_t *c;
int rc;
if (rc == 0) {
c = link_client_open(FINIT_BUS_SOCKET);
if (!c)
return -1; /* fall back to legacy transport */
/* Timeout in seconds arms the emergency shutdown bypass, 0 = none */
rc = link_client_call_v(c, "/org/finit/manager",
"org.finit.Manager1", method,
"u", (uint32_t)timeout);
if (rc == LINK_CALL_ERROR)
map_dbus_err(c, method, NULL); /* exits */
link_client_close(c);
if (rc == LINK_CALL_OK) {
sleep(5); /* match legacy: wait for finit to shut down */
return 0;
}
return rc; /* 1 = error, -1 = fall back */
return -1; /* fall back */
}
#endif
+23
View File
@@ -94,6 +94,29 @@ static void shutdown_wdt_cb(uev_t *w, void *arg, int events)
do_shutdown(halt);
}
/*
* Emergency shutdown bypass, same job as the shutdown watchdog above
* but armed by the user: `initctl -t SEC reboot`, on either transport.
*/
static void bypass_shutdown(void *unused)
{
(void)unused;
cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n");
do_shutdown(halt);
}
static struct wq emergency = { .cb = bypass_shutdown };
void shutdown_bypass(int timeout)
{
if (timeout <= 0)
return;
emergency.delay = timeout * 1000;
schedule_work(&emergency);
}
/*
* User-requested runlevel change, shared by the legacy API and the
* D-Bus SetRunlevel method. Refused in runlevel 0/6, deferred via
+1
View File
@@ -36,6 +36,7 @@ int sm_in_reload (void);
void sm_reload (void);
void sm_runlevel (int newlevel);
void sm_request_runlevel(int lvl);
void shutdown_bypass(int timeout);
#endif /* FINIT_SM_H_ */