diff options
-rw-r--r-- | src/login/logind-action.c | 37 | ||||
-rw-r--r-- | src/login/logind-action.h | 1 | ||||
-rw-r--r-- | src/login/logind-dbus.c | 45 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 26 | ||||
-rw-r--r-- | src/shared/bus-unit-util.h | 2 | ||||
-rw-r--r-- | src/shared/sleep-config.c | 60 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 18 | ||||
-rw-r--r-- | src/test/test-sleep.c | 15 |
8 files changed, 136 insertions, 68 deletions
diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 99138edfe4..81a30b73c8 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -20,6 +20,24 @@ #include "terminal-util.h" #include "user-util.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, InhibitWhat inhibit_key, @@ -38,21 +56,11 @@ int manager_handle_action( [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...", }; - 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, - }; - _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); @@ -84,7 +92,6 @@ int manager_handle_action( /* Locking is handled differently from the rest. */ if (handle == HANDLE_LOCK) { - if (!is_edge) return 0; @@ -116,6 +123,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; @@ -147,7 +156,7 @@ int manager_handle_action( log_info("%s", message_table[handle]); - 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); if (r < 0) { log_error("Failed to execute operation: %s", bus_error_message(&error, r)); return r; @@ -166,7 +175,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 8c7de83e8f..70439d767c 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -35,4 +35,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 27e6b9ddf9..563c7fef9f 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -16,6 +16,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" @@ -1692,6 +1693,7 @@ int bus_manager_shutdown_or_sleep_now_or_later( InhibitWhat w, sd_bus_error *error) { + _cleanup_free_ char *load_state = NULL; bool delayed; int r; @@ -1701,6 +1703,15 @@ int bus_manager_shutdown_or_sleep_now_or_later( 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; + } + /* Tell everybody to prepare for shutdown/sleep */ (void) send_prepare_for(m, w, true); @@ -1811,11 +1822,14 @@ static int method_do_shutdown_or_sleep( if (sleep_verb) { r = can_sleep(sleep_verb); + if (r == -ENOSPC) + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "Not enough swap space for hibernation"); + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "Sleep verb \"%s\" not supported", sleep_verb); if (r < 0) return r; - - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); } r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, @@ -2222,6 +2236,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; @@ -2237,10 +2252,10 @@ static int method_can_shutdown_or_sleep( if (sleep_verb) { r = can_sleep(sleep_verb); + if (IN_SET(r, 0, -ENOSPC)) + return sd_bus_reply_method_return(message, "s", "na"); if (r < 0) return r; - if (r == 0) - return sd_bus_reply_method_return(message, "s", "na"); } r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); @@ -2258,6 +2273,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) @@ -2300,6 +2334,7 @@ static int method_can_shutdown_or_sleep( result = "no"; } + finish: return sd_bus_reply_method_return(message, "s", result); } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 8000a23708..216140b248 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2449,3 +2449,29 @@ finish: return r; } + +int unit_load_state(sd_bus *bus, const char *name, char **load_state) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *path = NULL; + int r; + + path = unit_dbus_path_from_name(name); + if (!path) + return log_oom(); + + /* This function warns on it's own, because otherwise it'd be awkward to pass + * the dbus error message around. */ + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "LoadState", + &error, + load_state); + if (r < 0) + return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r)); + + return 0; +} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index f821e88ed9..e243b0c726 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -44,3 +44,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); + +int unit_load_state(sd_bus *bus, const char *name, char **load_state); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 36677f6f58..fba58ef367 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -226,7 +226,7 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us return -ENOSYS; } -static bool enough_memory_for_hibernation(void) { +static bool enough_swap_for_hibernation(void) { _cleanup_free_ char *active = NULL; unsigned long long act = 0; size_t size = 0, used = 0; @@ -261,11 +261,11 @@ static bool enough_memory_for_hibernation(void) { int read_fiemap(int fd, struct fiemap **ret) { _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL; - int extents_size; struct stat statinfo; uint32_t result_extents = 0; uint64_t fiemap_start = 0, fiemap_length; - size_t fiemap_size = 1, result_fiemap_size = 1; + const size_t n_extra = DIV_ROUND_UP(sizeof(struct fiemap), sizeof(struct fiemap_extent)); + size_t fiemap_allocated = n_extra, result_fiemap_allocated = n_extra; if (fstat(fd, &statinfo) < 0) return log_debug_errno(errno, "Cannot determine file size: %m"); @@ -273,12 +273,12 @@ int read_fiemap(int fd, struct fiemap **ret) { return -ENOTTY; fiemap_length = statinfo.st_size; - /* zero this out in case we run on a file with no extents */ - fiemap = new0(struct fiemap, 1); + /* Zero this out in case we run on a file with no extents */ + fiemap = calloc(n_extra, sizeof(struct fiemap_extent)); if (!fiemap) return -ENOMEM; - result_fiemap = new(struct fiemap, 1); + result_fiemap = malloc_multiply(n_extra, sizeof(struct fiemap_extent)); if (!result_fiemap) return -ENOMEM; @@ -302,12 +302,10 @@ int read_fiemap(int fd, struct fiemap **ret) { if (fiemap->fm_mapped_extents == 0) break; - /* Result fiemap has to hold all the extents for the whole file */ - extents_size = DIV_ROUND_UP(sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents, - sizeof(struct fiemap)); - - /* Resize fiemap to allow us to read in the extents */ - if (!GREEDY_REALLOC0(fiemap, fiemap_size, extents_size)) + /* Resize fiemap to allow us to read in the extents, result fiemap has to hold all + * the extents for the whole file. Add space for the initial struct fiemap. */ + if (!greedy_realloc0((void**) &fiemap, &fiemap_allocated, + n_extra + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent))) return -ENOMEM; fiemap->fm_extent_count = fiemap->fm_mapped_extents; @@ -316,12 +314,9 @@ int read_fiemap(int fd, struct fiemap **ret) { if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) return log_debug_errno(errno, "Failed to read extents: %m"); - extents_size = DIV_ROUND_UP(sizeof(struct fiemap_extent) * (result_extents + fiemap->fm_mapped_extents), - sizeof(struct fiemap)); - - /* Resize result_fiemap to allow us to read in the extents */ - if (!GREEDY_REALLOC(result_fiemap, result_fiemap_size, - extents_size)) + /* Resize result_fiemap to allow us to copy in the extents */ + if (!greedy_realloc((void**) &result_fiemap, &result_fiemap_allocated, + n_extra + result_extents + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent))) return -ENOMEM; memcpy(result_fiemap->fm_extents + result_extents, @@ -331,7 +326,7 @@ int read_fiemap(int fd, struct fiemap **ret) { result_extents += fiemap->fm_mapped_extents; /* Highly unlikely that it is zero */ - if (fiemap->fm_mapped_extents > 0) { + if (_likely_(fiemap->fm_mapped_extents > 0)) { uint32_t i = fiemap->fm_mapped_extents - 1; fiemap_start = fiemap->fm_extents[i].fe_logical + @@ -349,6 +344,7 @@ int read_fiemap(int fd, struct fiemap **ret) { } static bool can_s2h(void) { + const char *p; int r; r = access("/sys/class/rtc/rtc0/wakealarm", W_OK); @@ -358,16 +354,14 @@ static bool can_s2h(void) { return false; } - r = can_sleep("suspend"); - if (r < 0) { - log_debug_errno(r, "Unable to suspend system."); - return false; - } - - r = can_sleep("hibernate"); - if (r < 0) { - log_debug_errno(r, "Unable to hibernate system."); - return false; + FOREACH_STRING(p, "suspend", "hibernate") { + r = can_sleep(p); + if (IN_SET(r, 0, -ENOSPC)) { + log_debug("Unable to %s system.", p); + return false; + } + if (r < 0) + return log_debug_errno(r, "Failed to check if %s is possible: %m", p); } return true; @@ -389,5 +383,11 @@ int can_sleep(const char *verb) { if (!can_sleep_state(states) || !can_sleep_disk(modes)) return false; - return streq(verb, "suspend") || enough_memory_for_hibernation(); + if (streq(verb, "suspend")) + return true; + + if (!enough_swap_for_hibernation()) + return -ENOSPC; + + return true; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index fc42fdce02..079d571857 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2636,24 +2636,12 @@ static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *ac } static int unit_is_masked(sd_bus *bus, const char *name) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *path = NULL, *load_state = NULL; + _cleanup_free_ char *load_state = NULL; int r; - path = unit_dbus_path_from_name(name); - if (!path) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "LoadState", - &error, - &load_state); + r = unit_load_state(bus, name, &load_state); if (r < 0) - return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r)); + return r; return streq(load_state, "masked"); } diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index f52aea447d..c2cb4ef949 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -52,7 +52,9 @@ static void test_sleep(void) { **platform = strv_new("platform", NULL), **shutdown = strv_new("shutdown", NULL), **freez = strv_new("freeze", NULL); + int r; + log_info("/* configuration */"); log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0)); log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0)); log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0)); @@ -62,10 +64,15 @@ static void test_sleep(void) { log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0)); log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0)); - log_info("Suspend configured and possible: %s", yes_no(can_sleep("suspend") > 0)); - log_info("Hibernation configured and possible: %s", yes_no(can_sleep("hibernate") > 0)); - log_info("Hybrid-sleep configured and possible: %s", yes_no(can_sleep("hybrid-sleep") > 0)); - log_info("Suspend-then-Hibernate configured and possible: %s", yes_no(can_sleep("suspend-then-hibernate") > 0)); + log_info("/* running system */"); + r = can_sleep("suspend"); + log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r)); + r = can_sleep("hibernate"); + log_info("Hibernation configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r)); + r = can_sleep("hybrid-sleep"); + log_info("Hybrid-sleep configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r)); + r = can_sleep("suspend-then-hibernate"); + log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r)); } int main(int argc, char* argv[]) { |