summaryrefslogtreecommitdiff
path: root/src/login/logind-dbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/login/logind-dbus.c')
-rw-r--r--src/login/logind-dbus.c196
1 files changed, 158 insertions, 38 deletions
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 3fe6c872b7..a361b93dc7 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -144,10 +144,11 @@
" <arg name=\"what\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"why\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListInhibitors\">\n" \
- " <arg name=\"inhibitors\" type=\"a(sssuu)\" direction=\"out\"/>\n" \
+ " <arg name=\"inhibitors\" type=\"a(ssssuu)\" direction=\"out\"/>\n" \
" </method>\n" \
" <signal name=\"SessionNew\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
@@ -173,6 +174,9 @@
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
+ " <signal name=\"PrepareForShutdown\">\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" \
@@ -183,7 +187,9 @@
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"Inhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"BlockInhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DelayInhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"InhibitDelayMaxUSec\" type=\"t\" access=\"read\"/>\n" \
" </interface>\n"
#define INTROSPECTION_BEGIN \
@@ -239,7 +245,7 @@ static int bus_manager_append_inhibited(DBusMessageIter *i, const char *property
InhibitWhat w;
const char *p;
- w = manager_inhibit_what(m);
+ w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY);
p = inhibit_what_to_string(w);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &p))
@@ -638,9 +644,10 @@ fail:
static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessage *message, DBusError *error, DBusMessage **_reply) {
Inhibitor *i = NULL;
char *id = NULL;
- const char *who, *why, *what;
+ const char *who, *why, *what, *mode;
pid_t pid;
InhibitWhat w;
+ InhibitMode mm;
unsigned long ul;
int r, fifo_fd = -1;
DBusMessage *reply = NULL;
@@ -657,6 +664,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
DBUS_TYPE_STRING, &what,
DBUS_TYPE_STRING, &who,
DBUS_TYPE_STRING, &why,
+ DBUS_TYPE_STRING, &mode,
DBUS_TYPE_INVALID)) {
r = -EIO;
goto fail;
@@ -668,7 +676,16 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
goto fail;
}
- r = verify_polkit(connection, message, "org.freedesktop.login1.inhibit", false, NULL, error);
+ mm = inhibit_mode_from_string(mode);
+ if (mm < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ r = verify_polkit(connection, message,
+ m == INHIBIT_BLOCK ?
+ "org.freedesktop.login1.inhibit-block" :
+ "org.freedesktop.login1.inhibit-delay", false, NULL, error);
if (r < 0)
goto fail;
@@ -701,6 +718,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
goto fail;
i->what = w;
+ i->mode = mm;
i->pid = pid;
i->uid = (uid_t) ul;
i->why = strdup(why);
@@ -918,6 +936,76 @@ static int have_multiple_sessions(
return false;
}
+static int send_start_unit(DBusConnection *connection, const char *name, DBusError *error) {
+ DBusMessage *message, *reply;
+ const char *mode = "replace";
+
+ assert(connection);
+ assert(name);
+
+ message = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartUnit");
+ if (!message)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(message);
+ return -ENOMEM;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, message, -1, error);
+ dbus_message_unref(message);
+
+ if (!reply)
+ return -EIO;
+
+ dbus_message_unref(reply);
+ return 0;
+}
+
+static int send_prepare_for_shutdown(Manager *m, bool _active) {
+ dbus_bool_t active = _active;
+ DBusMessage *message;
+ int r = 0;
+
+ assert(m);
+
+ message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForShutdown");
+ if (!message)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
+ !dbus_connection_send(m->bus, message, NULL))
+ r = -ENOMEM;
+
+ dbus_message_unref(message);
+ return r;
+}
+
+static int delay_shutdown(Manager *m, const char *name) {
+ assert(m);
+
+ if (!m->delayed_shutdown) {
+ /* Tell everybody to prepare for shutdown */
+ send_prepare_for_shutdown(m, true);
+
+ /* Update timestamp for timeout */
+ m->delayed_shutdown_timestamp = now(CLOCK_MONOTONIC);
+ }
+
+ /* Remember what we want to do, possibly overriding what kind
+ * of shutdown we previously queued. */
+ m->delayed_shutdown = name;
+
+ return 0;
+}
+
static const BusProperty bus_login_manager_properties[] = {
{ "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
{ "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
@@ -929,7 +1017,9 @@ static const BusProperty bus_login_manager_properties[] = {
{ "IdleHint", bus_manager_append_idle_hint, "b", 0 },
{ "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 },
{ "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 },
- { "Inhibited", bus_manager_append_inhibited, "s", 0 },
+ { "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) },
{ NULL, }
};
@@ -1228,26 +1318,28 @@ static DBusHandlerResult manager_message_handler(
dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssuu)", &sub))
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssuu)", &sub))
goto oom;
HASHMAP_FOREACH(inhibitor, m->inhibitors, i) {
DBusMessageIter sub2;
dbus_uint32_t uid, pid;
- const char *what, *who, *why;
+ const char *what, *who, *why, *mode;
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
goto oom;
- what = inhibit_what_to_string(inhibitor->what);
+ what = strempty(inhibit_what_to_string(inhibitor->what));
who = strempty(inhibitor->who);
why = strempty(inhibitor->why);
+ mode = strempty(inhibit_mode_to_string(inhibitor->mode));
uid = (dbus_uint32_t) inhibitor->uid;
pid = (dbus_uint32_t) inhibitor->pid;
if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &mode) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid))
goto oom;
@@ -1641,10 +1733,8 @@ static DBusHandlerResult manager_message_handler(
} 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, inhibit;
- DBusMessage *forward, *freply;
+ bool multiple_sessions, blocked, delayed;
const char *name, *action;
- const char *mode = "replace";
if (!dbus_message_get_args(
message,
@@ -1658,7 +1748,7 @@ static DBusHandlerResult manager_message_handler(
return bus_send_error_reply(connection, message, &error, r);
multiple_sessions = r > 0;
- inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL);
+ blocked = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
if (multiple_sessions) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
@@ -1670,7 +1760,7 @@ static DBusHandlerResult manager_message_handler(
return bus_send_error_reply(connection, message, &error, r);
}
- if (inhibit) {
+ if (blocked) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
"org.freedesktop.login1.power-off-ignore-inhibit" :
"org.freedesktop.login1.reboot-ignore-inhibit";
@@ -1680,7 +1770,7 @@ static DBusHandlerResult manager_message_handler(
return bus_send_error_reply(connection, message, &error, r);
}
- if (!multiple_sessions && !inhibit) {
+ if (!multiple_sessions && !blocked) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
"org.freedesktop.login1.power-off" :
"org.freedesktop.login1.reboot";
@@ -1690,32 +1780,26 @@ static DBusHandlerResult manager_message_handler(
return bus_send_error_reply(connection, message, &error, r);
}
- forward = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StartUnit");
- if (!forward)
- return bus_send_error_reply(connection, message, NULL, -ENOMEM);
-
name = streq(dbus_message_get_member(message), "PowerOff") ?
SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET;
- if (!dbus_message_append_args(forward,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID)) {
- dbus_message_unref(forward);
- return bus_send_error_reply(connection, message, NULL, -ENOMEM);
- }
-
- freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
- dbus_message_unref(forward);
+ delayed =
+ m->inhibit_delay_max > 0 &&
+ manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
- if (!freply)
- return bus_send_error_reply(connection, message, &error, -EIO);
-
- dbus_message_unref(freply);
+ 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)
@@ -1732,7 +1816,7 @@ static DBusHandlerResult manager_message_handler(
return bus_send_error_reply(connection, message, &error, r);
multiple_sessions = r > 0;
- inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL);
+ inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
if (multiple_sessions) {
action = streq(dbus_message_get_member(message), "CanPowerOff") ?
@@ -1943,3 +2027,39 @@ finish:
return r;
}
+
+int manager_dispatch_delayed_shutdown(Manager *manager) {
+ const char *name;
+ DBusError error;
+ bool delayed;
+ int r;
+
+ assert(manager);
+
+ if (!manager->delayed_shutdown)
+ 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);
+ if (delayed)
+ return 0;
+
+ /* Reset delay data */
+ name = manager->delayed_shutdown;
+ manager->delayed_shutdown = NULL;
+
+ /* Actually do the shutdown */
+ dbus_error_init(&error);
+ r = send_start_unit(manager->bus, name, &error);
+ if (r < 0) {
+ log_warning("Failed to send delayed shutdown message: %s", bus_error_message_or_strerror(&error, -r));
+ return r;
+ }
+
+ /* Tell people about it */
+ send_prepare_for_shutdown(manager, false);
+
+ return 1;
+}