summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-01-11 04:24:05 +0100
committerLennart Poettering <lennart@poettering.net>2013-01-11 04:24:05 +0100
commitb37844d3d72af3afbcb801476cf07c085519f392 (patch)
treefc9b1ffc9ada510b7206431e6a3f389dc38723a0
parent946c11ed25429d6050a4d8fb0e0f9838167765e0 (diff)
systemctl: honour inhibitors when shutting down or entering sleep state
-rw-r--r--man/systemctl.xml27
-rw-r--r--src/systemctl/systemctl.c122
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);