diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index 00492829bd..dc6a7b4b17 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 d8ea8f5f81..9fbca6193f 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 f1b6f1bcdc..16ad48b386 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: