chiark / gitweb /
logind: implement suspend/hibernate calls with inhibition logic
authorLennart Poettering <lennart@poettering.net>
Tue, 8 May 2012 17:02:25 +0000 (19:02 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 8 May 2012 17:02:25 +0000 (19:02 +0200)
TODO
src/login/logind-dbus.c
src/login/logind-inhibit.c
src/login/logind-inhibit.h
src/login/logind.c
src/login/logind.h
src/login/org.freedesktop.login1.conf
src/login/org.freedesktop.login1.policy.in
src/shared/util.c
src/shared/util.h
src/systemctl/systemctl.c

diff --git a/TODO b/TODO
index aeba2b5..47a890d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -36,7 +36,7 @@ Features:
 * automount: implement expire
 
 * logind: auto-suspend, auto-shutdown:
-        IdleAction=(none|suspend|hibernate|poweroff)
+        IdleAction=(none|suspend|opportunistic|hibernate|poweroff)
         IdleActionDelay=...
         SessionIdleMode=(explicit|ignore|login)
         ForceShutdown=(yes|no)
index 9fc3a14..e089eeb 100644 (file)
         "  <method name=\"Reboot\">\n"                                  \
         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
         "  </method>\n"                                                 \
+        "  <method name=\"Suspend\">\n"                                 \
+        "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
+        "  </method>\n"                                                 \
+        "  <method name=\"Hibernate\">\n"                               \
+        "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
+        "  </method>\n"                                                 \
         "  <method name=\"CanPowerOff\">\n"                             \
         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
         "  </method>\n"                                                 \
         "  <method name=\"CanReboot\">\n"                               \
         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
         "  </method>\n"                                                 \
+        "  <method name=\"CanSuspend\">\n"                              \
+        "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
+        "  </method>\n"                                                 \
+        "  <method name=\"CanHibernate\">\n"                            \
+        "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
+        "  </method>\n"                                                 \
         "  <method name=\"Inhibit\">\n"                                 \
         "   <arg name=\"what\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
         "  <signal name=\"PrepareForShutdown\">\n"                      \
         "   <arg name=\"active\" type=\"b\"/>\n"                        \
         "  </signal>\n"                                                 \
+        "  <signal name=\"PrepareForSleep\">\n"                         \
+        "   <arg name=\"active\" type=\"b\"/>\n"                        \
+        "  </signal>\n"                                                 \
         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
@@ -937,12 +952,12 @@ static int have_multiple_sessions(
         return false;
 }
 
-static int send_start_unit(DBusConnection *connection, const char *name, DBusError *error) {
+static int send_start_unit(DBusConnection *connection, const char *unit_name, DBusError *error) {
         DBusMessage *message, *reply;
         const char *mode = "replace";
 
         assert(connection);
-        assert(name);
+        assert(unit_name);
 
         message = dbus_message_new_method_call(
                         "org.freedesktop.systemd1",
@@ -953,7 +968,7 @@ static int send_start_unit(DBusConnection *connection, const char *name, DBusErr
                 return -ENOMEM;
 
         if (!dbus_message_append_args(message,
-                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_STRING, &unit_name,
                                       DBUS_TYPE_STRING, &mode,
                                       DBUS_TYPE_INVALID)) {
                 dbus_message_unref(message);
@@ -970,14 +985,22 @@ static int send_start_unit(DBusConnection *connection, const char *name, DBusErr
         return 0;
 }
 
-static int send_prepare_for_shutdown(Manager *m, bool _active) {
+static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
+        static const char * const signal_name[_INHIBIT_WHAT_MAX] = {
+                [INHIBIT_SHUTDOWN] = "PrepareForShutdown",
+                [INHIBIT_SLEEP] = "PrepareForSleep"
+        };
+
         dbus_bool_t active = _active;
         DBusMessage *message;
         int r = 0;
 
         assert(m);
+        assert(w >= 0);
+        assert(w < _INHIBIT_WHAT_MAX);
+        assert(signal_name[w]);
 
-        message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForShutdown");
+        message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", signal_name[w]);
         if (!message)
                 return -ENOMEM;
 
@@ -989,21 +1012,222 @@ static int send_prepare_for_shutdown(Manager *m, bool _active) {
         return r;
 }
 
-static int delay_shutdown(Manager *m, const char *name) {
+static int delay_shutdown_or_sleep(Manager *m, InhibitWhat w, const char *unit_name) {
         assert(m);
+        assert(w >= 0);
+        assert(w < _INHIBIT_WHAT_MAX);
 
-        if (!m->delayed_shutdown) {
-                /* Tell everybody to prepare for shutdown */
-                send_prepare_for_shutdown(m, true);
+        /* Tell everybody to prepare for shutdown/sleep */
+        send_prepare_for(m, w, true);
 
-                /* Update timestamp for timeout */
-                m->delayed_shutdown_timestamp = now(CLOCK_MONOTONIC);
-        }
+        /* Update timestamp for timeout */
+        if (!m->delayed_unit)
+                m->delayed_timestamp = now(CLOCK_MONOTONIC);
 
         /* Remember what we want to do, possibly overriding what kind
-         * of shutdown we previously queued. */
-        m->delayed_shutdown = name;
+         * of unit we previously queued. */
+        m->delayed_unit = unit_name;
+        m->delayed_what = w;
+
+        return 0;
+}
+
+static int bus_manager_can_shutdown_or_sleep(
+                Manager *m,
+                DBusConnection *connection,
+                DBusMessage *message,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_type,
+                DBusError *error,
+                DBusMessage **_reply) {
+
+        bool multiple_sessions, challenge, blocked, b;
+        const char *result;
+        DBusMessage *reply = NULL;
+        int r;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+        assert(action);
+        assert(action_multiple_sessions);
+        assert(action_ignore_inhibit);
+        assert(error);
+        assert(_reply);
+
+        if (sleep_type) {
+                r = can_sleep(sleep_type);
+                if (r < 0)
+                        return r;
+
+                result = "na";
+                goto finish;
+        }
+
+        r = have_multiple_sessions(connection, m, message, error);
+        if (r < 0)
+                return r;
+
+        multiple_sessions = r > 0;
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL);
+
+        if (multiple_sessions) {
+                r = verify_polkit(connection, message, action_multiple_sessions, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0)
+                        result = "yes";
+                        else if (challenge)
+                                result = "challenge";
+                        else
+                                result = "no";
+                }
+
+        if (blocked) {
+                r = verify_polkit(connection, message, action_ignore_inhibit, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0 && !result)
+                        result = "yes";
+                else if (challenge && (!result || streq(result, "yes")))
+                        result = "challenge";
+                else
+                        result = "no";
+        }
+
+        if (!multiple_sessions && !blocked) {
+                /* If neither inhibit nor multiple sessions
+                 * apply then just check the normal policy */
+
+                r = verify_polkit(connection, message, action, false, &challenge, error);
+                if (r < 0)
+                        return r;
+
+                if (r > 0)
+                        result = "yes";
+                else if (challenge)
+                        result = "challenge";
+                else
+                        result = "no";
+        }
+
+finish:
+        reply = dbus_message_new_method_return(message);
+        if (!reply)
+                return -ENOMEM;
+
+        b = dbus_message_append_args(
+                        reply,
+                        DBUS_TYPE_STRING, &result,
+                        DBUS_TYPE_INVALID);
+        if (!b) {
+                dbus_message_unref(reply);
+                return -ENOMEM;
+        }
+
+        *_reply = reply;
+        return 0;
+}
+
+static int bus_manager_do_shutdown_or_sleep(
+                Manager *m,
+                DBusConnection *connection,
+                DBusMessage *message,
+                const char *unit_name,
+                InhibitWhat w,
+                const char *action,
+                const char *action_multiple_sessions,
+                const char *action_ignore_inhibit,
+                const char *sleep_type,
+                DBusError *error,
+                DBusMessage **_reply) {
+
+        dbus_bool_t interactive;
+        bool multiple_sessions, blocked, delayed;
+        DBusMessage *reply = NULL;
+        int r;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+        assert(unit_name);
+        assert(w >= 0);
+        assert(w <= _INHIBIT_WHAT_MAX);
+        assert(action);
+        assert(action_multiple_sessions);
+        assert(action_ignore_inhibit);
+        assert(error);
+        assert(_reply);
+
+        if (!dbus_message_get_args(
+                            message,
+                            error,
+                            DBUS_TYPE_BOOLEAN, &interactive,
+                            DBUS_TYPE_INVALID))
+                return -EINVAL;
+
+        if (sleep_type) {
+                r = can_sleep(sleep_type);
+                if (r < 0)
+                        return r;
+
+                if (r == 0)
+                        return -ENOTSUP;
+        }
+
+        r = have_multiple_sessions(connection, m, message, error);
+        if (r < 0)
+                return r;
+
+        multiple_sessions = r > 0;
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL);
+
+        if (multiple_sessions) {
+                r = verify_polkit(connection, message, action_multiple_sessions, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        if (blocked) {
+                r = verify_polkit(connection, message, action_ignore_inhibit, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!multiple_sessions && !blocked) {
+                r = verify_polkit(connection, message, action, interactive, NULL, error);
+                if (r < 0)
+                        return r;
+        }
+
+        delayed =
+                m->inhibit_delay_max > 0 &&
+                manager_is_inhibited(m, w, INHIBIT_DELAY, NULL);
+
+        if (delayed) {
+                /* Shutdown is delayed, keep in mind what we
+                 * want to do, and start a timeout */
+                r = delay_shutdown_or_sleep(m, w, unit_name);
+        } else
+                /* Shutdown is not delayed, execute it
+                 * immediately */
+                r = send_start_unit(connection, unit_name, error);
+
+        if (r < 0)
+                return r;
+
+        reply = dbus_message_new_method_return(message);
+        if (!reply)
+                return -ENOMEM;
 
+        *_reply = reply;
         return 0;
 }
 
@@ -1731,158 +1955,104 @@ static DBusHandlerResult manager_message_handler(
                 if (!reply)
                         goto oom;
 
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
-                   dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
-                dbus_bool_t interactive;
-                bool multiple_sessions, blocked, delayed;
-                const char *name, *action;
-
-                if (!dbus_message_get_args(
-                                    message,
-                                    &error,
-                                    DBUS_TYPE_BOOLEAN, &interactive,
-                                    DBUS_TYPE_INVALID))
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
-
-                r = have_multiple_sessions(connection, m, message, &error);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff")) {
+
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_POWEROFF_TARGET,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.power-off",
+                                "org.freedesktop.login1.power-off-multiple-sessions",
+                                "org.freedesktop.login1.power-off-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
-
-                multiple_sessions = r > 0;
-                blocked = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
-
-                if (multiple_sessions) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off-multiple-sessions" :
-                                "org.freedesktop.login1.reboot-multiple-sessions";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                if (blocked) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off-ignore-inhibit" :
-                                "org.freedesktop.login1.reboot-ignore-inhibit";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                if (!multiple_sessions && !blocked) {
-                        action = streq(dbus_message_get_member(message), "PowerOff") ?
-                                "org.freedesktop.login1.power-off" :
-                                "org.freedesktop.login1.reboot";
-
-                        r = verify_polkit(connection, message, action, interactive, NULL, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                name = streq(dbus_message_get_member(message), "PowerOff") ?
-                        SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET;
-
-                delayed =
-                        m->inhibit_delay_max > 0 &&
-                        manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
-
-                if (delayed) {
-                        /* Shutdown is delayed, keep in mind what we
-                         * want to do, and start a timeout */
-                        r = delay_shutdown(m, name);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, NULL, r);
-                } else {
-                        /* Shutdown is not delayed, execute it
-                         * immediately */
-                        r = send_start_unit(connection, name, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-                }
-
-                reply = dbus_message_new_method_return(message);
-                if (!reply)
-                        goto oom;
-
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") ||
-                   dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
-
-                bool multiple_sessions, challenge, inhibit, b;
-                const char *action, *result;
-
-                r = have_multiple_sessions(connection, m, message, &error);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_REBOOT_TARGET,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.reboot",
+                                "org.freedesktop.login1.reboot-multiple-sessions",
+                                "org.freedesktop.login1.reboot-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
 
-                multiple_sessions = r > 0;
-                inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
-
-                if (multiple_sessions) {
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off-multiple-sessions" :
-                                "org.freedesktop.login1.reboot-multiple-sessions";
-
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-
-                        if (r > 0)
-                                result = "yes";
-                        else if (challenge)
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
-
-                if (inhibit) {
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off-ignore-inhibit" :
-                                "org.freedesktop.login1.reboot-ignore-inhibit";
-
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
-
-                        if (r > 0 && !result)
-                                result = "yes";
-                        else if (challenge && (!result || streq(result, "yes")))
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
-
-                if (!multiple_sessions && !inhibit) {
-                        /* If neither inhibit nor multiple sessions
-                         * apply then just check the normal policy */
-
-                        action = streq(dbus_message_get_member(message), "CanPowerOff") ?
-                                "org.freedesktop.login1.power-off" :
-                                "org.freedesktop.login1.reboot";
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Suspend")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_SUSPEND_TARGET,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.suspend",
+                                "org.freedesktop.login1.suspend-multiple-sessions",
+                                "org.freedesktop.login1.suspend-ignore-inhibit",
+                                "mem",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Hibernate")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_HIBERNATE_TARGET,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.hibernate",
+                                "org.freedesktop.login1.hibernate-multiple-sessions",
+                                "org.freedesktop.login1.hibernate-ignore-inhibit",
+                                "disk",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                        r = verify_polkit(connection, message, action, false, &challenge, &error);
-                        if (r < 0)
-                                return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff")) {
 
-                        if (r > 0)
-                                result = "yes";
-                        else if (challenge)
-                                result = "challenge";
-                        else
-                                result = "no";
-                }
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.power-off",
+                                "org.freedesktop.login1.power-off-multiple-sessions",
+                                "org.freedesktop.login1.power-off-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SHUTDOWN,
+                                "org.freedesktop.login1.reboot",
+                                "org.freedesktop.login1.reboot-multiple-sessions",
+                                "org.freedesktop.login1.reboot-ignore-inhibit",
+                                NULL,
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                reply = dbus_message_new_method_return(message);
-                if (!reply)
-                        goto oom;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanSuspend")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.suspend",
+                                "org.freedesktop.login1.suspend-multiple-sessions",
+                                "org.freedesktop.login1.suspend-ignore-inhibit",
+                                "mem",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
-                b = dbus_message_append_args(
-                                reply,
-                                DBUS_TYPE_STRING, &result,
-                                DBUS_TYPE_INVALID);
-                if (!b)
-                        goto oom;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHibernate")) {
+                r = bus_manager_can_shutdown_or_sleep(
+                                m, connection, message,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.hibernate",
+                                "org.freedesktop.login1.hibernate-multiple-sessions",
+                                "org.freedesktop.login1.hibernate-ignore-inhibit",
+                                "disk",
+                                &error, &reply);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
                 char *introspection = NULL;
@@ -2029,38 +2199,39 @@ finish:
         return r;
 }
 
-int manager_dispatch_delayed_shutdown(Manager *manager) {
-        const char *name;
+int manager_dispatch_delayed(Manager *manager) {
+        const char *unit_name;
         DBusError error;
         bool delayed;
         int r;
 
         assert(manager);
 
-        if (!manager->delayed_shutdown)
+        if (!manager->delayed_unit)
                 return 0;
 
         /* Continue delay? */
         delayed =
-                manager->delayed_shutdown_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
-                manager_is_inhibited(manager, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
+                manager->delayed_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
+                manager_is_inhibited(manager, manager->delayed_what, INHIBIT_DELAY, NULL);
         if (delayed)
                 return 0;
 
         /* Reset delay data */
-        name = manager->delayed_shutdown;
-        manager->delayed_shutdown = NULL;
+        unit_name = manager->delayed_unit;
+        manager->delayed_unit = NULL;
 
         /* Actually do the shutdown */
         dbus_error_init(&error);
-        r = send_start_unit(manager->bus, name, &error);
+        r = send_start_unit(manager->bus, unit_name, &error);
         if (r < 0) {
-                log_warning("Failed to send delayed shutdown message: %s", bus_error_message_or_strerror(&error, -r));
+                log_warning("Failed to send delayed message: %s", bus_error_message_or_strerror(&error, -r));
+                dbus_error_free(&error);
                 return r;
         }
 
         /* Tell people about it */
-        send_prepare_for_shutdown(manager, false);
+        send_prepare_for(manager, manager->delayed_what, false);
 
         return 1;
 }
index 2007ec7..512fc07 100644 (file)
@@ -163,7 +163,7 @@ int inhibitor_start(Inhibitor *i) {
 
         i->started = true;
 
-        manager_send_changed(i->manager, "Inhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
 
         return 0;
 }
@@ -182,7 +182,7 @@ int inhibitor_stop(Inhibitor *i) {
 
         i->started = false;
 
-        manager_send_changed(i->manager, "Inhibited\0");
+        manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
 
         return 0;
 }
index 6364b00..4377f00 100644 (file)
 ***/
 
 typedef struct Inhibitor Inhibitor;
+typedef enum InhibitWhat InhibitWhat;
 
 #include "list.h"
 #include "util.h"
-#include "logind.h"
-#include "logind-seat.h"
 
 typedef enum InhibitWhat {
         INHIBIT_SHUTDOWN = 1,
@@ -44,6 +43,9 @@ typedef enum InhibitMode {
         _INHIBIT_MODE_INVALID = -1
 } InhibitMode;
 
+#include "logind.h"
+#include "logind-seat.h"
+
 struct Inhibitor {
         Manager *manager;
 
index 5860bfc..8d4f733 100644 (file)
@@ -1269,7 +1269,7 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
-                if (manager_dispatch_delayed_shutdown(m) > 0)
+                if (manager_dispatch_delayed(m) > 0)
                         continue;
 
                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
@@ -1277,11 +1277,11 @@ int manager_run(Manager *m) {
 
                 manager_gc(m, true);
 
-                if (m->delayed_shutdown) {
+                if (m->delayed_unit) {
                         usec_t x, y;
 
                         x = now(CLOCK_MONOTONIC);
-                        y = m->delayed_shutdown_timestamp + m->inhibit_delay_max;
+                        y = m->delayed_timestamp + m->inhibit_delay_max;
 
                         msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
                 }
index 2c05452..2433221 100644 (file)
@@ -85,8 +85,9 @@ struct Manager {
         /* If a shutdown was delayed due to a inhibitor this contains
            the unit name we are supposed to start after the delay is
            over */
-        const char *delayed_shutdown;
-        usec_t delayed_shutdown_timestamp;
+        const char *delayed_unit;
+        InhibitWhat delayed_what;
+        usec_t delayed_timestamp;
 
         usec_t inhibit_delay_max;
 };
@@ -140,7 +141,7 @@ DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, vo
 
 int manager_send_changed(Manager *manager, const char *properties);
 
-int manager_dispatch_delayed_shutdown(Manager *manager);
+int manager_dispatch_delayed(Manager *manager);
 
 /* gperf lookup function */
 const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
index 0f70b37..5860fd9 100644 (file)
 
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
+                       send_member="Suspend"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="Hibernate"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
                        send_member="CanPowerOff"/>
 
                 <allow send_destination="org.freedesktop.login1"
 
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
+                       send_member="CanSuspend"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="CanHibernate"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
                        send_member="AttachDevice"/>
 
                 <allow send_destination="org.freedesktop.login1"
index 76ed2be..8422645 100644 (file)
@@ -48,7 +48,7 @@
 
         <action id="org.freedesktop.login1.attach-device">
                 <_description>Allow attaching devices to seats</_description>
-                <_message>Authentication is required to allow attaching a device to a seat.</_message>
+                <_message>Authentication is required for attaching a device to a seat.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -58,7 +58,7 @@
 
         <action id="org.freedesktop.login1.flush-devices">
                 <_description>Flush device to seat attachments</_description>
-                <_message>Authentication is required to allow resetting how devices are attached to seats.</_message>
+                <_message>Authentication is required for resetting how devices are attached to seats.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -68,7 +68,7 @@
 
         <action id="org.freedesktop.login1.power-off">
                 <_description>Power off the system</_description>
-                <_message>Authentication is required to allow powering off the system.</_message>
+                <_message>Authentication is required for powering off the system.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -77,8 +77,8 @@
         </action>
 
         <action id="org.freedesktop.login1.power-off-multiple-sessions">
-                <_description>Power off the system when other users are logged in</_description>
-                <_message>Authentication is required to allow powering off the system while other users are logged in.</_message>
+                <_description>Power off the system while other users are logged in</_description>
+                <_message>Authentication is required for powering off the system while other users are logged in.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -87,8 +87,8 @@
         </action>
 
         <action id="org.freedesktop.login1.power-off-ignore-inhibit">
-                <_description>Power off the system when an application asked to inhibit it</_description>
-                <_message>Authentication is required to allow powering off the system while an application asked to inhibit it.</_message>
+                <_description>Power off the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for powering off the system while an application asked to inhibit it.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -98,7 +98,7 @@
 
         <action id="org.freedesktop.login1.reboot">
                 <_description>Reboot the system</_description>
-                <_message>Authentication is required to allow rebooting the system.</_message>
+                <_message>Authentication is required for rebooting the system.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.reboot-multiple-sessions">
-                <_description>Reboot the system when other users are logged in</_description>
-                <_message>Authentication is required to allow rebooting the system while other users are logged in.</_message>
+                <_description>Reboot the system while other users are logged in</_description>
+                <_message>Authentication is required for rebooting the system while other users are logged in.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.reboot-ignore-inhibit">
-                <_description>Reboot the system when an application asked to inhibit it</_description>
-                <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it.</_message>
+                <_description>Reboot the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for rebooting the system while an application asked to inhibit it.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend">
+                <_description>Suspend the system</_description>
+                <_message>Authentication is required for suspending the system.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend-multiple-sessions">
+                <_description>Suspend the system while other users are logged in</_description>
+                <_message>Authentication is required for suspending the system while other users are logged in.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.suspend-ignore-inhibit">
+                <_description>Suspend the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for suspending the system while an application asked to inhibit it.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate">
+                <_description>Hibernate the system</_description>
+                <_message>Authentication is required for hibernating the system.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate-multiple-sessions">
+                <_description>Hibernate the system while other users are logged in</_description>
+                <_message>Authentication is required for hibernating the system while other users are logged in.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.login1.hibernate-ignore-inhibit">
+                <_description>Hibernate the system while an application asked to inhibit it</_description>
+                <_message>Authentication is required for hibernating the system while an application asked to inhibit it.</_message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
index d8d3f1a..5f06c4b 100644 (file)
@@ -5590,3 +5590,28 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
 
         return r;
 }
+
+int can_sleep(const char *type) {
+        char *p, *w, *state;
+        size_t l, k;
+        bool found = false;
+        int r;
+
+        assert(type);
+
+        r = read_one_line_file("/sys/power/state", &p);
+        if (r < 0)
+                return r == -ENOENT ? 0 : r;
+
+        k = strlen(type);
+
+        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
+                if (l == k && strncmp(w, type, l) == 0) {
+                        found = true;
+                        break;
+                }
+        }
+
+        free(p);
+        return found;
+}
index 59a69a8..272ab48 100644 (file)
@@ -505,4 +505,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim);
 
 int getenv_for_pid(pid_t pid, const char *field, char **_value);
 
+int can_sleep(const char *type);
+
 #endif
index 762b5be..4708a35 100644 (file)
@@ -1750,6 +1750,14 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
                 method = "PowerOff";
                 break;
 
+        case ACTION_SUSPEND:
+                method = "Suspend";
+                break;
+
+        case ACTION_HIBERNATE:
+                method = "Hibernate";
+                break;
+
         default:
                 return -EINVAL;
         }
@@ -1839,7 +1847,9 @@ static int start_special(DBusConnection *bus, char **args) {
         /* first try logind, to allow authentication with polkit */
         if (geteuid() != 0 &&
             (a == ACTION_POWEROFF ||
-             a == ACTION_REBOOT)) {
+             a == ACTION_REBOOT ||
+             a == ACTION_SUSPEND ||
+             a == ACTION_HIBERNATE)) {
                 r = reboot_with_logind(bus, a);
                 if (r >= 0)
                         return r;