]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sleep: Always freeze user.slice
authorAdrian Vovk <adrianvovk@gmail.com>
Sat, 23 Dec 2023 22:03:42 +0000 (17:03 -0500)
committerAdrian Vovk <adrianvovk@gmail.com>
Tue, 5 Mar 2024 17:12:35 +0000 (12:12 -0500)
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

docs/ENVIRONMENT.md
man/systemd-suspend.service.xml
src/sleep/sleep.c

index 00492829bdc5dc565a4eeb4e2d4cce83d82758dd..dc6a7b4b17f56aea6a6cf4b23269258f4924624f 100644 (file)
@@ -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`):
 
index d8ea8f5f8171ad93ea76ce113d22a052b67808a6..9fbca6193f771aae55f246e33c1713f96c3f9134 100644 (file)
@@ -66,7 +66,9 @@
     same executables are run, but the first argument is now
     <literal>post</literal>. All executables in this directory are
     executed in parallel, and execution of the action is not continued
-    until all executables have finished.</para>
+    until all executables have finished. Note that <filename>user.slice</filename> will
+    be frozen while the executables are running, so they should not attempt to
+    communicate with any user services expecting a reply.</para>
 
     <para>Note that scripts or binaries dropped in
     <filename>/usr/lib/systemd/system-sleep/</filename> are intended
     <filename>sleep.conf.d</filename> file. See
     <citerefentry><refentrytitle>systemd-sleep.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
     </para>
+
+    <para>Note that by default these services freeze <filename>user.slice</filename> 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 <filename>/usr/lib/systemd/system-sleep/</filename>, or any other process
+    for that matter, from communicating with any user session process during sleep.</para>
   </refsect1>
 
   <refsect1>
index f1b6f1bcdc7974af52632a9705c25f6b8bec5df3..16ad48b3862cd27d45779e996de75981eccbcd8b 100644 (file)
 #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: