chiark / gitweb /
logind: add support for automatic suspend/hibernate/shutdown on idle
[elogind.git] / src / login / logind-dbus.c
index 4ae5ba70ba6e646366769ffeed23e1d159d7f304..77a06f2ce4587a6c228ee15b2521d566f8b09f8c 100644 (file)
@@ -83,6 +83,7 @@
         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
         "   <arg name=\"seat\" type=\"s\" direction=\"out\"/>\n"        \
         "   <arg name=\"vtnr\" type=\"u\" direction=\"out\"/>\n"        \
+        "   <arg name=\"existing\" type=\"b\" direction=\"out\"/>\n"    \
         "  </method>\n"                                                 \
         "  <method name=\"ReleaseSession\">\n"                          \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
         "  <method name=\"Hibernate\">\n"                               \
         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
         "  </method>\n"                                                 \
+        "  <method name=\"HybridSleep\">\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=\"CanHibernate\">\n"                            \
         "   <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"      \
         "  </method>\n"                                                 \
+        "  <method name=\"CanHybridSleep\">\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"          \
         "  <property name=\"DelayInhibited\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"InhibitDelayMaxUSec\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"HandlePowerKey\" type=\"s\" access=\"read\"/>\n" \
-        "  <property name=\"HandleSleepKey\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"HandleSuspendKey\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"HandleHibernateKey\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"HandleLidSwitch\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"IdleAction\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"IdleActionUSec\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"PreparingForShutdown\" type=\"b\" access=\"read\"/>\n" \
         "  <property name=\"PreparingForSleep\" type=\"b\" access=\"read\"/>\n" \
         " </interface>\n"
@@ -299,7 +309,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
         User *user = NULL;
         const char *type, *class, *seat, *tty, *display, *remote_user, *remote_host, *service;
         uint32_t uid, leader, audit_id = 0;
-        dbus_bool_t remote, kill_processes;
+        dbus_bool_t remote, kill_processes, exists;
         char **controllers = NULL, **reset_controllers = NULL;
         SessionType t;
         SessionClass c;
@@ -407,8 +417,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                 if (vtnr != 0)
                         return -EINVAL;
 
-        } else if (!isempty(tty) && s && seat_is_vtconsole(s))
-                return -EINVAL;
+        }
 
         if (s) {
                 if (seat_can_multi_session(s)) {
@@ -517,6 +526,8 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
 
                         seat = session->seat ? session->seat->id : "";
                         vtnr = session->vtnr;
+                        exists = true;
+
                         b = dbus_message_append_args(
                                         reply,
                                         DBUS_TYPE_STRING, &session->id,
@@ -525,6 +536,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                                         DBUS_TYPE_UNIX_FD, &fifo_fd,
                                         DBUS_TYPE_STRING, &seat,
                                         DBUS_TYPE_UINT32, &vtnr,
+                                        DBUS_TYPE_BOOLEAN, &exists,
                                         DBUS_TYPE_INVALID);
                         free(p);
 
@@ -641,6 +653,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
         }
 
         seat = s ? s->id : "";
+        exists = false;
         b = dbus_message_append_args(
                         reply,
                         DBUS_TYPE_STRING, &session->id,
@@ -649,6 +662,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                         DBUS_TYPE_UNIX_FD, &fifo_fd,
                         DBUS_TYPE_STRING, &seat,
                         DBUS_TYPE_UINT32, &vtnr,
+                        DBUS_TYPE_BOOLEAN, &exists,
                         DBUS_TYPE_INVALID);
         free(p);
 
@@ -729,12 +743,13 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
         }
 
         r = verify_polkit(connection, message,
-                          w == INHIBIT_SHUTDOWN         ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
-                          w == INHIBIT_SLEEP            ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep"    : "org.freedesktop.login1.inhibit-delay-sleep") :
-                          w == INHIBIT_IDLE             ? "org.freedesktop.login1.inhibit-block-idle" :
-                          w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
-                          w == INHIBIT_HANDLE_SLEEP_KEY ? "org.freedesktop.login1.inhibit-handle-sleep-key" :
-                                                          "org.freedesktop.login1.inhibit-handle-lid-switch",
+                          w == INHIBIT_SHUTDOWN             ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
+                          w == INHIBIT_SLEEP                ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep"    : "org.freedesktop.login1.inhibit-delay-sleep") :
+                          w == INHIBIT_IDLE                 ? "org.freedesktop.login1.inhibit-block-idle" :
+                          w == INHIBIT_HANDLE_POWER_KEY     ? "org.freedesktop.login1.inhibit-handle-power-key" :
+                          w == INHIBIT_HANDLE_SUSPEND_KEY   ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
+                          w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
+                                                              "org.freedesktop.login1.inhibit-handle-lid-switch",
                           false, NULL, error);
         if (r < 0)
                 goto fail;
