chiark / gitweb /
Prep v239: Fix and add debug messages to method_can_shutdown_or_sleep()
[elogind.git] / src / login / logind-dbus.c
index f444ad9b4bf49bc5da987aeffbc81cb6d695d13a..9d70928691eda62c0c8f8772964c8c08a59799d4 100644 (file)
@@ -1,22 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  This file is part of systemd.
-
-  Copyright 2011 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
 
 #include <errno.h>
 #include <pwd.h>
@@ -29,6 +11,7 @@
 #include "audit-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+//#include "bus-unit-util.h"
 #include "bus-util.h"
 #include "dirent-util.h"
 //#include "efivars.h"
@@ -54,6 +37,7 @@
 
 /// Additional includes needed by elogind
 #include "elogind-dbus.h"
+
 static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@@ -256,9 +240,9 @@ static int property_get_preparing(
         assert(m);
 
         if (streq(property, "PreparingForShutdown"))
-                b = !!(m->action_what & INHIBIT_SHUTDOWN);
+                b = m->action_what & INHIBIT_SHUTDOWN;
         else
-                b = !!(m->action_what & INHIBIT_SLEEP);
+                b = m->action_what & INHIBIT_SLEEP;
 
         return sd_bus_message_append(reply, "b", b);
 }
@@ -291,60 +275,9 @@ static int property_get_scheduled_shutdown(
 }
 
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
-
-static int property_get_docked(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        Manager *m = userdata;
-
-        assert(bus);
-        assert(reply);
-        assert(m);
-
-        return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m));
-}
-
-static int property_get_current_sessions(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        Manager *m = userdata;
-
-        assert(bus);
-        assert(reply);
-        assert(m);
-
-        return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions));
-}
-
-static int property_get_current_inhibitors(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        Manager *m = userdata;
-
-        assert(bus);
-        assert(reply);
-        assert(m);
-
-        return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors));
-}
+static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays);
+static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_compat_user_tasks_max, "t", CGROUP_LIMIT_MAX);
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "t", Hashmap *, (uint64_t) hashmap_size);
 
 static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
@@ -660,10 +593,9 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
 
 static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
-        uint32_t audit_id = 0;
-        _cleanup_free_ char *unit = NULL;
         _cleanup_free_ char *id = NULL;
         Session *session = NULL;
+        uint32_t audit_id = 0;
         Manager *m = userdata;
         User *user = NULL;
         Seat *seat = NULL;
@@ -687,7 +619,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
 
         if (!uid_is_valid(uid))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
-        if (leader < 0 || leader == 1)
+        if (leader < 0 || leader == 1 || leader == getpid_cached())
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
 
         if (isempty(type))
@@ -733,7 +665,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 if (v <= 0)
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty);
 
-                if (!vtnr)
+                if (vtnr == 0)
                         vtnr = (uint32_t) v;
                 else if (vtnr != (uint32_t) v)
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match");
@@ -751,7 +683,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
 
         if (seat) {
                 if (seat_has_vts(seat)) {
-                        if (!vtnr || vtnr > 63)
+                        if (vtnr <= 0 || vtnr > 63)
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range");
                 } else {
                         if (vtnr != 0)
@@ -759,10 +691,6 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 }
         }
 
-        r = sd_bus_message_enter_container(message, 'a', "(sv)");
-        if (r < 0)
-                return r;
-
         if (t == _SESSION_TYPE_INVALID) {
                 if (!isempty(display))
                         t = SESSION_X11;
@@ -791,16 +719,13 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         return r;
         }
 
-        /*
-         * Check if we are already in a logind session.  Or if we are in user@.service
-         * which is a special PAM session that avoids creating a logind session.
-         */
-        r = cg_pid_get_unit(leader, &unit);
+        /* Check if we are already in a logind session. Or if we are in user@.service which is a special PAM session
+         * that avoids creating a logind session. */
+        r = manager_get_user_by_pid(m, leader, NULL);
         if (r < 0)
                 return r;
-        if (hashmap_get(m->session_units, unit) ||
-            hashmap_get(m->user_units, unit))
-                return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session");
+        if (r > 0)
+                return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
 
         /*
          * Old gdm and lightdm start the user-session on the same VT as
@@ -834,9 +759,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                  * the audit data and let's better register a new
                  * ID */
                 if (hashmap_get(m->sessions, id)) {
-                        log_warning("Existing logind session ID %s used by new audit session, ignoring", id);
+                        log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
                         audit_id = AUDIT_SESSION_INVALID;
-
                         id = mfree(id);
                 }
         }
