From: Zbigniew Jędrzejewski-Szmek Date: Tue, 10 Apr 2018 11:15:00 +0000 (+0200) Subject: logind: refuse operations if the target unit is masked or unavailable X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=797ec1bf0caeea2b72de01f8082bba2d785a20e4;p=elogind.git logind: refuse operations if the target unit is masked or unavailable If hibernate.target is masked, and systemctl hibernate is invoked, havoc ensues. logind starts the hibernation operation, but then doesn't go through with it; gnome-shell segfaults. Let's be nice to the user and refuse doing anything in that case. $ sudo systemctl mask hibernate.target $ busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanHibernate s "no" $ sudo systemctl hibernate Failed to hibernate system via logind: Access denied Failed to start hibernate.target: Unit hibernate.target is masked. https://bugzilla.redhat.com/show_bug.cgi?id=1468003#c4 --- diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 21d68d828..d2fd8ddfb 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -3,6 +3,19 @@ This file is part of systemd. Copyright 2012 Lennart Poettering + + elogind 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. + + elogind 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 elogind; If not, see . ***/ #include @@ -25,6 +38,23 @@ #include "fileio.h" #include "sd-messages.h" #include "strv.h" +const char* manager_target_for_action(HandleAction handle) { + static const char * const target_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, + [HANDLE_HALT] = SPECIAL_HALT_TARGET, + [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, + [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET, + [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, + }; + + assert(handle >= 0); + if (handle < (ssize_t) ELEMENTSOF(target_table)) + return target_table[handle]; + return NULL; +} int manager_handle_action( Manager *m, @@ -45,22 +75,12 @@ int manager_handle_action( }; #if 0 /// elogind does this itself. No target table required - static const char * const target_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, - [HANDLE_HALT] = SPECIAL_HALT_TARGET, - [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, - [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, - [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET, - [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, - }; #endif // 0 - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; InhibitWhat inhibit_operation; Inhibitor *offending = NULL; bool supported; + const char *target; int r; assert(m); @@ -92,7 +112,6 @@ int manager_handle_action( /* Locking is handled differently from the rest. */ if (handle == HANDLE_LOCK) { - if (!is_edge) return 0; @@ -133,6 +152,8 @@ int manager_handle_action( return -EALREADY; } + assert_se(target = manager_target_for_action(handle)); + inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; @@ -165,7 +186,7 @@ int manager_handle_action( log_info("%s", message_table[handle]); #if 0 /// elogind uses its own variant, which can use the handle directly. - r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error); #else r = bus_manager_shutdown_or_sleep_now_or_later(m, handle, inhibit_operation, &error); #endif // 0 @@ -187,7 +208,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { [HANDLE_HIBERNATE] = "hibernate", [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate", - [HANDLE_LOCK] = "lock" + [HANDLE_LOCK] = "lock", }; DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); diff --git a/src/login/logind-action.h b/src/login/logind-action.h index 8c7de83e8..4fdbc60e4 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -5,6 +5,19 @@ This file is part of systemd. Copyright 2012 Lennart Poettering + + elogind 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. + + elogind 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 elogind; If not, see . ***/ typedef enum HandleAction { @@ -35,4 +48,5 @@ int manager_handle_action( const char* handle_action_to_string(HandleAction h) _const_; HandleAction handle_action_from_string(const char *s) _pure_; +const char* manager_target_for_action(HandleAction handle); int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index e53644f8d..73be87b19 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -3,6 +3,19 @@ This file is part of systemd. Copyright 2011 Lennart Poettering + + elogind 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. + + elogind 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 elogind; If not, see . ***/ #include @@ -16,6 +29,7 @@ #include "audit-util.h" #include "bus-common-errors.h" #include "bus-error.h" +//#include "bus-unit-util.h" #include "bus-util.h" #include "dirent-util.h" //#include "efivars.h" @@ -1741,6 +1755,7 @@ int bus_manager_shutdown_or_sleep_now_or_later( #if 0 /// elogind has HandleAction instead of const char* unit_name const char *unit_name, + _cleanup_free_ char *load_state = NULL; #else HandleAction unit_name, #endif // 0 @@ -1755,6 +1770,15 @@ int bus_manager_shutdown_or_sleep_now_or_later( assert(w > 0); assert(w <= _INHIBIT_WHAT_MAX); assert(!m->action_job); + + r = unit_load_state(m->bus, unit_name, &load_state); + if (r < 0) + return r; + + if (!streq(load_state, "loaded")) { + log_notice("Unit %s is %s, refusing operation.", unit_name, load_state); + return -EACCES; + } #else assert(w > 0); assert(w <= _INHIBIT_WHAT_MAX); @@ -2360,6 +2384,7 @@ static int method_can_shutdown_or_sleep( sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + HandleAction handle; bool multiple_sessions, challenge, blocked; const char *result = NULL; uid_t uid; @@ -2400,6 +2425,25 @@ static int method_can_shutdown_or_sleep( multiple_sessions = r > 0; blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + handle = handle_action_from_string(sleep_verb); + if (handle >= 0) { + const char *target; + + target = manager_target_for_action(handle); + if (target) { + _cleanup_free_ char *load_state = NULL; + + r = unit_load_state(m->bus, target, &load_state); + if (r < 0) + return r; + + if (!streq(load_state, "loaded")) { + result = "no"; + goto finish; + } + } + } + if (multiple_sessions) { r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); if (r < 0) @@ -2442,6 +2486,7 @@ static int method_can_shutdown_or_sleep( result = "no"; } + finish: return sd_bus_reply_method_return(message, "s", result); }