summaryrefslogtreecommitdiff
path: root/src/login
diff options
context:
space:
mode:
Diffstat (limited to 'src/login')
-rw-r--r--src/login/inhibit.c353
-rw-r--r--src/login/logind-dbus.c196
-rw-r--r--src/login/logind-gperf.gperf1
-rw-r--r--src/login/logind-inhibit.c47
-rw-r--r--src/login/logind-inhibit.h15
-rw-r--r--src/login/logind.c21
-rw-r--r--src/login/logind.conf1
-rw-r--r--src/login/logind.h10
-rw-r--r--src/login/org.freedesktop.login1.policy.in32
9 files changed, 612 insertions, 64 deletions
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
new file mode 100644
index 0000000000..a817c84b36
--- /dev/null
+++ b/src/login/inhibit.c
@@ -0,0 +1,353 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <getopt.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dbus.h>
+#include <unistd.h>
+
+#include "dbus-common.h"
+#include "util.h"
+#include "build.h"
+#include "strv.h"
+
+static const char* arg_what = "idle:suspend:shutdown";
+static const char* arg_who = NULL;
+static const char* arg_why = "Unknown reason";
+static const char* arg_mode = "block";
+
+static enum {
+ ACTION_INHIBIT,
+ ACTION_LIST
+} arg_action = ACTION_INHIBIT;
+
+static int inhibit(DBusConnection *bus, DBusError *error) {
+ DBusMessage *m = NULL, *reply = NULL;
+ int fd;
+
+ assert(bus);
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "Inhibit");
+ if (!m)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &arg_what,
+ DBUS_TYPE_STRING, &arg_who,
+ DBUS_TYPE_STRING, &arg_why,
+ DBUS_TYPE_STRING, &arg_mode,
+ DBUS_TYPE_INVALID)) {
+ fd = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ fd = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, error,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID)){
+ fd = -EIO;
+ goto finish;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return fd;
+}
+
+static int print_inhibitors(DBusConnection *bus, DBusError *error) {
+ DBusMessage *m, *reply;
+ unsigned n = 0;
+ DBusMessageIter iter, sub, sub2;
+ int r;
+
+ assert(bus);
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListInhibitors");
+ if (!m)
+ return -ENOMEM;
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ r = -EIO;
+ goto finish;
+ }
+ dbus_message_iter_recurse(&iter, &sub);
+
+ printf("%-21s %-20s %-20s %-5s %6s %6s\n",
+ "WHAT",
+ "WHO",
+ "WHY",
+ "MODE",
+ "UID",
+ "PID");
+
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *what, *who, *why, *mode;
+ char *ewho, *ewhy;
+ dbus_uint32_t uid, pid;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ewho = ellipsize(who, 20, 66);
+ ewhy = ellipsize(why, 20, 66);
+
+ printf("%-21s %-20s %-20s %-5s %6lu %6lu\n",
+ what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid);
+
+ free(ewho);
+ free(ewhy);
+
+ dbus_message_iter_next(&sub);
+
+ n++;
+ }
+
+ printf("\n%u inhibitors listed.\n", n);
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Execute a process while inhibiting shutdown/suspend/idle.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --what=WHAT Operations to inhibit, colon separated list of idle,\n"
+ " suspend, shutdown\n"
+ " --who=STRING A descriptive string who is inhibiting\n"
+ " --why=STRING A descriptive string why is being inhibited\n"
+ " --mode=MODE One of block or delay\n"
+ " --list List active inhibitors\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_WHAT,
+ ARG_WHO,
+ ARG_WHY,
+ ARG_MODE,
+ ARG_LIST,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "what", required_argument, NULL, ARG_WHAT },
+ { "who", required_argument, NULL, ARG_WHO },
+ { "why", required_argument, NULL, ARG_WHY },
+ { "mode", required_argument, NULL, ARG_MODE },
+ { "list", no_argument, NULL, ARG_LIST },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(DISTRIBUTION);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_WHAT:
+ arg_what = optarg;
+ break;
+
+ case ARG_WHO:
+ arg_who = optarg;
+ break;
+
+ case ARG_WHY:
+ arg_why = optarg;
+ break;
+
+ case ARG_MODE:
+ arg_mode = optarg;
+ break;
+
+ case ARG_LIST:
+ arg_action = ACTION_LIST;
+ break;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (arg_action == ACTION_INHIBIT && optind >= argc) {
+ log_error("Missing command line to execute.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r, exit_code = 0;
+ DBusConnection *bus = NULL;
+ DBusError error;
+ int fd = -1;
+
+ dbus_error_init(&error);
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+ if (!bus) {
+ log_error("Failed to connect to bus: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (arg_action == ACTION_LIST) {
+
+ r = print_inhibitors(bus, &error);
+ if (r < 0) {
+ log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
+ goto finish;
+ }
+
+ } else {
+ char *w = NULL;
+ pid_t pid;
+
+ if (!arg_who)
+ arg_who = w = strv_join(argv + optind, " ");
+
+ fd = inhibit(bus, &error);
+ free(w);
+
+ if (fd < 0) {
+ log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
+ r = fd;
+ goto finish;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if (pid == 0) {
+ /* Child */
+
+ close_nointr_nofail(fd);
+ execvp(argv[optind], argv + optind);
+ log_error("Failed to execute %s: %m", argv[optind]);
+ _exit(EXIT_FAILURE);
+ }
+
+ r = wait_for_terminate_and_warn(argv[optind], pid);
+ if (r >= 0)
+ exit_code = r;
+ }
+
+finish:
+ if (bus) {
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ dbus_error_free(&error);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r < 0 ? EXIT_FAILURE : exit_code;
+}
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;
+}
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
index 940fe10e89..d8ef92a016 100644
--- a/src/login/logind-gperf.gperf
+++ b/src/login/logind-gperf.gperf
@@ -20,3 +20,4 @@ Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_u
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers)
Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers)
+Login.InhibitDelayMaxSec,config_parse_usec, 0, offsetof(Manager, inhibit_delay_max)
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 78afee3139..e4eefd0def 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -97,9 +97,11 @@ int inhibitor_save(Inhibitor *i) {
fprintf(f,
"# This is private data. Do not parse.\n"
"WHAT=%s\n"
+ "MODE=%s\n"
"UID=%lu\n"
"PID=%lu\n",
inhibit_what_to_string(i->what),
+ inhibit_mode_to_string(i->mode),
(unsigned long) i->uid,
(unsigned long) i->pid);
@@ -152,9 +154,10 @@ int inhibitor_start(Inhibitor *i) {
dual_timestamp_get(&i->since);
- log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.",
+ log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.",
strna(i->who), strna(i->why),
- (unsigned long) i->pid, (unsigned long) i->uid);
+ (unsigned long) i->pid, (unsigned long) i->uid,
+ inhibit_mode_to_string(i->mode));
inhibitor_save(i);
@@ -169,9 +172,10 @@ int inhibitor_stop(Inhibitor *i) {
assert(i);
if (i->started)
- log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.",
+ log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.",
strna(i->who), strna(i->why),
- (unsigned long) i->pid, (unsigned long) i->uid);
+ (unsigned long) i->pid, (unsigned long) i->uid,
+ inhibit_mode_to_string(i->mode));
if (i->state_file)
unlink(i->state_file);
@@ -185,13 +189,15 @@ int inhibitor_stop(Inhibitor *i) {
int inhibitor_load(Inhibitor *i) {
InhibitWhat w;
+ InhibitMode mm;
int r;
char *cc,
*what = NULL,
*uid = NULL,
*pid = NULL,
*who = NULL,
- *why = NULL;
+ *why = NULL,
+ *mode = NULL;
r = parse_env_file(i->state_file, NEWLINE,
"WHAT", &what,
@@ -199,17 +205,25 @@ int inhibitor_load(Inhibitor *i) {
"PID", &pid,
"WHO", &who,
"WHY", &why,
+ "MODE", &mode,
"FIFO", &i->fifo_path,
NULL);
if (r < 0)
goto finish;
- w = inhibit_what_from_string(what);
+ w = what ? inhibit_what_from_string(what) : 0;
if (w >= 0)
i->what = w;
- parse_uid(uid, &i->uid);
- parse_pid(pid, &i->pid);
+ mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
+ if (mm >= 0)
+ i->mode = mm;
+
+ if (uid)
+ parse_uid(uid, &i->uid);
+
+ if (pid)
+ parse_pid(pid, &i->pid);
if (who) {
cc = cunescape(who);
@@ -314,7 +328,7 @@ void inhibitor_remove_fifo(Inhibitor *i) {
}
}
-InhibitWhat manager_inhibit_what(Manager *m) {
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
Inhibitor *i;
Iterator j;
InhibitWhat what = 0;
@@ -322,12 +336,13 @@ InhibitWhat manager_inhibit_what(Manager *m) {
assert(m);
HASHMAP_FOREACH(i, m->inhibitor_fds, j)
- what |= i->what;
+ if (i->mode == mm)
+ what |= i->what;
return what;
}
-bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) {
+bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) {
Inhibitor *i;
Iterator j;
struct dual_timestamp ts = { 0, 0 };
@@ -340,6 +355,9 @@ bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) {
if (!(i->what & w))
continue;
+ if (i->mode != mm)
+ continue;
+
if (!inhibited ||
i->since.monotonic < ts.monotonic)
ts = i->since;
@@ -391,3 +409,10 @@ InhibitWhat inhibit_what_from_string(const char *s) {
return what;
}
+
+static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
+ [INHIBIT_BLOCK] = "block",
+ [INHIBIT_DELAY] = "delay"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index 1d47cfc68d..823af39918 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -37,6 +37,13 @@ typedef enum InhibitWhat {
_INHIBIT_WHAT_INVALID = -1
} InhibitWhat;
+typedef enum InhibitMode {
+ INHIBIT_BLOCK,
+ INHIBIT_DELAY,
+ _INHIBIT_MODE_MAX,
+ _INHIBIT_MODE_INVALID = -1
+} InhibitMode;
+
struct Inhibitor {
Manager *manager;
@@ -48,6 +55,7 @@ struct Inhibitor {
InhibitWhat what;
char *who;
char *why;
+ InhibitMode mode;
pid_t pid;
uid_t uid;
@@ -70,10 +78,13 @@ int inhibitor_stop(Inhibitor *i);
int inhibitor_create_fifo(Inhibitor *i);
void inhibitor_remove_fifo(Inhibitor *i);
-InhibitWhat manager_inhibit_what(Manager *m);
-bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since);
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm);
+bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since);
const char *inhibit_what_to_string(InhibitWhat k);
InhibitWhat inhibit_what_from_string(const char *s);
+const char *inhibit_mode_to_string(InhibitMode k);
+InhibitMode inhibit_mode_from_string(const char *s);
+
#endif
diff --git a/src/login/logind.c b/src/login/logind.c
index 6b7012ec73..5860bfc8d7 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -50,6 +50,7 @@ Manager *manager_new(void) {
m->udev_vcsa_fd = -1;
m->epoll_fd = -1;
m->n_autovts = 6;
+ m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->devices = hashmap_new(string_hash_func, string_compare_func);
m->seats = hashmap_new(string_hash_func, string_compare_func);
@@ -1163,7 +1164,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
assert(m);
- idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, t);
+ idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t);
HASHMAP_FOREACH(s, m->sessions, i) {
dual_timestamp k;
@@ -1264,15 +1265,28 @@ int manager_run(Manager *m) {
for (;;) {
struct epoll_event event;
int n;
+ int msec = -1;
manager_gc(m, true);
+ if (manager_dispatch_delayed_shutdown(m) > 0)
+ continue;
+
if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
continue;
manager_gc(m, true);
- n = epoll_wait(m->epoll_fd, &event, 1, -1);
+ if (m->delayed_shutdown) {
+ usec_t x, y;
+
+ x = now(CLOCK_MONOTONIC);
+ y = m->delayed_shutdown_timestamp + m->inhibit_delay_max;
+
+ msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
+ }
+
+ n = epoll_wait(m->epoll_fd, &event, 1, msec);
if (n < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
@@ -1281,6 +1295,9 @@ int manager_run(Manager *m) {
return -errno;
}
+ if (n == 0)
+ continue;
+
switch (event.data.u32) {
case FD_SEAT_UDEV:
diff --git a/src/login/logind.conf b/src/login/logind.conf
index 6a2cefa576..c0779a7685 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf
@@ -14,3 +14,4 @@
#KillExcludeUsers=root
#Controllers=
#ResetControllers=cpu
+#InhibitDelayMaxSec=5
diff --git a/src/login/logind.h b/src/login/logind.h
index 4e9dcd5fed..2c0545206d 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -81,6 +81,14 @@ struct Manager {
Hashmap *cgroups;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
+
+ /* 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;
+
+ usec_t inhibit_delay_max;
};
enum {
@@ -132,6 +140,8 @@ 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);
+
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index a2dc4025ce..76ed2bea38 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -16,9 +16,9 @@
<vendor>The systemd Project</vendor>
<vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
- <action id="org.freedesktop.login1.inhibit">
+ <action id="org.freedesktop.login1.inhibit-block">
<_description>Allow applications to inhibit system shutdown and suspend</_description>
- <_message>Authentication is required to allow an application to inhibit system shutdown or suspend</_message>
+ <_message>Authentication is required to allow an application to inhibit system shutdown or suspend.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>yes</allow_inactive>
@@ -26,9 +26,19 @@
</defaults>
</action>
+ <action id="org.freedesktop.login1.inhibit-delay">
+ <_description>Allow applications to delay system shutdown and suspend</_description>
+ <_message>Authentication is required to allow an application to delay system shutdown or suspend.</_message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.login1.set-user-linger">
<_description>Allow non-logged-in users to run programs</_description>
- <_message>Authentication is required to allow a non-logged-in user to run programs</_message>
+ <_message>Authentication is required to allow a non-logged-in user to run programs.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
@@ -38,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 to allow attaching a device to a seat.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
@@ -48,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 to allow resetting how devices are attached to seats.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
@@ -58,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 to allow powering off the system.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
@@ -68,7 +78,7 @@
<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>
+ <_message>Authentication is required to allow 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>
@@ -78,7 +88,7 @@
<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>
+ <_message>Authentication is required to allow 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>
@@ -88,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 to allow rebooting the system.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
@@ -98,7 +108,7 @@
<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>
+ <_message>Authentication is required to allow 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>
@@ -108,7 +118,7 @@
<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>
+ <_message>Authentication is required to allow 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>