diff --git a/doc/dbus.md b/doc/dbus.md index 6c1e52d7..49afcc6b 100644 --- a/doc/dbus.md +++ b/doc/dbus.md @@ -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)` | diff --git a/src/api.c b/src/api.c index a7f3f4d8..e82510a5 100644 --- a/src/api.c +++ b/src/api.c @@ -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: diff --git a/src/dbus.c b/src/dbus.c index 6aa0c44e..389f0d14 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -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 }, diff --git a/src/initctl.c b/src/initctl.c index 646cd092..dbda3e7e 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -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 diff --git a/src/sm.c b/src/sm.c index 35a46e3e..4776b648 100644 --- a/src/sm.c +++ b/src/sm.c @@ -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 diff --git a/src/sm.h b/src/sm.h index f2873861..659ca34f 100644 --- a/src/sm.h +++ b/src/sm.h @@ -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_ */