diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-05-05 00:34:48 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-05-05 00:36:08 +0200 |
commit | eecd1362f7f4de432483b5d77c56726c3621a83a (patch) | |
tree | e8f1f3639121d317660cc6cc0d5ffe8f8731a39f /src/login/logind-dbus.c | |
parent | a26336da875a6657d404d1e44b86ae067c34b110 (diff) |
logind: implement delay inhibitor locks in addition to block inhibitor locks
This is useful to allow applications to synchronously save data before
the system is suspended or shut down.
Diffstat (limited to 'src/login/logind-dbus.c')
-rw-r--r-- | src/login/logind-dbus.c | 196 |
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; +} |