summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2012-05-08 19:02:25 +0200
committerLennart Poettering <lennart@poettering.net>2012-05-08 19:02:25 +0200
commitd889a2069a87e4617b32ddbdeace5a53a12c699d (patch)
tree7b83f31c7e3f02fda73d4e208598bd93713f9685
parent6edd7d0a09171ea5ae8e01b7b1cbcb0bdfbfeb16 (diff)
logind: implement suspend/hibernate calls with inhibition logic
-rw-r--r--TODO2
-rw-r--r--src/login/logind-dbus.c505
-rw-r--r--src/login/logind-inhibit.c4
-rw-r--r--src/login/logind-inhibit.h6
-rw-r--r--src/login/logind.c6
-rw-r--r--src/login/logind.h7
-rw-r--r--src/login/org.freedesktop.login1.conf16
-rw-r--r--src/login/org.freedesktop.login1.policy.in84
-rw-r--r--src/shared/util.c25
-rw-r--r--src/shared/util.h2
-rw-r--r--src/systemctl/systemctl.c12
11 files changed, 478 insertions, 191 deletions
diff --git a/TODO b/TODO
index aeba2b5165..47a890d8b1 100644
--- 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)
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 9fc3a14317..e089eebed9 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -135,12 +135,24 @@
" <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" \
@@ -178,6 +190,9 @@
" <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;
}
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 2007ec79e4..512fc0716b 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -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;
}
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index 6364b00586..4377f00429 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -23,11 +23,10 @@
***/
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;
diff --git a/src/login/logind.c b/src/login/logind.c
index 5860bfc8d7..8d4f7333c9 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -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);
}
diff --git a/src/login/logind.h b/src/login/logind.h
index 2c0545206d..24332210c2 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -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);
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index 0f70b37c04..5860fd9226 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -94,6 +94,14 @@
<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"
@@ -102,6 +110,14 @@
<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"
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 76ed2bea38..8422645b80 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -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>
@@ -107,8 +107,8 @@
</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>
@@ -117,8 +117,68 @@
</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>
diff --git a/src/shared/util.c b/src/shared/util.c
index d8d3f1a16d..5f06c4b7d8 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -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;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index 59a69a898a..272ab48651 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -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
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 762b5be346..4708a35835 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -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;