2 This file is part of systemd.
4 Copyright 2013 Zbigniew Jędrzejewski-Szmek
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "sd-messages.h"
30 //#include "alloc-util.h"
31 //#include "conf-parser.h"
36 #include "logind-sleep.h"
38 #include "parse-util.h"
39 #include "string-util.h"
42 static int can_sleep_state(char **types) {
45 _cleanup_free_ char *p = NULL;
47 if (strv_isempty(types))
50 /* If /sys is read-only we cannot sleep */
51 if (access("/sys/power/state", W_OK) < 0)
54 r = read_one_line_file("/sys/power/state", &p);
58 STRV_FOREACH(type, types) {
59 const char *word, *state;
63 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
64 if (l == k && memcmp(word, *type, l) == 0)
71 static int can_sleep_disk(char **types) {
74 _cleanup_free_ char *p = NULL;
76 if (strv_isempty(types))
79 /* If /sys is read-only we cannot sleep */
80 if (access("/sys/power/disk", W_OK) < 0)
83 r = read_one_line_file("/sys/power/disk", &p);
87 STRV_FOREACH(type, types) {
88 const char *word, *state;
92 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
93 if (l == k && memcmp(word, *type, l) == 0)
98 memcmp(word + 1, *type, l - 2) == 0 &&
107 #define HIBERNATION_SWAP_THRESHOLD 0.98
109 static int hibernation_partition_size(size_t *size, size_t *used) {
110 _cleanup_fclose_ FILE *f;
116 f = fopen("/proc/swaps", "re");
118 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
119 "Failed to retrieve open /proc/swaps: %m");
124 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
127 _cleanup_free_ char *dev = NULL, *type = NULL;
128 size_t size_field, used_field;
132 "%ms " /* device/file */
133 "%ms " /* type of swap */
134 "%zu " /* swap size */
136 "%*i\n", /* priority */
137 &dev, &type, &size_field, &used_field);
142 log_warning("Failed to parse /proc/swaps:%u", i);
146 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
147 log_warning("Ignoring deleted swapfile '%s'.", dev);
156 log_debug("No swap partitions were found.");
160 static bool enough_memory_for_hibernation(void) {
161 _cleanup_free_ char *active = NULL;
162 unsigned long long act = 0;
163 size_t size = 0, used = 0;
166 r = hibernation_partition_size(&size, &used);
170 r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
172 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
176 r = safe_atollu(active, &act);
178 log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
183 r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
184 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
185 r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
190 int can_sleep(Manager *m, const char *verb) {
192 assert(streq(verb, "suspend") ||
193 streq(verb, "hibernate") ||
194 streq(verb, "hybrid-sleep"));
196 if ( streq(verb, "suspend")
197 && ( !can_sleep_state(m->suspend_state)
198 || !can_sleep_disk(m->suspend_mode) ) )
201 if ( streq(verb, "hibernate")
202 && ( !can_sleep_state(m->hibernate_state)
203 || !can_sleep_disk(m->hibernate_mode) ) )
206 if ( streq(verb, "hybrid-sleep")
207 && ( !can_sleep_state(m->hybrid_sleep_state)
208 || !can_sleep_disk(m->hybrid_sleep_mode) ) )
212 return streq(verb, "suspend") || enough_memory_for_hibernation();
215 static int write_mode(char **modes) {
219 STRV_FOREACH(mode, modes) {
222 k = write_string_file("/sys/power/disk", *mode, 0);
226 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
233 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
238 static int write_state(FILE **f, char **states) {
242 STRV_FOREACH(state, states) {
245 k = write_string_stream(*f, *state, true);
248 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
254 *f = fopen("/sys/power/state", "we");
256 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
262 int do_sleep(const char *arg_verb, char **modes, char **states) {
264 char *arguments[] = {
270 static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL};
273 _cleanup_fclose_ FILE *f = NULL;
275 /* This file is opened first, so that if we hit an error,
276 * we can abort before modifying any state. */
277 f = fopen("/sys/power/state", "we");
279 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
281 /* Configure the hibernation mode */
282 r = write_mode(modes);
286 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
289 LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
290 LOG_MESSAGE("Suspending system..."),
291 "SLEEP=%s", arg_verb,
294 r = write_state(&f, states);
299 LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
300 LOG_MESSAGE("System resumed."),
301 "SLEEP=%s", arg_verb,
304 arguments[1] = (char*) "post";
305 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);