@@ -956,24 +971,17 @@ static int flush_devices(Manager *m) {
 }
 
 static int have_multiple_sessions(
-                DBusConnection *connection,
                 Manager *m,
-                DBusMessage *message,
-                DBusError *error) {
+                uid_t uid) {
 
         Session *session;
         Iterator i;
-        unsigned long ul;
 
         assert(m);
 
-        ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
-        if (ul == (unsigned long) -1)
-                return -EIO;
-
         /* Check for other users' sessions. Greeter sessions do not count. */
         HASHMAP_FOREACH(session, m->sessions, i)
-                if (session->class == SESSION_USER && session->user->uid != ul)
+                if (session->class == SESSION_USER && session->user->uid != uid)
                         return true;
 
         return false;
@@ -1053,6 +1061,7 @@ static int bus_manager_can_shutdown_or_sleep(
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
                 const char *sleep_type,
+                const char *sleep_disk_type,
                 DBusError *error,
                 DBusMessage **_reply) {
 
@@ -1060,6 +1069,7 @@ static int bus_manager_can_shutdown_or_sleep(
         const char *result;
         DBusMessage *reply = NULL;
         int r;
+        unsigned long ul;
 
         assert(m);
         assert(connection);
@@ -1083,12 +1093,27 @@ static int bus_manager_can_shutdown_or_sleep(
                 }
         }
 
-        r = have_multiple_sessions(connection, m, message, error);
+        if (sleep_disk_type) {
+                r = can_sleep_disk(sleep_disk_type);
+                if (r < 0)
+                        return r;
+
+                if (r == 0) {
+                        result = "na";
+                        goto finish;
+                }
+        }
+
+        ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
+        if (ul == (unsigned long) -1)
+                return -EIO;
+
+        r = have_multiple_sessions(m, (uid_t) ul);
         if (r < 0)
                 return r;
 
         multiple_sessions = r > 0;
-        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false);
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
 
         if (multiple_sessions) {
                 r = verify_polkit(connection, message, action_multiple_sessions, false, &challenge, error);
@@ -1180,8 +1205,7 @@ static int bus_manager_log_shutdown(
                 q = NULL;
         }
 
-        return log_struct(LOG_NOTICE,
-                          "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SHUTDOWN),
+        return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
                           p,
                           q, NULL);
 }
@@ -1202,7 +1226,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
 
         delayed =
                 m->inhibit_delay_max > 0 &&
-                manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false);
+                manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0);
 
         if (delayed)
                 /* Shutdown is delayed, keep in mind what we
@@ -1229,6 +1253,7 @@ static int bus_manager_do_shutdown_or_sleep(
                 const char *action_multiple_sessions,
                 const char *action_ignore_inhibit,
                 const char *sleep_type,
+                const char *sleep_disk_type,
                 DBusError *error,
                 DBusMessage **_reply) {
 
@@ -1236,6 +1261,7 @@ static int bus_manager_do_shutdown_or_sleep(
         bool multiple_sessions, blocked;
         DBusMessage *reply = NULL;
         int r;
+        unsigned long ul;
 
         assert(m);
         assert(connection);
@@ -1265,12 +1291,25 @@ static int bus_manager_do_shutdown_or_sleep(
                         return -ENOTSUP;
         }
 
-        r = have_multiple_sessions(connection, m, message, error);
+        if (sleep_disk_type) {
+                r = can_sleep_disk(sleep_disk_type);
+                if (r < 0)
+                        return r;
+
+                if (r == 0)
+                        return -ENOTSUP;
+        }
+
+        ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
+        if (ul == (unsigned long) -1)
+                return -EIO;
+
+        r = have_multiple_sessions(m, (uid_t) ul);
         if (r < 0)
                 return r;
 
         multiple_sessions = r > 0;
-        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false);
+        blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, (uid_t) ul);
 
         if (multiple_sessions) {
                 r = verify_polkit(connection, message, action_multiple_sessions, interactive, NULL, error);
@@ -1302,7 +1341,7 @@ static int bus_manager_do_shutdown_or_sleep(
         return 0;
 }
 
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_button, handle_button, HandleButton);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction);
 
 static const BusProperty bus_login_manager_properties[] = {
         { "ControlGroupHierarchy",  bus_property_append_string,         "s",  offsetof(Manager, cgroup_path),        true },
@@ -1318,9 +1357,12 @@ static const BusProperty bus_login_manager_properties[] = {
         { "BlockInhibited",         bus_manager_append_inhibited,       "s",  0 },
         { "DelayInhibited",         bus_manager_append_inhibited,       "s",  0 },
         { "InhibitDelayMaxUSec",    bus_property_append_usec,           "t",  offsetof(Manager, inhibit_delay_max)   },
-        { "HandlePowerKey",         bus_manager_append_handle_button,   "s",  offsetof(Manager, handle_power_key)    },
-        { "HandleSleepKey",         bus_manager_append_handle_button,   "s",  offsetof(Manager, handle_sleep_key)    },
-        { "HandleLidSwitch",        bus_manager_append_handle_button,   "s",  offsetof(Manager, handle_lid_switch)   },
+        { "HandlePowerKey",         bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_power_key)    },
+        { "HandleSuspendKey",       bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_suspend_key)  },
+        { "HandleHibernateKey",     bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_hibernate_key)},
+        { "HandleLidSwitch",        bus_manager_append_handle_action,   "s",  offsetof(Manager, handle_lid_switch)   },
+        { "IdleAction",             bus_manager_append_handle_action,   "s",  offsetof(Manager, idle_action)         },
+        { "IdleActionUSec",         bus_property_append_usec,           "t",  offsetof(Manager, idle_action_usec) },
         { "PreparingForShutdown",   bus_manager_append_preparing,       "b",  0 },
         { "PreparingForSleep",      bus_manager_append_preparing,       "b",  0 },
         { NULL, }
@@ -1773,7 +1815,7 @@ static DBusHandlerResult manager_message_handler(
 
                 session = hashmap_get(m->sessions, name);
                 if (!session)
-                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+                        return bus_send_error_reply(connection, message, NULL, -ENOENT);
 
                 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
                         goto oom;
@@ -1783,12 +1825,9 @@ static DBusHandlerResult manager_message_handler(
                         goto oom;
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSessions")) {
-                Session *session;
-                Iterator i;
-
-                HASHMAP_FOREACH(session, m->sessions, i)
-                        if (session_send_lock(session, true) < 0)
-                                goto oom;
+                r = session_send_lock_all(m, true);
+                if (r < 0)
+                        bus_send_error_reply(connection, message, NULL, r);
 
                 reply = dbus_message_new_method_return(message);
                 if (!reply)
@@ -2054,7 +2093,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.power-off",
                                 "org.freedesktop.login1.power-off-multiple-sessions",
                                 "org.freedesktop.login1.power-off-ignore-inhibit",
-                                NULL,
+                                NULL, NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2066,7 +2105,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.reboot",
                                 "org.freedesktop.login1.reboot-multiple-sessions",
                                 "org.freedesktop.login1.reboot-ignore-inhibit",
-                                NULL,
+                                NULL, NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2079,7 +2118,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.suspend",
                                 "org.freedesktop.login1.suspend-multiple-sessions",
                                 "org.freedesktop.login1.suspend-ignore-inhibit",
-                                "mem",
+                                "mem", NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2091,7 +2130,20 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.hibernate",
                                 "org.freedesktop.login1.hibernate-multiple-sessions",
                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
-                                "disk",
+                                "disk", 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", "HybridSleep")) {
+                r = bus_manager_do_shutdown_or_sleep(
+                                m, connection, message,
+                                SPECIAL_HYBRID_SLEEP_TARGET,
+                                INHIBIT_SLEEP,
+                                "org.freedesktop.login1.hibernate",
+                                "org.freedesktop.login1.hibernate-multiple-sessions",
+                                "org.freedesktop.login1.hibernate-ignore-inhibit",
+                                "disk", "suspend",
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2104,7 +2156,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.power-off",
                                 "org.freedesktop.login1.power-off-multiple-sessions",
                                 "org.freedesktop.login1.power-off-ignore-inhibit",
-                                NULL,
+                                NULL, NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2115,7 +2167,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.reboot",
                                 "org.freedesktop.login1.reboot-multiple-sessions",
                                 "org.freedesktop.login1.reboot-ignore-inhibit",
-                                NULL,
+                                NULL, NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2127,7 +2179,7 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.suspend",
                                 "org.freedesktop.login1.suspend-multiple-sessions",
                                 "org.freedesktop.login1.suspend-ignore-inhibit",
-                                "mem",
+                                "mem", NULL,
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2139,7 +2191,19 @@ static DBusHandlerResult manager_message_handler(
                                 "org.freedesktop.login1.hibernate",
                                 "org.freedesktop.login1.hibernate-multiple-sessions",
                                 "org.freedesktop.login1.hibernate-ignore-inhibit",
-                                "disk",
+                                "disk", 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", "CanHybridSleep")) {
+                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", "suspend",
                                 &error, &reply);
                 if (r < 0)
                         return bus_send_error_reply(connection, message, &error, r);
@@ -2216,8 +2280,8 @@ static DBusHandlerResult manager_message_handler(
         }
 
         if (reply) {
-                if (!dbus_connection_send(connection, reply, NULL))
-                        goto oom;
+                if (!bus_maybe_send_reply(connection, message, reply))
+                                goto oom;
 
                 dbus_message_unref(reply);
         }
@@ -2303,7 +2367,7 @@ int manager_dispatch_delayed(Manager *manager) {
         /* Continue delay? */
         delayed =
                 manager->delayed_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
-                manager_is_inhibited(manager, manager->delayed_what, INHIBIT_DELAY, NULL, false);
+                manager_is_inhibited(manager, manager->delayed_what, INHIBIT_DELAY, NULL, false, false, 0);
         if (delayed)
                 return 0;