Merge pull request #23575 from keszybz/logind-wall-message-cleanup

Cleanup wall messages emitted by logind and systemctl
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2022-06-01 16:26:29 +02:00 committed by GitHub
commit 14c811ff4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 116 additions and 115 deletions

View file

@ -1924,23 +1924,21 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<term><option>--check-inhibitors=</option></term>
<listitem>
<para>When system shutdown or sleep state is request, this option controls how to deal with
inhibitor locks. It takes one of <literal>auto</literal>, <literal>yes</literal> or
<para>When system shutdown or sleep state is requested, this option controls checking of inhibitor
locks. It takes one of <literal>auto</literal>, <literal>yes</literal> or
<literal>no</literal>. Defaults to <literal>auto</literal>, which will behave like
<literal>yes</literal> for interactive invocations (i.e. from a TTY) and <literal>no</literal>
for non-interactive invocations.
<literal>yes</literal> will let the request respect inhibitor locks.
<literal>no</literal> will let the request ignore inhibitor locks.
</para>
<para>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 <literal>no</literal> is specified or <literal>auto</literal> 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 <option>--force</option>.</para>
<literal>yes</literal> for interactive invocations (i.e. from a TTY) and <literal>no</literal> for
non-interactive invocations. <literal>yes</literal> lets the request respect inhibitor locks.
<literal>no</literal> lets the request ignore inhibitor locks.</para>
<para>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 <literal>no</literal> is specified or
<literal>auto</literal> is specified on a non-interactive requests, the operation will be
attempted. If locks are present, the operation may require additional privileges.</para>
<para>Option <option>--force</option> provides another way to override inhibitors.</para>
</listitem>
</varlistentry>

View file

@ -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);

View file

@ -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_;

View file

@ -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);
}

View file

@ -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 */

View file

@ -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;

View file

@ -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 */

View file

@ -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)

View file

@ -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);

View file

@ -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"

View file

@ -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")