diff --git a/man/systemctl.xml b/man/systemctl.xml index 63ea80a4536..6b2798ecdec 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1924,23 +1924,21 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When system shutdown or sleep state is request, this option controls how to deal with - inhibitor locks. It takes one of auto, yes or + When system shutdown or sleep state is requested, this option controls checking of inhibitor + locks. It takes one of auto, yes or no. Defaults to auto, which will behave like - yes for interactive invocations (i.e. from a TTY) and no - for non-interactive invocations. - yes will let the request respect inhibitor locks. - no will let the request ignore inhibitor locks. - - Applications can establish inhibitor locks to avoid that certain important operations - (such as CD burning or suchlike) are interrupted by system shutdown or a sleep state. Any user may - take these locks and privileged users may override these locks. - If any locks are taken, shutdown and sleep state requests will normally fail (unless privileged) - and a list of active locks is printed. - However, if no is specified or auto is specified on a - non-interactive requests, the established locks are ignored and not shown, and the operation - attempted anyway, possibly requiring additional privileges. - May be overridden by . + yes for interactive invocations (i.e. from a TTY) and no for + non-interactive invocations. yes lets the request respect inhibitor locks. + no lets the request ignore inhibitor locks. + + Applications can establish inhibitor locks to prevent certain important operations (such as + CD burning) from being interrupted by system shutdown or sleep. Any user may take these locks and + privileged users may override these locks. If any locks are taken, shutdown and sleep state + requests will normally fail (unless privileged). However, if no is specified or + auto is specified on a non-interactive requests, the operation will be + attempted. If locks are present, the operation may require additional privileges. + + Option provides another way to override inhibitors. diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 6a873009bc0..c6a36a1556c 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -129,15 +129,15 @@ int manager_handle_action( bool is_edge) { static const char * const message_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = "Powering Off...", - [HANDLE_REBOOT] = "Rebooting...", - [HANDLE_HALT] = "Halting...", - [HANDLE_KEXEC] = "Rebooting via kexec...", - [HANDLE_SUSPEND] = "Suspending...", - [HANDLE_HIBERNATE] = "Hibernating...", - [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...", + [HANDLE_POWEROFF] = "Powering off...", + [HANDLE_REBOOT] = "Rebooting...", + [HANDLE_HALT] = "Halting...", + [HANDLE_KEXEC] = "Rebooting via kexec...", + [HANDLE_SUSPEND] = "Suspending...", + [HANDLE_HIBERNATE] = "Hibernating...", + [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...", [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...", - [HANDLE_FACTORY_RESET] = "Performing factory reset...", + [HANDLE_FACTORY_RESET] = "Performing factory reset...", }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -251,18 +251,34 @@ int manager_handle_action( return 1; } +static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = { + [HANDLE_IGNORE] = "do nothing", + [HANDLE_POWEROFF] = "power off", + [HANDLE_REBOOT] = "reboot", + [HANDLE_HALT] = "halt", + [HANDLE_KEXEC] = "kexec", + [HANDLE_SUSPEND] = "suspend", + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "enter hybrid sleep", + [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend and later hibernate", + [HANDLE_FACTORY_RESET] = "perform a factory reset", + [HANDLE_LOCK] = "be locked", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb, HandleAction); + static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { - [HANDLE_IGNORE] = "ignore", - [HANDLE_POWEROFF] = "poweroff", - [HANDLE_REBOOT] = "reboot", - [HANDLE_HALT] = "halt", - [HANDLE_KEXEC] = "kexec", - [HANDLE_SUSPEND] = "suspend", - [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", + [HANDLE_IGNORE] = "ignore", + [HANDLE_POWEROFF] = "poweroff", + [HANDLE_REBOOT] = "reboot", + [HANDLE_HALT] = "halt", + [HANDLE_KEXEC] = "kexec", + [HANDLE_SUSPEND] = "suspend", + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate", - [HANDLE_FACTORY_RESET] = "factory-reset", - [HANDLE_LOCK] = "lock", + [HANDLE_FACTORY_RESET] = "factory-reset", + [HANDLE_LOCK] = "lock", }; DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); diff --git a/src/login/logind-action.h b/src/login/logind-action.h index c891787e6cd..9a01af16908 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -49,6 +49,8 @@ int manager_handle_action( bool ignore_inhibited, bool is_edge); +const char* handle_action_verb_to_string(HandleAction h) _const_; + const char* handle_action_to_string(HandleAction h) _const_; HandleAction handle_action_from_string(const char *s) _pure_; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index c40d8defaf1..031b96e9f15 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1888,9 +1888,11 @@ static int method_do_shutdown_or_sleep( if (r < 0) 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"); + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid flags parameter"); 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"); + 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 * payload. Let's convert this argument to the new-style flags parameter for our internal @@ -1919,7 +1921,8 @@ static int method_do_shutdown_or_sleep( "Not enough swap space for hibernation"); if (r == 0) return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, - "Sleep verb \"%s\" not supported", sleep_operation_to_string(a->sleep_operation)); + "Sleep verb \"%s\" not supported", + sleep_operation_to_string(a->sleep_operation)); if (r < 0) return r; } @@ -2355,7 +2358,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd } username = uid_to_name(uid); - utmp_wall("The system shutdown has been cancelled", + utmp_wall("System shutdown has been cancelled", username, tty, logind_wall_tty_filter, m); } diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 7d761a0d674..36a3fcd0e93 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -21,9 +21,6 @@ #include "utmp-wtmp.h" _const_ static usec_t when_wall(usec_t n, usec_t elapse) { - - usec_t left; - unsigned i; static const int wall_timers[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 40, 55, 70, 100, 130, 150, 180, @@ -33,9 +30,9 @@ _const_ static usec_t when_wall(usec_t n, usec_t elapse) { if (n >= elapse) return 0; - left = elapse - n; + usec_t left = elapse - n; - for (i = 1; i < ELEMENTSOF(wall_timers); i++) + for (unsigned i = 1; i < ELEMENTSOF(wall_timers); i++) if (wall_timers[i] * USEC_PER_MINUTE >= left) return left - wall_timers[i-1] * USEC_PER_MINUTE; @@ -67,8 +64,6 @@ bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) { } static int warn_wall(Manager *m, usec_t n) { - _cleanup_free_ char *l = NULL, *username = NULL; - usec_t left; int r; assert(m); @@ -76,13 +71,15 @@ static int warn_wall(Manager *m, usec_t n) { if (!m->enable_wall_messages || !m->scheduled_shutdown_action) return 0; - left = m->scheduled_shutdown_timeout > n; + usec_t left = m->scheduled_shutdown_timeout > n; - r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", + _cleanup_free_ char *l = NULL, *username = NULL; + + r = asprintf(&l, "%s%sThe system will %s %s%s!", strempty(m->wall_message), isempty(m->wall_message) ? "" : "\n", - handle_action_to_string(m->scheduled_shutdown_action->handle), - left ? "at " : "NOW", + handle_action_verb_to_string(m->scheduled_shutdown_action->handle), + left ? "at " : "now", left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : ""); if (r < 0) { log_oom(); @@ -100,20 +97,18 @@ static int wall_message_timeout_handler( uint64_t usec, void *userdata) { - Manager *m = userdata; - usec_t n, next; + Manager *m = ASSERT_PTR(userdata); int r; - assert(m); assert(s == m->wall_message_timeout_source); - n = now(CLOCK_REALTIME); + usec_t n = now(CLOCK_REALTIME); r = warn_wall(m, n); if (r == 0) return 0; - next = when_wall(n, m->scheduled_shutdown_timeout); + usec_t next = when_wall(n, m->scheduled_shutdown_timeout); if (next > 0) { r = sd_event_source_set_time(s, n + next); if (r < 0) @@ -128,14 +123,12 @@ static int wall_message_timeout_handler( } int manager_setup_wall_message_timer(Manager *m) { - - usec_t n, elapse; int r; assert(m); - n = now(CLOCK_REALTIME); - elapse = m->scheduled_shutdown_timeout; + usec_t n = now(CLOCK_REALTIME); + usec_t elapse = m->scheduled_shutdown_timeout; /* wall message handling */ diff --git a/src/login/logind.c b/src/login/logind.c index 0483902edd9..aa854805484 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -972,7 +972,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us if (n >= since.monotonic + m->idle_action_usec && (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) { - log_info("System idle. Doing %s operation.", handle_action_to_string(m->idle_action)); + log_info("System idle. Will %s now.", handle_action_verb_to_string(m->idle_action)); manager_handle_action(m, 0, m->idle_action, false, false); m->idle_action_not_before_usec = n; diff --git a/src/shared/pager.c b/src/shared/pager.c index 1a93deb6284..dc717cd1fe8 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -219,31 +219,30 @@ void pager_open(PagerFlags flags) { /* Debian's alternatives command for pagers is called 'pager'. Note that we do not call * sensible-pagers here, since that is just a shell script that implements a logic that is * similar to this one anyway, but is Debian-specific. */ - FOREACH_STRING(exe, "pager", "less", "more") { - /* Only less implements secure mode right now. */ - if (use_secure_mode && !streq(exe, "less")) + static const char* pagers[] = { "pager", "less", "more", "(built-in)" }; + + for (unsigned i = 0; i < ELEMENTSOF(pagers); i++) { + /* Only less (and our trivial fallback) implement secure mode right now. */ + if (use_secure_mode && !STR_IN_SET(pagers[i], "less", "(built-in)")) continue; - r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false); - if (r < 0) { + r = loop_write(exe_name_pipe[1], pagers[i], strlen(pagers[i]) + 1, false); + if (r < 0) { log_error_errno(r, "Failed to write pager name to socket: %m"); _exit(EXIT_FAILURE); } - execlp(exe, exe, NULL); - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to execute '%s', using next fallback pager: %m", exe); - } - /* Our builtin is also very secure. */ - r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false); - if (r < 0) { - log_error_errno(r, "Failed to write pager name to socket: %m"); - _exit(EXIT_FAILURE); + if (i < ELEMENTSOF(pagers) - 1) { + execlp(pagers[i], pagers[i], NULL); + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to execute '%s', will try '%s' next: %m", pagers[i], pagers[i+1]); + } else { + /* Close pipe to signal the parent to start sending data */ + safe_close_pair(exe_name_pipe); + pager_fallback(); + assert_not_reached(); + } } - /* Close pipe to signal the parent to start sending data */ - safe_close_pair(exe_name_pipe); - pager_fallback(); - /* not reached */ } /* Return in the parent */ diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index 40521b85954..1c3b68f09f1 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -15,17 +15,12 @@ #include "terminal-util.h" #include "user-util.h" -int logind_set_wall_message(void) { +static int logind_set_wall_message(sd_bus *bus) { #if ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus; _cleanup_free_ char *m = NULL; int r; - r = acquire_bus(BUS_FULL, &bus); - if (r < 0) - return r; - m = strv_join(arg_wall, " "); if (!m) return log_oom(); @@ -44,27 +39,23 @@ int logind_set_wall_message(void) { /* Ask systemd-logind, which might grant access to unprivileged users through polkit */ int logind_reboot(enum action a) { #if ENABLE_LOGIND - static const struct { - const char *method; - const char *description; - } actions[_ACTION_MAX] = { - [ACTION_POWEROFF] = { "PowerOff", "power off system" }, - [ACTION_REBOOT] = { "Reboot", "reboot system" }, - [ACTION_KEXEC] = { "Reboot", "kexec reboot system" }, - [ACTION_HALT] = { "Halt", "halt system" }, - [ACTION_SUSPEND] = { "Suspend", "suspend system" }, - [ACTION_HIBERNATE] = { "Hibernate", "hibernate system" }, - [ACTION_HYBRID_SLEEP] = { "HybridSleep", "put system into hybrid sleep" }, - [ACTION_SUSPEND_THEN_HIBERNATE] = { "SuspendThenHibernate", "suspend system, hibernate later" }, + static const char* actions[_ACTION_MAX] = { + [ACTION_POWEROFF] = "PowerOff", + [ACTION_REBOOT] = "Reboot", + [ACTION_KEXEC] = "Reboot", + [ACTION_HALT] = "Halt", + [ACTION_SUSPEND] = "Suspend", + [ACTION_HIBERNATE] = "Hibernate", + [ACTION_HYBRID_SLEEP] = "HybridSleep", + [ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate", }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *method_with_flags; uint64_t flags = 0; sd_bus *bus; int r; - if (a < 0 || a >= _ACTION_MAX || !actions[a].method) + if (a < 0 || a >= _ACTION_MAX || !actions[a]) return -EINVAL; r = acquire_bus(BUS_FULL, &bus); @@ -72,9 +63,12 @@ int logind_reboot(enum action a) { return r; polkit_agent_open_maybe(); - (void) logind_set_wall_message(); + (void) logind_set_wall_message(bus); - log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", actions[a].method); + const char *method_with_flags = strjoina(actions[a], "WithFlags"); + + log_debug("%s org.freedesktop.login1.Manager %s dbus call.", + arg_dry_run ? "Would execute" : "Executing", method_with_flags); if (arg_dry_run) return 0; @@ -82,21 +76,19 @@ int logind_reboot(enum action a) { SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0); SET_FLAG(flags, SD_LOGIND_REBOOT_VIA_KEXEC, a == ACTION_KEXEC); - method_with_flags = strjoina(actions[a].method, "WithFlags"); - r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags); if (r >= 0) return 0; if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) - return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); + return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r)); - /* Fallback to original methods in case there is older version of systemd-logind */ - log_debug("Method %s not available: %s. Falling back to %s", method_with_flags, bus_error_message(&error, r), actions[a].method); + /* Fall back to original methods in case there is an older version of systemd-logind */ + log_debug("Method %s not available: %s. Falling back to %s", method_with_flags, bus_error_message(&error, r), actions[a]); sd_bus_error_free(&error); - r = bus_call_method(bus, bus_login_mgr, actions[a].method, &error, NULL, "b", arg_ask_password); + r = bus_call_method(bus, bus_login_mgr, actions[a], &error, NULL, "b", arg_ask_password); if (r < 0) - return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); + return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r)); return 0; #else @@ -310,7 +302,7 @@ int logind_schedule_shutdown(void) { if (arg_dry_run) action = strjoina("dry-", action); - (void) logind_set_wall_message(); + (void) logind_set_wall_message(bus); r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when); if (r < 0) @@ -336,7 +328,7 @@ int logind_cancel_shutdown(void) { if (r < 0) return r; - (void) logind_set_wall_message(); + (void) logind_set_wall_message(bus); r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL); if (r < 0) diff --git a/src/systemctl/systemctl-logind.h b/src/systemctl/systemctl-logind.h index 6e73cb76252..925f4559c1b 100644 --- a/src/systemctl/systemctl-logind.h +++ b/src/systemctl/systemctl-logind.h @@ -3,8 +3,6 @@ #include "systemctl.h" -int logind_set_wall_message(void); - int logind_reboot(enum action a); int logind_check_inhibitors(enum action a); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 97ec51b7d94..d1cb4b70033 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -257,8 +257,8 @@ static int systemctl_help(void) { " --show-types When showing sockets, explicitly show their type\n" " --value When showing properties, only print the value\n" " --check-inhibitors=MODE\n" - " Specify if checking inhibitors before shutting down,\n" - " sleeping or hibernating\n" + " Whether to check inhibitors before shutting down,\n" + " sleeping, or hibernating\n" " -i Shortcut for --check-inhibitors=no\n" " --kill-who=WHO Whom to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" diff --git a/test/test-shutdown.py b/test/test-shutdown.py index 77ac3a7d9fb..060a0e651af 100755 --- a/test/test-shutdown.py +++ b/test/test-shutdown.py @@ -54,7 +54,7 @@ def run(args): console.send('0') logger.info("verify broadcast message") console.expect('Broadcast message from root@H on %s' % pty, 2) - console.expect('The system is going down for reboot at %s' % date, 2) + console.expect('The system will reboot at %s' % date, 2) logger.info("check show output") console.sendline('shutdown --show') @@ -64,13 +64,13 @@ def run(args): console.sendline('shutdown -c') console.sendcontrol('a') console.send('1') - console.expect('The system shutdown has been cancelled', 2) + console.expect('System shutdown has been cancelled', 2) logger.info("call for reboot") console.sendline('sleep 10; shutdown -r now') console.sendcontrol('a') console.send('0') - console.expect("The system is going down for reboot NOW!", 12) + console.expect("The system will reboot now!", 12) logger.info("waiting for reboot")