From c2c096d2926572113cba380fde34b119661d45f8 Mon Sep 17 00:00:00 2001 From: Sven Eden Date: Wed, 14 Jun 2017 17:23:00 +0200 Subject: [PATCH] Prep v231: Reorganize elogind specific code in login/logind-action.c - Move elogind specific code in login/logind-action.c to login/elogind-action.c - Remove login/logind-sleep.* - Add src/sleep from upstream - Integrate the systemd-sleep utility, so future fixes and updates will be easier to spot and to apply. --- src/.gitignore | 1 - src/login/elogind-action.c | 69 +++++ src/login/elogind-action.h | 30 ++ src/login/logind-action.c | 47 +-- src/login/logind-action.h | 5 +- src/login/logind-sleep.c | 309 -------------------- src/sleep/Makefile | 1 + src/sleep/sleep.c | 229 +++++++++++++++ src/{login/logind-sleep.h => sleep/sleep.h} | 17 +- 9 files changed, 343 insertions(+), 365 deletions(-) create mode 100644 src/login/elogind-action.c create mode 100644 src/login/elogind-action.h delete mode 100644 src/login/logind-sleep.c create mode 120000 src/sleep/Makefile create mode 100644 src/sleep/sleep.c rename src/{login/logind-sleep.h => sleep/sleep.h} (51%) diff --git a/src/.gitignore b/src/.gitignore index fce60d3ce..bea726814 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -31,7 +31,6 @@ org.freedesktop.systemd1.policy /journal /socket-proxy /test -/sleep /cgtop /locale /cryptsetup diff --git a/src/login/elogind-action.c b/src/login/elogind-action.c new file mode 100644 index 000000000..17e1dd22d --- /dev/null +++ b/src/login/elogind-action.c @@ -0,0 +1,69 @@ +/*** + This file is part of elogind. + + Copyright 2017 Sven Eden + + 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 "elogind-action.h" +#include "fd-util.h" +#include "process-util.h" +#include "sleep.h" +#include "sleep-config.h" + + +static int run_helper(const char *helper) { + int pid = fork(); + if (pid < 0) { + return log_error_errno(errno, "Failed to fork: %m"); + } + + if (pid == 0) { + /* Child */ + + close_all_fds(NULL, 0); + + execlp(helper, helper, NULL); + log_error_errno(errno, "Failed to execute %s: %m", helper); + _exit(EXIT_FAILURE); + } + + return wait_for_terminate_and_warn(helper, pid, true); +} + +int shutdown_or_sleep(Manager *m, HandleAction action) { + + assert(m); + + switch (action) { + case HANDLE_POWEROFF: + return run_helper(HALT); + case HANDLE_REBOOT: + return run_helper(REBOOT); + case HANDLE_HALT: + return run_helper(HALT); + case HANDLE_KEXEC: + return run_helper(KEXEC); + case HANDLE_SUSPEND: + return do_sleep("suspend", m->suspend_mode, m->suspend_state); + case HANDLE_HIBERNATE: + return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state); + case HANDLE_HYBRID_SLEEP: + return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state); + default: + return -EINVAL; + } +} diff --git a/src/login/elogind-action.h b/src/login/elogind-action.h new file mode 100644 index 000000000..8df7aad53 --- /dev/null +++ b/src/login/elogind-action.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef ELOGIND_SRC_LOGIN_ELOGIND_ACTION_H_INCLUDED +#define ELOGIND_SRC_LOGIN_ELOGIND_ACTION_H_INCLUDED + +/*** + This file is part of elogind. + + Copyright 2017 Sven Eden + + 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 "logind.h" + + +int shutdown_or_sleep(Manager *m, HandleAction action); + + +#endif // ELOGIND_SRC_LOGIN_ELOGIND_ACTION_H_INCLUDED diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 5b4a247b5..d50f6ebf6 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -26,13 +26,13 @@ #include "formats-util.h" #include "logind-action.h" #include "process-util.h" -//#include "sleep-config.h" +#include "sleep-config.h" //#include "special.h" #include "string-table.h" #include "terminal-util.h" #include "user-util.h" -// Additional includes needed by elogind +/// Additional includes needed by elogind #include "fd-util.h" #include "fileio.h" #include "sd-messages.h" @@ -175,49 +175,6 @@ int manager_handle_action( return 1; } -static int run_helper(const char *helper) { - int pid = fork(); - if (pid < 0) { - return log_error_errno(errno, "Failed to fork: %m"); - } - - if (pid == 0) { - /* Child */ - - close_all_fds(NULL, 0); - - execlp(helper, helper, NULL); - log_error_errno(errno, "Failed to execute %s: %m", helper); - _exit(EXIT_FAILURE); - } - - return wait_for_terminate_and_warn(helper, pid, true); -} - -int shutdown_or_sleep(Manager *m, HandleAction action) { - - assert(m); - - switch (action) { - case HANDLE_POWEROFF: - return run_helper(HALT); - case HANDLE_REBOOT: - return run_helper(REBOOT); - case HANDLE_HALT: - return run_helper(HALT); - case HANDLE_KEXEC: - return run_helper(KEXEC); - case HANDLE_SUSPEND: - return do_sleep("suspend", m->suspend_mode, m->suspend_state); - case HANDLE_HIBERNATE: - return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state); - case HANDLE_HYBRID_SLEEP: - return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state); - default: - return -EINVAL; - } -} - static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { [HANDLE_IGNORE] = "ignore", [HANDLE_POWEROFF] = "poweroff", diff --git a/src/login/logind-action.h b/src/login/logind-action.h index 33c302112..d33b9de44 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -36,6 +36,9 @@ typedef enum HandleAction { #include "logind-inhibit.h" #include "logind.h" +/// Additional includes needed by elogind +#include "elogind-action.h" + int manager_handle_action( Manager *m, InhibitWhat inhibit_key, @@ -43,8 +46,6 @@ int manager_handle_action( bool ignore_inhibited, bool is_edge); -int shutdown_or_sleep(Manager *m, HandleAction action); - const char* handle_action_to_string(HandleAction h) _const_; HandleAction handle_action_from_string(const char *s) _pure_; diff --git a/src/login/logind-sleep.c b/src/login/logind-sleep.c deleted file mode 100644 index cd6691ace..000000000 --- a/src/login/logind-sleep.c +++ /dev/null @@ -1,309 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd 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. - - systemd 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 systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -//#include -//#include - -#include "sd-messages.h" - -//#include "alloc-util.h" -//#include "conf-parser.h" -//#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "logind-sleep.h" -//#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" - -static int can_sleep_state(char **types) { - char **type; - int r; - _cleanup_free_ char *p = NULL; - - if (strv_isempty(types)) - return true; - - /* If /sys is read-only we cannot sleep */ - if (access("/sys/power/state", W_OK) < 0) - return false; - - r = read_one_line_file("/sys/power/state", &p); - if (r < 0) - return false; - - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; - - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) - if (l == k && memcmp(word, *type, l) == 0) - return true; - } - - return false; -} - -static int can_sleep_disk(char **types) { - char **type; - int r; - _cleanup_free_ char *p = NULL; - - if (strv_isempty(types)) - return true; - - /* If /sys is read-only we cannot sleep */ - if (access("/sys/power/disk", W_OK) < 0) - return false; - - r = read_one_line_file("/sys/power/disk", &p); - if (r < 0) - return false; - - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; - - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) { - if (l == k && memcmp(word, *type, l) == 0) - return true; - - if (l == k + 2 && - word[0] == '[' && - memcmp(word + 1, *type, l - 2) == 0 && - word[l-1] == ']') - return true; - } - } - - return false; -} - -#define HIBERNATION_SWAP_THRESHOLD 0.98 - -static int hibernation_partition_size(size_t *size, size_t *used) { - _cleanup_fclose_ FILE *f; - unsigned i; - - assert(size); - assert(used); - - f = fopen("/proc/swaps", "re"); - if (!f) { - log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "Failed to retrieve open /proc/swaps: %m"); - assert(errno > 0); - return -errno; - } - - (void) fscanf(f, "%*s %*s %*s %*s %*s\n"); - - for (i = 1;; i++) { - _cleanup_free_ char *dev = NULL, *type = NULL; - size_t size_field, used_field; - int k; - - k = fscanf(f, - "%ms " /* device/file */ - "%ms " /* type of swap */ - "%zu " /* swap size */ - "%zu " /* used */ - "%*i\n", /* priority */ - &dev, &type, &size_field, &used_field); - if (k != 4) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u", i); - continue; - } - - if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) { - log_warning("Ignoring deleted swapfile '%s'.", dev); - continue; - } - - *size = size_field; - *used = used_field; - return 0; - } - - log_debug("No swap partitions were found."); - return -ENOSYS; -} - -static bool enough_memory_for_hibernation(void) { - _cleanup_free_ char *active = NULL; - unsigned long long act = 0; - size_t size = 0, used = 0; - int r; - - r = hibernation_partition_size(&size, &used); - if (r < 0) - return false; - - r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active); - if (r < 0) { - log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); - return false; - } - - r = safe_atollu(active, &act); - if (r < 0) { - log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", - active); - return false; - } - - r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; - log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", - r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); - - return r; -} - -int can_sleep(Manager *m, const char *verb) { - - assert(streq(verb, "suspend") || - streq(verb, "hibernate") || - streq(verb, "hybrid-sleep")); - - if ( streq(verb, "suspend") - && ( !can_sleep_state(m->suspend_state) - || !can_sleep_disk(m->suspend_mode) ) ) - return false; - - if ( streq(verb, "hibernate") - && ( !can_sleep_state(m->hibernate_state) - || !can_sleep_disk(m->hibernate_mode) ) ) - return false; - - if ( streq(verb, "hybrid-sleep") - && ( !can_sleep_state(m->hybrid_sleep_state) - || !can_sleep_disk(m->hybrid_sleep_mode) ) ) - return false; - - - return streq(verb, "suspend") || enough_memory_for_hibernation(); -} - -static int write_mode(char **modes) { - int r = 0; - char **mode; - - STRV_FOREACH(mode, modes) { - int k; - - k = write_string_file("/sys/power/disk", *mode, 0); - if (k == 0) - return 0; - - log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", - *mode); - if (r == 0) - r = k; - } - - if (r < 0) - log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); - - return r; -} - -static int write_state(FILE **f, char **states) { - char **state; - int r = 0; - - STRV_FOREACH(state, states) { - int k; - - k = write_string_stream(*f, *state, true); - if (k == 0) - return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", - *state); - if (r == 0) - r = k; - - fclose(*f); - *f = fopen("/sys/power/state", "we"); - if (!*f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - } - - return r; -} - -int do_sleep(const char *arg_verb, char **modes, char **states) { - - char *arguments[] = { - NULL, - (char*) "pre", - (char*) arg_verb, - NULL - }; - static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; - - int r; - _cleanup_fclose_ FILE *f = NULL; - - /* This file is opened first, so that if we hit an error, - * we can abort before modifying any state. */ - f = fopen("/sys/power/state", "we"); - if (!f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - - /* Configure the hibernation mode */ - r = write_mode(modes); - if (r < 0) - return r; - - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), - LOG_MESSAGE("Suspending system..."), - "SLEEP=%s", arg_verb, - NULL); - - r = write_state(&f, states); - if (r < 0) - return r; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), - LOG_MESSAGE("System resumed."), - "SLEEP=%s", arg_verb, - NULL); - - arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - return r; -} - diff --git a/src/sleep/Makefile b/src/sleep/Makefile new file mode 120000 index 000000000..d0b0e8e00 --- /dev/null +++ b/src/sleep/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c new file mode 100644 index 000000000..a2e8e6035 --- /dev/null +++ b/src/sleep/sleep.c @@ -0,0 +1,229 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +//#include +//#include +//#include + +#include "sd-messages.h" + +#include "def.h" +#include "fd-util.h" +#include "fileio.h" +//#include "log.h" +//#include "sleep-config.h" +//#include "string-util.h" +#include "strv.h" +//#include "util.h" + +/// Additional includes needed by elogind +#include "sleep.h" + +static char* arg_verb = NULL; + +static int write_mode(char **modes) { + int r = 0; + char **mode; + + STRV_FOREACH(mode, modes) { + int k; + + k = write_string_file("/sys/power/disk", *mode, 0); + if (k == 0) + return 0; + + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", + *mode); + if (r == 0) + r = k; + } + + if (r < 0) + log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); + + return r; +} + +static int write_state(FILE **f, char **states) { + char **state; + int r = 0; + + STRV_FOREACH(state, states) { + int k; + + k = write_string_stream(*f, *state, true); + if (k == 0) + return 0; + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", + *state); + if (r == 0) + r = k; + + fclose(*f); + *f = fopen("/sys/power/state", "we"); + if (!*f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + } + + return r; +} + +static int execute(char **modes, char **states) { + + char *arguments[] = { + NULL, + (char*) "pre", + arg_verb, + NULL + }; + static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; + + int r; + _cleanup_fclose_ FILE *f = NULL; + + /* This file is opened first, so that if we hit an error, + * we can abort before modifying any state. */ + f = fopen("/sys/power/state", "we"); + if (!f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + + /* Configure the hibernation mode */ + r = write_mode(modes); + if (r < 0) + return r; + + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + LOG_MESSAGE("Suspending system..."), + "SLEEP=%s", arg_verb, + NULL); + + r = write_state(&f, states); + if (r < 0) + return r; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb, + NULL); + + arguments[1] = (char*) "post"; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + return r; +} + +#if 0 /// elogind calls execute() by itself and does not need another binary +static void help(void) { + printf("%s COMMAND\n\n" + "Suspend the system, hibernate the system, or both.\n\n" + "Commands:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Both hibernate and suspend the system\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; /* done */ + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc - optind != 1) { + log_error("Usage: %s COMMAND", + program_invocation_short_name); + return -EINVAL; + } + + arg_verb = argv[optind]; + + if (!streq(arg_verb, "suspend") && + !streq(arg_verb, "hibernate") && + !streq(arg_verb, "hybrid-sleep")) { + log_error("Unknown command '%s'.", arg_verb); + return -EINVAL; + } + + return 1 /* work to do */; +} + +int main(int argc, char *argv[]) { + _cleanup_strv_free_ char **modes = NULL, **states = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = parse_sleep_config(arg_verb, &modes, &states); + if (r < 0) + goto finish; + + r = execute(modes, states); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} +#else +int do_sleep(const char *verb, char **modes, char **states) { + assert(verb); + assert(modes); + assert(states); + + arg_verb = (char*)verb; + return execute(modes, states); +} +#endif // 0 diff --git a/src/login/logind-sleep.h b/src/sleep/sleep.h similarity index 51% rename from src/login/logind-sleep.h rename to src/sleep/sleep.h index c493c68dd..85669fa07 100644 --- a/src/login/logind-sleep.h +++ b/src/sleep/sleep.h @@ -1,25 +1,26 @@ #pragma once +#ifndef ELOGIND_SRC_SLEEP_SLEEP_H_INCLUDED +#define ELOGIND_SRC_SLEEP_SLEEP_H_INCLUDED /*** - This file is part of systemd. + This file is part of elogind. - Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2017 Sven Eden - systemd is free software; you can redistribute it and/or modify it + 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. - systemd is distributed in the hope that it will be useful, but + 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 systemd; If not, see . + along with elogind; If not, see . ***/ -#include "logind.h" +int do_sleep(const char *verb, char **modes, char **states); -int can_sleep(Manager *m, const char *verb); -int do_sleep(const char *arg_verb, char **modes, char **states); +#endif // ELOGIND_SRC_SLEEP_SLEEP_H_INCLUDED -- 2.30.2