1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "conf-parser.h"
25 #include "sleep-config.h"
31 int parse_sleep_config(const char *verb, char ***modes, char ***states) {
32 _cleanup_strv_free_ char
33 **suspend_mode = NULL, **suspend_state = NULL,
34 **hibernate_mode = NULL, **hibernate_state = NULL,
35 **hybrid_mode = NULL, **hybrid_state = NULL;
37 const ConfigTableItem items[] = {
38 { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
39 { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
40 { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
41 { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
42 { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
43 { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
47 FILE _cleanup_fclose_ *f;
49 f = fopen(PKGSYSCONFDIR "/sleep.conf", "re");
51 log_full(errno == ENOENT ? LOG_DEBUG: LOG_WARNING,
52 "Failed to open configuration file " PKGSYSCONFDIR "/sleep.conf: %m");
54 r = config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", f, "Sleep\0",
55 config_item_table_lookup, (void*) items, false, false, NULL);
57 log_warning("Failed to parse configuration file: %s", strerror(-r));
60 if (streq(verb, "suspend")) {
61 /* empty by default */
62 *modes = suspend_mode;
65 *states = suspend_state;
67 *states = strv_split_nulstr("mem\0standby\0freeze\0");
69 suspend_mode = suspend_state = NULL;
70 } else if (streq(verb, "hibernate")) {
72 *modes = hibernate_mode;
74 *modes = strv_split_nulstr("platform\0shutdown\0");
77 *states = hibernate_state;
79 *states = strv_split_nulstr("disk\0");
81 hibernate_mode = hibernate_state = NULL;
82 } else if (streq(verb, "hybrid-sleep")) {
86 *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
89 *states = hybrid_state;
91 *states = strv_split_nulstr("disk\0");
93 hybrid_mode = hybrid_state = NULL;
95 assert_not_reached("what verb");
97 if (!modes || !states) {
106 int can_sleep_state(char **types) {
107 char *w, *state, **type;
109 _cleanup_free_ char *p = NULL;
111 if (strv_isempty(types))
114 /* If /sys is read-only we cannot sleep */
115 if (access("/sys/power/state", W_OK) < 0)
118 r = read_one_line_file("/sys/power/state", &p);
122 STRV_FOREACH(type, types) {
126 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
127 if (l == k && memcmp(w, *type, l) == 0)
134 int can_sleep_disk(char **types) {
135 char *w, *state, **type;
137 _cleanup_free_ char *p = NULL;
139 if (strv_isempty(types))
142 /* If /sys is read-only we cannot sleep */
143 if (access("/sys/power/disk", W_OK) < 0)
146 r = read_one_line_file("/sys/power/disk", &p);
150 STRV_FOREACH(type, types) {
154 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
155 if (l == k && memcmp(w, *type, l) == 0)
158 if (l == k + 2 && w[0] == '[' && memcmp(w + 1, *type, l - 2) == 0 && w[l-1] == ']')
166 #define HIBERNATION_SWAP_THRESHOLD 0.98
168 static bool enough_memory_for_hibernation(void) {
169 _cleanup_free_ char *active = NULL, *swapfree = NULL;
170 unsigned long long act, swap;
173 r = get_status_field("/proc/meminfo", "\nSwapFree:", &swapfree);
175 log_error("Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r));
179 r = safe_atollu(swapfree, &swap);
181 log_error("Failed to parse SwapFree from /proc/meminfo: %s: %s",
182 swapfree, strerror(-r));
186 r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
188 log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
192 r = safe_atollu(active, &act);
194 log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
195 active, strerror(-r));
199 r = act <= swap * HIBERNATION_SWAP_THRESHOLD;
200 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, SwapFree=%llu kB, threshold=%.2g%%",
201 r ? "" : "im", act, swap, 100*HIBERNATION_SWAP_THRESHOLD);
206 int can_sleep(const char *verb) {
207 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
210 assert(streq(verb, "suspend") ||
211 streq(verb, "hibernate") ||
212 streq(verb, "hybrid-sleep"));
214 r = parse_sleep_config(verb, &modes, &states);
218 if (!can_sleep_state(states) || !can_sleep_disk(modes))
221 return streq(verb, "suspend") || enough_memory_for_hibernation();