@@ -922,15 +846,22 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         goto fail;
         }
 
-        r = session_start(session);
+        r = sd_bus_message_enter_container(message, 'a', "(sv)");
+        if (r < 0)
+                return r;
+
+        r = session_start(session, message);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_message_exit_container(message);
         if (r < 0)
                 goto fail;
 
         session->create_message = sd_bus_message_ref(message);
 
 #if 0 /// UNNEEDED by elogind
-        /* Now, let's wait until the slice unit and stuff got
-         * created. We send the reply back from
+        /* Now, let's wait until the slice unit and stuff got created. We send the reply back from
          * session_send_create_reply(). */
 #else
         /* We reply directly. */
@@ -1238,7 +1169,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
 
         mkdir_p_label("/var/lib/elogind", 0755);
 
-        r = mkdir_safe_label("/var/lib/elogind/linger", 0755, 0, 0, false);
+        r = mkdir_safe_label("/var/lib/elogind/linger", 0755, 0, 0, MKDIR_WARN_MODE);
         if (r < 0)
                 return r;
 
@@ -1273,7 +1204,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
 }
 
 static int trigger_device(Manager *m, struct udev_device *d) {
-        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+        _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
         struct udev_list_entry *first, *item;
         int r;
 
@@ -1311,7 +1242,7 @@ static int trigger_device(Manager *m, struct udev_device *d) {
 }
 
 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
-        _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+        _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
         _cleanup_free_ char *rule = NULL, *file = NULL;
         const char *id_for_seat;
         int r;
@@ -1500,8 +1431,7 @@ static int bus_manager_log_shutdown(
         return log_struct(LOG_NOTICE,
                           "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
                           p,
-                          q,
-                          NULL);
+                          q);
 }
 #endif // 0
 
@@ -1634,6 +1564,7 @@ error:
 
         return r;
 }
+#endif // 0
 
 int manager_dispatch_delayed(Manager *manager, bool timeout) {
 
@@ -1643,7 +1574,11 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
 
         assert(manager);
 
+#if 0 /// elogind has no action_job, but a pending_action
         if (manager->action_what == 0 || manager->action_job)
+#else
+        if ( (0 == manager->action_what) || (HANDLE_IGNORE == manager->pending_action) )
+#endif // 0
                 return 0;
 
         if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
@@ -1661,25 +1596,31 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
         }
 
         /* Actually do the operation */
+#if 0 /// elogind has no action_unit but a pending_action
         r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+#else
+        r = execute_shutdown_or_sleep(manager, manager->action_what, manager->pending_action, &error);
+#endif // 0
         if (r < 0) {
                 log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
                             bus_error_message(&error, r));
 
+
+#if 0 /// elogind has no action_unit but a pending_action
                 manager->action_unit = NULL;
                 manager->action_what = 0;
                 return r;
+#else
+                manager->pending_action = HANDLE_IGNORE;
+                manager->action_what    = 0;
+                /* It is not a critical error for elogind if suspending fails */
+#endif // 0
         }
 
         return 1;
 }
-#endif // 0
 
