mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
systemctl: honour inhibitors when shutting down or entering sleep state
This commit is contained in:
parent
946c11ed25
commit
b37844d3d7
@ -190,6 +190,33 @@
|
||||
applications.</para></listitem>
|
||||
</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>
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user