diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-01-11 04:24:05 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-01-11 04:24:05 +0100 |
commit | b37844d3d72af3afbcb801476cf07c085519f392 (patch) | |
tree | fc9b1ffc9ada510b7206431e6a3f389dc38723a0 | |
parent | 946c11ed25429d6050a4d8fb0e0f9838167765e0 (diff) |
systemctl: honour inhibitors when shutting down or entering sleep state
-rw-r--r-- | man/systemctl.xml | 27 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 122 |
2 files changed, 147 insertions, 2 deletions
diff --git a/man/systemctl.xml b/man/systemctl.xml index f86952c683..2f33e0c9e2 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -191,6 +191,33 @@ </varlistentry> <varlistentry> + <term><option>-i</option></term> + <term><option>--ignore-inhibitors</option></term> + + <listitem><para>When system shutdown + or a sleep state is requested, ignore + inhibitor locks. Applications can + establish inhibitor locks to avoid + that certain important operations + (such as CD burning or suchlike) are + interrupted by system shutdown or a + sleep state. Any user may take these + locks and privileged users may + override these locks. If any locks are + taken, shutdown and sleep state + requests will normally fail + (regardless if privileged or not) and + list of active locks is + printed. However if + <option>--ignore-inhibitors</option> + is specified the locks are ignored and + not printed, and the operation + attempted anyway, possibly requiring + additional + privileges.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>--quiet</option></term> <term><option>-q</option></term> diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index b4c4ea36dc..bf76a0e9c0 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -78,6 +78,7 @@ static bool arg_no_pager = false; static bool arg_no_wtmp = false; static bool arg_no_wall = false; static bool arg_no_reload = false; +static bool arg_ignore_inhibitors = false; static bool arg_dry = false; static bool arg_quiet = false; static bool arg_full = false; @@ -1778,6 +1779,104 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { #endif } +static int check_inhibitors(DBusConnection *bus, enum action a) { +#ifdef HAVE_LOGIND + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub, sub2; + int r; + unsigned c = 0; + + if (arg_ignore_inhibitors) + return 0; + + if (!on_tty()) + return 0; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + /* If logind is not around, then there are no inhibitors... */ + return 0; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *what, *who, *why, *mode; + uint32_t uid, pid; + _cleanup_strv_free_ char **sv = NULL; + _cleanup_free_ char *comm = NULL; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; + } + + 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) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (!streq(mode, "block")) + goto next; + + sv = strv_split(what, ":"); + if (!sv) + return log_oom(); + + if (!strv_contains(sv, + a == ACTION_HALT || + a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_KEXEC ? "shutdown" : "sleep")) + goto next; + + get_process_comm(pid, &comm); + log_warning("Operation inhibited by \"%s\" (PID %lu \"%s\", UID %lu), reason is \"%s\".", who, (unsigned long) pid, strna(comm), (unsigned long) uid, why); + c++; + + next: + dbus_message_iter_next(&sub); + } + + dbus_message_iter_recurse(&iter, &sub); + + if (c <= 0) + return 0; + + log_error("Please try again after closing inhibitors or ignore them with 'systemctl %s -i'.", + a == ACTION_HALT ? "halt" : + a == ACTION_POWEROFF ? "poweroff" : + a == ACTION_REBOOT ? "reboot" : + a == ACTION_KEXEC ? "kexec" : + a == ACTION_SUSPEND ? "suspend" : + a == ACTION_HIBERNATE ? "hibernate" : "hybrid-sleep"); + + return -EPERM; +#else + return 0; +#endif +} + static int start_special(DBusConnection *bus, char **args) { enum action a; int r; @@ -1805,6 +1904,12 @@ static int start_special(DBusConnection *bus, char **args) { a == ACTION_EXIT)) return daemon_reload(bus, args); + if (arg_force <= 0) { + r = check_inhibitors(bus, a); + if (r < 0) + return r; + } + /* first try logind, to allow authentication with polkit */ if (geteuid() != 0 && (a == ACTION_POWEROFF || @@ -3895,6 +4000,8 @@ static int systemctl_help(void) { " pending\n" " --ignore-dependencies\n" " When queueing a new job, ignore all its dependencies\n" + " -i --ignore-inhibitors\n" + " When shutting down or sleeping, ignore inhibitors\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" " -H --host=[USER@]HOST\n" @@ -4105,6 +4212,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "full", no_argument, NULL, ARG_FULL }, { "fail", no_argument, NULL, ARG_FAIL }, { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, + { "ignore-inhibitors", no_argument, NULL, 'i' }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, { "global", no_argument, NULL, ARG_GLOBAL }, @@ -4134,7 +4242,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:i", options, NULL)) >= 0) { switch (c) { @@ -4300,6 +4408,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } break; + case 'i': + arg_ignore_inhibitors = true; + break; + case '?': return -EINVAL; @@ -5172,6 +5284,12 @@ static _noreturn_ void halt_now(enum action a) { static int halt_main(DBusConnection *bus) { int r; + if (arg_when <= 0 && arg_force <= 0) { + r = check_inhibitors(bus, arg_action); + if (r < 0) + return r; + } + if (geteuid() != 0) { /* Try logind if we are a normal user and no special * mode applies. Maybe PolicyKit allows us to shutdown @@ -5179,7 +5297,7 @@ static int halt_main(DBusConnection *bus) { if (arg_when <= 0 && !arg_dry && - !arg_force && + arg_force <= 0 && (arg_action == ACTION_POWEROFF || arg_action == ACTION_REBOOT)) { r = reboot_with_logind(bus, arg_action); |