-#if 0 /// elogind-dbus.c needs to access this
 static int manager_inhibit_timeout_handler(
-#else
-int manager_inhibit_timeout_handler(
-#endif // 0
                         sd_event_source *s,
                         uint64_t usec,
                         void *userdata) {
@@ -1694,19 +1635,27 @@ int manager_inhibit_timeout_handler(
         return (r < 0) ? r : 0;
 }
 
-#if 0 /// elogind has its own variant in elogind-dbus.c
+#if 0 /// elogind does not have unit_name but action
 static int delay_shutdown_or_sleep(
                 Manager *m,
                 InhibitWhat w,
                 const char *unit_name) {
 
+#else
+int delay_shutdown_or_sleep(
+                Manager *m,
+                InhibitWhat w,
+                HandleAction action) {
+#endif // 0
         int r;
         usec_t timeout_val;
 
         assert(m);
         assert(w >= 0);
         assert(w < _INHIBIT_WHAT_MAX);
+#if 0 /// UNNEEDED by elogind
         assert(unit_name);
+#endif // 0
 
         timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max;
 
@@ -1725,29 +1674,50 @@ static int delay_shutdown_or_sleep(
                         return r;
         }
 
+#if 0 /// elogind does not have unit_name but pendig_action
         m->action_unit = unit_name;
+#else
+        m->pending_action = action;
+#endif // 0
         m->action_what = w;
 
         return 0;
 }
-#endif // 0
 
-#if 0 /// elogind has its own variant in elogind-dbus.c
 int bus_manager_shutdown_or_sleep_now_or_later(
                 Manager *m,
+#if 0 /// elogind has HandleAction instead of const char* unit_name
                 const char *unit_name,
+
+        _cleanup_free_ char *load_state = NULL;
+#else
+                HandleAction unit_name,
+#endif // 0
                 InhibitWhat w,
                 sd_bus_error *error) {
-
         bool delayed;
         int r;
 
         assert(m);
+#if 0 /// for elogind only w has to be checked.
         assert(unit_name);
         assert(w > 0);
         assert(w <= _INHIBIT_WHAT_MAX);
         assert(!m->action_job);
 
+        r = unit_load_state(m->bus, unit_name, &load_state);
+        if (r < 0)
+                return r;
+
+        if (!streq(load_state, "loaded")) {
+                log_notice("Unit %s is %s, refusing operation.", unit_name, load_state);
+                return -EACCES;
+        }
+#else
+        assert(w > 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+#endif // 0
+
         /* Tell everybody to prepare for shutdown/sleep */
         (void) send_prepare_for(m, w, true);
 
@@ -1756,7 +1726,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
                 manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL);
 
         log_debug_elogind("%s called for %s (%sdelayed)", __FUNCTION__,
-                          handle_action_to_string(action),
+                          handle_action_to_string(unit_name),
                           delayed ? "" : "NOT ");
         if (delayed)
                 /* Shutdown is delayed, keep in mind what we
@@ -1769,13 +1739,8 @@ int bus_manager_shutdown_or_sleep_now_or_later(
 
         return r;
 }
-#endif // 0
 
-#if 0 /// elogind-dbus.c needs to access this
 static int verify_shutdown_creds(
-#else
-int verify_shutdown_creds(
-#endif // 0
                 Manager *m,
                 sd_bus_message *message,
                 InhibitWhat w,
@@ -1837,11 +1802,14 @@ int verify_shutdown_creds(
         return 0;
 }
 
-#if 0 /// elogind has its own variant in elogind-dbus.c
 static int method_do_shutdown_or_sleep(
                 Manager *m,
                 sd_bus_message *message,
+#if 0 /// elogind has HandleAction instead of const char* unit_name
                 const char *unit_name,
+#else
+                HandleAction unit_name,
+#endif // 0
                 InhibitWhat w,
                 const char *action,
                 const char *action_multiple_sessions,
@@ -1853,7 +1821,9 @@ static int method_do_shutdown_or_sleep(
 
         assert(m);
         assert(message);
+#if 0 /// elogind does not need this to be checked
         assert(unit_name);
+#endif // 0
         assert(w >= 0);
         assert(w <= _INHIBIT_WHAT_MAX);
 
@@ -1861,24 +1831,43 @@ static int method_do_shutdown_or_sleep(
         if (r < 0)
                 return r;
 
+        log_debug_elogind("%s called with action '%s', sleep '%s' (%sinteractive)",
+                          __FUNCTION__, action, sleep_verb,
+                          interactive ? "" : "NOT ");
         /* Don't allow multiple jobs being executed at the same time */
         if (m->action_what)
                 return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
 
         if (sleep_verb) {
+#if 0 /// Within elogind the manager m must be provided, too
                 r = can_sleep(sleep_verb);
+                if (r == -ENOSPC)
+                        return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
+                                                "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_verb);
+#else
+                r = can_sleep(m, sleep_verb);
+#endif // 0
                 if (r < 0)
                         return r;
-
-                if (r == 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
         }
 
+#if 0 /// Within elogind it does not make sense to verify shutdown creds when suspending
         r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
                                   action_ignore_inhibit, error);
-        log_debug_elogind("verify_shutdown_creds() returned %d", r);
         if (r != 0)
                 return r;
+#else
+        if (IN_SET(unit_name, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
+                r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
+                                          action_ignore_inhibit, error);
+                log_debug_elogind("verify_shutdown_creds() returned %d", r);
+                if (r != 0)
+                        return r;
+        }
+#endif // 0
 
         r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
         if (r < 0)
@@ -1890,9 +1879,14 @@ static int method_do_shutdown_or_sleep(
 static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_POWEROFF_TARGET,
+#else
+                        HANDLE_POWEROFF,
+#endif // 0
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.power-off",
                         "org.freedesktop.login1.power-off-multiple-sessions",
@@ -1904,9 +1898,14 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
 static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_REBOOT_TARGET,
+#else
+                        HANDLE_REBOOT,
+#endif // 0
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.reboot",
                         "org.freedesktop.login1.reboot-multiple-sessions",
@@ -1918,9 +1917,14 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
 static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_HALT_TARGET,
+#else
+                        HANDLE_HALT,
+#endif // 0
                         INHIBIT_SHUTDOWN,
                         "org.freedesktop.login1.halt",
                         "org.freedesktop.login1.halt-multiple-sessions",
@@ -1932,9 +1936,14 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
 static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_SUSPEND_TARGET,
+#else
+                        HANDLE_SUSPEND,
+#endif // 0
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.suspend",
                         "org.freedesktop.login1.suspend-multiple-sessions",
@@ -1946,9 +1955,14 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error
 static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_HIBERNATE_TARGET,
+#else
+                        HANDLE_HIBERNATE,
+#endif // 0
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
@@ -1960,9 +1974,14 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro
 static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
 
+        log_debug_elogind("%s called", __FUNCTION__);
         return method_do_shutdown_or_sleep(
                         m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
                         SPECIAL_HYBRID_SLEEP_TARGET,
+#else
+                        HANDLE_HYBRID_SLEEP,
+#endif // 0
                         INHIBIT_SLEEP,
                         "org.freedesktop.login1.hibernate",
                         "org.freedesktop.login1.hibernate-multiple-sessions",
@@ -1970,7 +1989,24 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
                         "hybrid-sleep",
                         error);
 }
+
+static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+
+        return method_do_shutdown_or_sleep(
+                        m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
+                        SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+#else
+                        HANDLE_SUSPEND_THEN_HIBERNATE,
 #endif // 0
+                        INHIBIT_SLEEP,
+                        "org.freedesktop.login1.hibernate",
+                        "org.freedesktop.login1.hibernate-multiple-sessions",
+                        "org.freedesktop.login1.hibernate-ignore-inhibit",
+                        "hybrid-sleep",
+                        error);
+}
 
 static int nologin_timeout_handler(
                         sd_event_source *s,
@@ -1994,7 +2030,7 @@ static int update_schedule_file(Manager *m) {
 
         assert(m);
 
-        r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, false);
+        r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, MKDIR_WARN_MODE);
         if (r < 0)
                 return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
 
@@ -2042,7 +2078,11 @@ fail:
         return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m");
 }
 
+#if 0 /// elogind must access this from elogind-dbus.c
 static void reset_scheduled_shutdown(Manager *m) {
+#else
+void reset_scheduled_shutdown(Manager *m) {
+#endif // 0
         assert(m);
 
         m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
@@ -2243,7 +2283,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
         cancelled = m->scheduled_shutdown_type != NULL;
         reset_scheduled_shutdown(m);
 
-        if (cancelled) {
+        if (cancelled && m->enable_wall_messages) {
                 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
                 const char *tty = NULL;
                 uid_t uid = 0;
@@ -2285,6 +2325,7 @@ static int method_can_shutdown_or_sleep(
                 sd_bus_error *error) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        HandleAction handle;
         bool multiple_sessions, challenge, blocked;
         const char *result = NULL;
         uid_t uid;
@@ -2304,10 +2345,10 @@ static int method_can_shutdown_or_sleep(
 #else
                 r = can_sleep(m, sleep_verb);
 #endif // 0
+                if (IN_SET(r,  0, -ENOSPC))
+                        return sd_bus_reply_method_return(message, "s", "na");
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return sd_bus_reply_method_return(message, "s", "na");
         }
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
@@ -2325,6 +2366,32 @@ static int method_can_shutdown_or_sleep(
         multiple_sessions = r > 0;
         blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
 
+        handle = handle_action_from_string(sleep_verb);
+
+        if (handle >= 0) {
+#if 0 /// elogind uses its own variant, which can use the handle directly.
+                const char *target;
+
+                target = manager_target_for_action(handle);
+                if (target) {
+                        _cleanup_free_ char *load_state = NULL;
+
+                        r = unit_load_state(m->bus, target, &load_state);
+                        if (r < 0)
+                                return r;
+
+                        if (!streq(load_state, "loaded")) {
+                                result = "no";
+                                goto finish;
+                        }
+                }
+        }
+#else
+                log_debug_elogind("CanShutDownOrSleep: %s [%d] %s blocked",
+                                  sleep_verb, handle, blocked ? "is" : "not");
+#endif // 0
+        }
+
         if (multiple_sessions) {
                 r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
                 if (r < 0)
@@ -2336,6 +2403,8 @@ static int method_can_shutdown_or_sleep(
                         result = "challenge";
                 else
                         result = "no";
+                log_debug_elogind("CanShutDownOrSleep: multiple_sessions: %s = %s",
+                                  action_multiple_sessions, result);
         }
 
         if (blocked) {
@@ -2349,6 +2418,8 @@ static int method_can_shutdown_or_sleep(
                         result = "challenge";
                 else
                         result = "no";
+                log_debug_elogind("CanShutDownOrSleep: blocked          : %s = %s",
+                                  action_ignore_inhibit, result);
         }
 
         if (!multiple_sessions && !blocked) {
@@ -2365,8 +2436,11 @@ static int method_can_shutdown_or_sleep(
                         result = "challenge";
                 else
                         result = "no";
+                log_debug_elogind("CanShutDownOrSleep: regular          : %s = %s",
+                                  action, result);
         }
 
+ finish:
         return sd_bus_reply_method_return(message, "s", result);
 }
 
@@ -2448,6 +2522,19 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
                         error);
 }
 
+static int method_can_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+
+        return method_can_shutdown_or_sleep(
+                        m, message,
+                        INHIBIT_SLEEP,
+                        "org.freedesktop.login1.hibernate",
+                        "org.freedesktop.login1.hibernate-multiple-sessions",
+                        "org.freedesktop.login1.hibernate-ignore-inhibit",
+                        "suspend-then-hibernate",
+                        error);
+}
+
 static int property_get_reboot_to_firmware_setup(
                 sd_bus *bus,
                 const char *path,
@@ -2563,7 +2650,7 @@ static int method_set_wall_message(
         int r;
         Manager *m = userdata;
         char *wall_message;
-        int enable_wall_messages;
+        unsigned enable_wall_messages;
 
         assert(message);
         assert(m);
@@ -2744,12 +2831,12 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
         SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0),
+        SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0),
         SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0),
-        SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0),
+        SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
 
         SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2782,12 +2869,14 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SuspendThenHibernate", "b", NULL, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CanSuspendThenHibernate", NULL, "s", method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -3010,78 +3099,6 @@ static int strdup_job(sd_bus_message *reply, char **job) {
         return 1;
 }
 
-int manager_start_slice(
-                Manager *manager,
-                const char *slice,
-                const char *description,
-                const char *after,
-                const char *after2,
-                uint64_t tasks_max,
-                sd_bus_error *error,
-                char **job) {
-
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
-        int r;
-
-        assert(manager);
-        assert(slice);
-        assert(job);
-
-        r = sd_bus_message_new_method_call(
-                        manager->bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(m, "ss", strempty(slice), "fail");
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_open_container(m, 'a', "(sv)");
-        if (r < 0)
-                return r;
-
-        if (!isempty(description)) {
-                r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!isempty(after)) {
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!isempty(after2)) {
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
-                if (r < 0)
-                        return r;
-        }
-
-        r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_close_container(m);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(m, "a(sa(sv))", 0);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_call(manager->bus, m, 0, error, &reply);
-        if (r < 0)
-                return r;
-
-        return strdup_job(reply, job);
-}
-
 int manager_start_scope(
                 Manager *manager,
                 const char *scope,
@@ -3090,7 +3107,7 @@ int manager_start_scope(
                 const char *description,
                 const char *after,
                 const char *after2,
-                uint64_t tasks_max,
+                sd_bus_message *more_properties,
                 sd_bus_error *error,
                 char **job) {
 
@@ -3144,13 +3161,8 @@ int manager_start_scope(
                         return r;
         }
 
-        /* cgroup empty notification is not available in containers
-         * currently. To make this less problematic, let's shorten the
-         * stop timeout for sessions, so that we don't wait
-         * forever. */
-
-        /* Make sure that the session shells are terminated with
-         * SIGHUP since bash and friends tend to ignore SIGTERM */
+        /* Make sure that the session shells are terminated with SIGHUP since bash and friends tend to ignore
+         * SIGTERM */
         r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true);
         if (r < 0)
                 return r;
@@ -3159,9 +3171,17 @@ int manager_start_scope(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
+        /* disable TasksMax= for the session scope, rely on the slice setting for it */
+        r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", (uint64_t)-1);
         if (r < 0)
-                return r;
+                return bus_log_create_error(r);
+
+        if (more_properties) {
+                /* If TasksMax also appears here, it will overwrite the default value set above */
+                r = sd_bus_message_copy(m, more_properties, true);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_bus_message_close_container(m);
         if (r < 0)