From: Adrian Vovk Date: Sat, 23 Dec 2023 22:03:42 +0000 (-0500) Subject: sleep: Always freeze user.slice X-Git-Tag: v256-rc1~617^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0b958bb3eeae61dd25a1f6735477ea239981b162;p=thirdparty%2Fsystemd.git sleep: Always freeze user.slice Previously, we'd only freeze user.slice in the case of s2h, because we didn't want the user session to resume while systemd was transitioning from suspend to hibernate. This commit extends this freezing behavior to all sleep modes. We also have an environment variable to disable the freezing behavior outright. This is a necessary workaround for someone that has hooks in /usr/lib/systemd/system-sleep/ which communicate with some process running under user.slice, or if someone is using the proprietary NVIDIA driver which breaks when user.slice is frozen (issue #27559) Fixes #27559 --- diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 00492829bdc..dc6a7b4b17f 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -630,6 +630,14 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \ file (containing firmware measurement data) to read. This allows overriding the default of `/sys/kernel/security/tpm0/binary_bios_measurements`. +`systemd-sleep`: + +* `$SYSTEMD_SLEEP_FREEZE_USER_SESSIONS` - Takes a boolean. When true (the default), + `user.slice` will be frozen during sleep. When false it will not be. We recommend + against using this variable, because it can lead to undesired behavior, especially + for systems that use home directory encryption and for + `systemd-suspend-then-hibernate.service`. + Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as `busctl`): diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml index d8ea8f5f817..9fbca6193f7 100644 --- a/man/systemd-suspend.service.xml +++ b/man/systemd-suspend.service.xml @@ -66,7 +66,9 @@ same executables are run, but the first argument is now post. All executables in this directory are executed in parallel, and execution of the action is not continued - until all executables have finished. + until all executables have finished. Note that user.slice will + be frozen while the executables are running, so they should not attempt to + communicate with any user services expecting a reply. Note that scripts or binaries dropped in /usr/lib/systemd/system-sleep/ are intended @@ -90,6 +92,11 @@ sleep.conf.d file. See systemd-sleep.conf5. + + Note that by default these services freeze user.slice while they run. This prevents + the execution of any process in any of the user sessions while the system is entering into and resuming from + sleep. Thus, this prevents the hooks in /usr/lib/systemd/system-sleep/, or any other process + for that matter, from communicating with any user session process during sleep. diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index f1b6f1bcdc7..16ad48b3862 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -23,10 +23,12 @@ #include "build.h" #include "bus-error.h" #include "bus-locator.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "constants.h" #include "devnum-util.h" #include "efivars.h" +#include "env-util.h" #include "exec-util.h" #include "fd-util.h" #include "fileio.h" @@ -444,38 +446,11 @@ static int custom_timer_suspend(const SleepConfig *sleep_config) { return 1; } -/* Freeze when invoked and thaw on cleanup */ -static int freeze_thaw_user_slice(const char **method) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - if (!method || !*method) - return 0; - - r = bus_connect_system_systemd(&bus); - if (r < 0) - return log_debug_errno(r, "Failed to open connection to systemd: %m"); - - (void) sd_bus_set_method_call_timeout(bus, FREEZE_TIMEOUT); - - r = bus_call_method(bus, bus_systemd_mgr, *method, &error, NULL, "s", SPECIAL_USER_SLICE); - if (r < 0) - return log_debug_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); - - return 1; -} - static int execute_s2h(const SleepConfig *sleep_config) { - _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = "ThawUnit"; int r; assert(sleep_config); - r = freeze_thaw_user_slice(&(const char*) { "FreezeUnit" }); - if (r < 0) - log_warning_errno(r, "Failed to freeze unit user.slice, ignoring: %m"); - /* Only check if we have automated battery alarms if HibernateDelaySec= is not set, as in that case * we'll busy poll for the configured interval instead */ if (!timestamp_is_set(sleep_config->hibernate_delay_usec)) { @@ -599,6 +574,7 @@ static int parse_argv(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { + _cleanup_(unit_freezer_done_thaw) UnitFreezer user_slice_freezer = {}; _cleanup_(sleep_config_freep) SleepConfig *sleep_config = NULL; int r; @@ -617,6 +593,22 @@ static int run(int argc, char *argv[]) { "Sleep operation \"%s\" is disabled by configuration, refusing.", sleep_operation_to_string(arg_operation)); + /* Freeze the user sessions */ + r = getenv_bool("SYSTEMD_SLEEP_FREEZE_USER_SESSIONS"); + if (r < 0 && r != -ENXIO) + log_warning_errno(r, "Cannot parse value of $SYSTEMD_SLEEP_FREEZE_USER_SESSIONS, ignoring."); + if (r != 0) { + r = unit_freezer_new_freeze(SPECIAL_USER_SLICE, &user_slice_freezer); + if (r < 0) + log_warning_errno(r, "Failed to freeze user sessions, ignoring: %m"); + else + log_info("Froze user sessions"); + } else + log_notice("User sessions remain unfrozen on explicit request " + "($SYSTEMD_SLEEP_FREEZE_USER_SESSIONS is set to false). This is not recommended, " + "and might result in unexpected behavior, particularly in sysupend-then-hibernate " + "operations or setups with encrypted home directories."); + switch (arg_operation) { case SLEEP_SUSPEND_THEN_HIBERNATE: