chiark / gitweb /
systemd-sleep: add support for freeze and standby
[elogind.git] / src / shared / sleep-config.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <stdio.h>
23
24 #include "conf-parser.h"
25 #include "sleep-config.h"
26 #include "fileio.h"
27 #include "log.h"
28 #include "strv.h"
29 #include "util.h"
30
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;
36
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 },
44                 {}};
45
46         int r;
47         FILE _cleanup_fclose_ *f;
48
49         f = fopen(PKGSYSCONFDIR "/sleep.conf", "re");
50         if (!f) {
51                 if (errno == ENOENT)
52                         return 0;
53
54                 log_warning("Failed to open configuration file " PKGSYSCONFDIR "/sleep.conf: %m");
55                 return 0;
56         }
57
58         r = config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", f, "Sleep\0",
59                          config_item_table_lookup, (void*) items, false, false, NULL);
60         if (r < 0)
61                 log_warning("Failed to parse configuration file: %s", strerror(-r));
62
63         if (streq(verb, "suspend")) {
64                 /* empty by default */
65                 *modes = suspend_mode;
66
67                 if (suspend_state)
68                         *states = suspend_state;
69                 else
70                         *states = strv_split_nulstr("mem\0standby\0freeze\0");
71
72                 suspend_mode = suspend_state = NULL;
73         } else if (streq(verb, "hibernate")) {
74                 if (hibernate_mode)
75                         *modes = hibernate_mode;
76                 else
77                         *modes = strv_split_nulstr("platform\0shutdown\0");
78
79                 if (hibernate_state)
80                         *states = hibernate_state;
81                 else
82                         *states = strv_split_nulstr("disk\0");
83
84                 hibernate_mode = hibernate_state = NULL;
85         } else if (streq(verb, "hybrid-sleep")) {
86                 if (hybrid_mode)
87                         *modes = hybrid_mode;
88                 else
89                         *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
90
91                 if (hybrid_state)
92                         *states = hybrid_state;
93                 else
94                         *states = strv_split_nulstr("disk\0");
95
96                 hybrid_mode = hybrid_state = NULL;
97         } else
98                 assert_not_reached("what verb");
99
100         if (!modes || !states) {
101                 strv_free(*modes);
102                 strv_free(*states);
103                 return log_oom();
104         }
105
106         return 0;
107 }
108
109 int can_sleep_state(char **types) {
110         char *w, *state, **type;
111         int r;
112         _cleanup_free_ char *p = NULL;
113
114         if (strv_isempty(types))
115                 return true;
116
117         /* If /sys is read-only we cannot sleep */
118         if (access("/sys/power/state", W_OK) < 0)
119                 return false;
120
121         r = read_one_line_file("/sys/power/state", &p);
122         if (r < 0)
123                 return false;
124
125         STRV_FOREACH(type, types) {
126                 size_t l, k;
127
128                 k = strlen(*type);
129                 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
130                         if (l == k && memcmp(w, *type, l) == 0)
131                                 return true;
132         }
133
134         return false;
135 }
136
137 int can_sleep_disk(char **types) {
138         char *w, *state, **type;
139         int r;
140         _cleanup_free_ char *p = NULL;
141
142         if (strv_isempty(types))
143                 return true;
144
145         /* If /sys is read-only we cannot sleep */
146         if (access("/sys/power/disk", W_OK) < 0)
147                 return false;
148
149         r = read_one_line_file("/sys/power/disk", &p);
150         if (r < 0)
151                 return false;
152
153         STRV_FOREACH(type, types) {
154                 size_t l, k;
155
156                 k = strlen(*type);
157                 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
158                         if (l == k && memcmp(w, *type, l) == 0)
159                                 return true;
160
161                         if (l == k + 2 && w[0] == '[' && memcmp(w + 1, *type, l - 2) == 0 && w[l-1] == ']')
162                                 return true;
163                 }
164         }
165
166         return false;
167 }
168
169 int can_sleep(const char *verb) {
170         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
171         int r;
172
173         assert(streq(verb, "suspend") ||
174                streq(verb, "hibernate") ||
175                streq(verb, "hybrid-sleep"));
176
177         r = parse_sleep_config(verb, &modes, &states);
178         if (r < 0)
179                 return false;
180
181         return can_sleep_state(states) && can_sleep_disk(modes);
182 }