diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 45f77bc090..50feb7e651 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -20,101 +20,99 @@ static const ActionTableItem action_table[_HANDLE_ACTION_MAX] = { [HANDLE_POWEROFF] = { - SPECIAL_POWEROFF_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - _SLEEP_OPERATION_INVALID, - SD_MESSAGE_SHUTDOWN_STR, - "System is powering down", - "power-off", - }, + .handle = HANDLE_POWEROFF, + .target = SPECIAL_POWEROFF_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.power-off", + .polkit_action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is powering down", + .log_message = "power-off", + }, [HANDLE_REBOOT] = { - SPECIAL_REBOOT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - _SLEEP_OPERATION_INVALID, - SD_MESSAGE_SHUTDOWN_STR, - "System is rebooting", - "reboot", - }, - [HANDLE_HALT] = { - SPECIAL_HALT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.halt", - "org.freedesktop.login1.halt-multiple-sessions", - "org.freedesktop.login1.halt-ignore-inhibit", - _SLEEP_OPERATION_INVALID, - SD_MESSAGE_SHUTDOWN_STR, - "System is halting", - "halt", - }, - [HANDLE_KEXEC] = { - SPECIAL_KEXEC_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - _SLEEP_OPERATION_INVALID, - SD_MESSAGE_SHUTDOWN_STR, - "System is rebooting with kexec", - "kexec", - }, - [HANDLE_SUSPEND] = { - SPECIAL_SUSPEND_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - SLEEP_SUSPEND, - }, - [HANDLE_HIBERNATE] = { - SPECIAL_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HIBERNATE, - }, - [HANDLE_HYBRID_SLEEP] = { - SPECIAL_HYBRID_SLEEP_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HYBRID_SLEEP, - }, - [HANDLE_SUSPEND_THEN_HIBERNATE] = { - SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_SUSPEND_THEN_HIBERNATE, - }, - [HANDLE_FACTORY_RESET] = { - SPECIAL_FACTORY_RESET_TARGET, - _INHIBIT_WHAT_INVALID, - NULL, - NULL, - NULL, - _SLEEP_OPERATION_INVALID, - SD_MESSAGE_FACTORY_RESET_STR, - "System is performing factory reset", - NULL - }, + .handle = HANDLE_REBOOT, + .target = SPECIAL_REBOOT_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.reboot", + .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is rebooting", + .log_message = "reboot", + }, + [HANDLE_HALT] = { + .handle = HANDLE_HALT, + .target = SPECIAL_HALT_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.halt", + .polkit_action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is halting", + .log_message = "halt", + }, + [HANDLE_KEXEC] = { + .handle = HANDLE_KEXEC, + .target = SPECIAL_KEXEC_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.reboot", + .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is rebooting with kexec", + .log_message = "kexec", + }, + [HANDLE_SUSPEND] = { + .handle = HANDLE_SUSPEND, + .target = SPECIAL_SUSPEND_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.suspend", + .polkit_action_multiple_sessions = "org.freedesktop.login1.suspend-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.suspend-ignore-inhibit", + .sleep_operation = SLEEP_SUSPEND, + }, + [HANDLE_HIBERNATE] = { + .handle = HANDLE_HIBERNATE, + .target = SPECIAL_HIBERNATE_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_HIBERNATE, + }, + [HANDLE_HYBRID_SLEEP] = { + .handle = HANDLE_HYBRID_SLEEP, + .target = SPECIAL_HYBRID_SLEEP_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_HYBRID_SLEEP, + }, + [HANDLE_SUSPEND_THEN_HIBERNATE] = { + .handle = HANDLE_SUSPEND_THEN_HIBERNATE, + .target = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_SUSPEND_THEN_HIBERNATE, + }, + [HANDLE_FACTORY_RESET] = { + .handle = HANDLE_FACTORY_RESET, + .target = SPECIAL_FACTORY_RESET_TARGET, + .inhibit_what = _INHIBIT_WHAT_INVALID, + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_FACTORY_RESET_STR, + .message = "System is performing factory reset", + }, }; -const char* manager_target_for_action(HandleAction handle) { - assert(handle >= 0); - assert(handle < (ssize_t) ELEMENTSOF(action_table)); - - return action_table[handle].target; -} - const ActionTableItem* manager_item_for_handle(HandleAction handle) { assert(handle >= 0); assert(handle < (ssize_t) ELEMENTSOF(action_table)); @@ -122,12 +120,6 @@ const ActionTableItem* manager_item_for_handle(HandleAction handle) { return &action_table[handle]; } -HandleAction manager_handle_for_item(const ActionTableItem* a) { - if (a && a < action_table + ELEMENTSOF(action_table)) - return a - action_table; - return _HANDLE_ACTION_INVALID; -} - int manager_handle_action( Manager *m, InhibitWhat inhibit_key, diff --git a/src/login/logind-action.h b/src/login/logind-action.h index e6d3047743..621c5f97bc 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -28,6 +28,7 @@ typedef struct ActionTableItem ActionTableItem; #include "sleep-config.h" struct ActionTableItem { + HandleAction handle; const char *target; InhibitWhat inhibit_what; const char *polkit_action; @@ -36,8 +37,7 @@ struct ActionTableItem { SleepOperation sleep_operation; const char* message_id; const char* message; - const char* log_str; - + const char* log_message; }; int manager_handle_action( @@ -50,7 +50,6 @@ int manager_handle_action( const char* handle_action_to_string(HandleAction h) _const_; HandleAction handle_action_from_string(const char *s) _pure_; -const char* manager_target_for_action(HandleAction handle); const ActionTableItem* manager_item_for_handle(HandleAction handle); HandleAction manager_handle_for_item(const ActionTableItem* a); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index e801f24532..8fcdf6e936 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -54,6 +54,15 @@ #include "utmp-wtmp.h" #include "virt.h" +/* As a random fun fact sysvinit had a 252 (256-(strlen(" \r\n")+1)) + * character limit for the wall message. + * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72 + * There is no real technical need for that but doesn't make sense + * to store arbitrary amounts either. As we are not stingy here, we + * allow 4k. + */ +#define WALL_MESSAGE_MAX 4096 + static void reset_scheduled_shutdown(Manager *m); static int get_sender_session( @@ -349,7 +358,7 @@ static int property_get_scheduled_shutdown( return r; r = sd_bus_message_append(reply, "st", - handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)), + m->scheduled_shutdown_type ? handle_action_to_string(m->scheduled_shutdown_type->handle) : NULL, m->scheduled_shutdown_timeout); if (r < 0) return r; @@ -1499,13 +1508,13 @@ static int bus_manager_log_shutdown( Manager *m, const ActionTableItem *a) { - const char *message, *log_str; + const char *message, *log_message; assert(m); assert(a); message = a->message; - log_str = a->log_str; + log_message = a->log_message; if (message) message = strjoina("MESSAGE=", message); @@ -1517,13 +1526,13 @@ static int bus_manager_log_shutdown( else message = strjoina(message, " (", m->wall_message, ")."); - if (log_str) - log_str = strjoina("SHUTDOWN=", log_str); + if (log_message) + log_message = strjoina("SHUTDOWN=", log_message); return log_struct(LOG_NOTICE, "MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR, message, - log_str); + log_message); } static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { @@ -1884,7 +1893,7 @@ static int method_do_shutdown_or_sleep( return r; if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); - if (manager_handle_for_item(a) != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC)) + if (a->handle != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations"); } else { /* Old style method: no flags parameter, but interactive bool passed as boolean in @@ -2035,6 +2044,7 @@ static int update_schedule_file(Manager *m) { int r; assert(m); + assert(m->scheduled_shutdown_type); r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, MKDIR_WARN_MODE); if (r < 0) @@ -2052,7 +2062,7 @@ static int update_schedule_file(Manager *m) { "MODE=%s\n", m->scheduled_shutdown_timeout, m->enable_wall_messages, - handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type))); + handle_action_to_string(m->scheduled_shutdown_type->handle)); if (!isempty(m->wall_message)) { _cleanup_free_ char *t = NULL; @@ -2240,9 +2250,10 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd assert(m); assert(message); - cancelled = !IN_SET(manager_handle_for_item(m->scheduled_shutdown_type), HANDLE_IGNORE, _HANDLE_ACTION_INVALID); + cancelled = m->scheduled_shutdown_type + && !IN_SET(m->scheduled_shutdown_type->handle, HANDLE_IGNORE, _HANDLE_ACTION_INVALID); if (!cancelled) - goto done; + return sd_bus_reply_method_return(message, "b", false); a = m->scheduled_shutdown_type; if (!a->polkit_action) @@ -2281,8 +2292,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd username, tty, logind_wall_tty_filter, m); } -done: - return sd_bus_reply_method_return(message, "b", cancelled); + return sd_bus_reply_method_return(message, "b", true); } static int method_can_shutdown_or_sleep( @@ -2328,7 +2338,7 @@ static int method_can_shutdown_or_sleep( if (handle >= 0) { const char *target; - target = manager_target_for_action(handle); + target = manager_item_for_handle(handle)->target; if (target) { _cleanup_free_ char *load_state = NULL; @@ -3115,14 +3125,10 @@ static int method_set_wall_message( if (r < 0) return r; - /* sysvinit has a 252 (256-(strlen(" \r\n")+1)) character - * limit for the wall message. There is no real technical - * need for that but doesn't make sense to store arbitrary - * amounts either. - * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72) - */ - if (strlen(wall_message) > 252) - return -EMSGSIZE; + if (strlen(wall_message) > WALL_MESSAGE_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Wall message too long, maximum permitted length is %u characters.", + WALL_MESSAGE_MAX); /* Short-circuit the operation if the desired state is already in place, to * avoid an unnecessary polkit permission check. */ diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 03817f9c1a..5a9e8db79d 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -64,7 +64,7 @@ static int warn_wall(Manager *m, usec_t n) { assert(m); - if (!m->enable_wall_messages) + if (!m->enable_wall_messages || !m->scheduled_shutdown_type) return 0; left = m->scheduled_shutdown_timeout > n; @@ -72,7 +72,7 @@ static int warn_wall(Manager *m, usec_t n) { r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", strempty(m->wall_message), isempty(m->wall_message) ? "" : "\n", - handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)), + handle_action_to_string(m->scheduled_shutdown_type->handle), left ? "at " : "NOW", left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : ""); if (r < 0) { diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c index a7d3ffadf4..c8d745592f 100644 --- a/src/systemctl/systemctl-compat-halt.c +++ b/src/systemctl/systemctl-compat-halt.c @@ -12,6 +12,7 @@ #include "systemctl-compat-halt.h" #include "systemctl-compat-telinit.h" #include "systemctl-logind.h" +#include "systemctl-start-unit.h" #include "systemctl-util.h" #include "systemctl.h" #include "terminal-util.h" @@ -144,30 +145,32 @@ int halt_parse_argv(int argc, char *argv[]) { int halt_main(void) { int r; - /* always try logind first */ - if (arg_when > 0) - r = logind_schedule_shutdown(); - else { - r = logind_check_inhibitors(arg_action); - if (r < 0) + if (arg_force == 0) { + /* always try logind first */ + if (arg_when > 0) + r = logind_schedule_shutdown(); + else { + r = logind_check_inhibitors(arg_action); + if (r < 0) + return r; + + r = logind_reboot(arg_action); + } + if (r >= 0) return r; + if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + /* Requested operation requires auth, is not supported on the local system or already in + * progress */ + return r; + /* on all other errors, try low-level operation */ - r = logind_reboot(arg_action); + /* In order to minimize the difference between operation with and without logind, we explicitly + * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ + arg_no_block = true; + + if (!arg_dry_run) + return start_with_fallback(); } - if (r >= 0) - return r; - if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) - /* Requested operation requires auth, is not supported on the local system or already in - * progress */ - return r; - /* on all other errors, try low-level operation */ - - /* In order to minimize the difference between operation with and without logind, we explicitly - * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ - arg_no_block = true; - - if (!arg_dry_run && !arg_force) - return start_with_fallback(); if (geteuid() != 0) { (void) must_be_root();