chiark / gitweb /
tree-wide: drop license boilerplate
[elogind.git] / src / sleep / sleep.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2012 Lennart Poettering
6   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek
7   Copyright 2018 Dell Inc.
8 ***/
9
10 //#include <errno.h>
11 //#include <getopt.h>
12 //#include <stdio.h>
13
14 #include "sd-messages.h"
15
16 //#include "parse-util.h"
17 #include "def.h"
18 #include "exec-util.h"
19 #include "fd-util.h"
20 #include "fileio.h"
21 //#include "log.h"
22 //#include "sleep-config.h"
23 //#include "stdio-util.h"
24 //#include "string-util.h"
25 #include "strv.h"
26 //#include "util.h"
27
28 /// Additional includes needed by elogind
29 #include "sleep.h"
30
31 static char* arg_verb = NULL;
32
33 static int write_mode(char **modes) {
34         int r = 0;
35         char **mode;
36
37         STRV_FOREACH(mode, modes) {
38                 int k;
39
40                 k = write_string_file("/sys/power/disk", *mode, 0);
41                 if (k == 0)
42                         return 0;
43
44                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
45                                 *mode);
46                 if (r == 0)
47                         r = k;
48         }
49
50         if (r < 0)
51                 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
52
53         return r;
54 }
55
56 static int write_state(FILE **f, char **states) {
57         char **state;
58         int r = 0;
59
60         STRV_FOREACH(state, states) {
61                 int k;
62
63                 k = write_string_stream(*f, *state, 0);
64                 if (k == 0)
65                         return 0;
66                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
67                                 *state);
68                 if (r == 0)
69                         r = k;
70
71                 fclose(*f);
72                 *f = fopen("/sys/power/state", "we");
73                 if (!*f)
74                         return log_error_errno(errno, "Failed to open /sys/power/state: %m");
75         }
76
77         return r;
78 }
79
80 static int execute(char **modes, char **states) {
81
82         char *arguments[] = {
83                 NULL,
84                 (char*) "pre",
85                 arg_verb,
86                 NULL
87         };
88         static const char* const dirs[] = {
89                 SYSTEM_SLEEP_PATH,
90                 NULL
91         };
92
93         int r;
94         _cleanup_fclose_ FILE *f = NULL;
95
96         /* This file is opened first, so that if we hit an error,
97          * we can abort before modifying any state. */
98         f = fopen("/sys/power/state", "we");
99         if (!f)
100                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
101
102         /* Configure the hibernation mode */
103         r = write_mode(modes);
104         if (r < 0)
105                 return r;
106
107         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
108
109         log_struct(LOG_INFO,
110                    "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
111                    LOG_MESSAGE("Suspending system..."),
112                    "SLEEP=%s", arg_verb,
113                    NULL);
114
115         r = write_state(&f, states);
116         if (r < 0)
117                 return r;
118
119         log_struct(LOG_INFO,
120                    "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
121                    LOG_MESSAGE("System resumed."),
122                    "SLEEP=%s", arg_verb,
123                    NULL);
124
125         arguments[1] = (char*) "post";
126         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
127
128         return r;
129 }
130
131 static int read_wakealarm(uint64_t *result) {
132         _cleanup_free_ char *t = NULL;
133
134         if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0)
135                 return safe_atou64(t, result);
136         return -EBADF;
137 }
138
139 static int write_wakealarm(const char *str) {
140
141         _cleanup_fclose_ FILE *f = NULL;
142         int r;
143
144         f = fopen("/sys/class/rtc/rtc0/wakealarm", "we");
145         if (!f)
146                 return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
147
148         r = write_string_stream(f, str, 0);
149         if (r < 0)
150                 return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str);
151
152         return 0;
153 }
154
155 static int execute_s2h(usec_t hibernate_delay_sec) {
156
157         _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL,
158                                  **suspend_modes = NULL, **suspend_states = NULL;
159         usec_t orig_time, cmp_time;
160         char time_str[DECIMAL_STR_MAX(uint64_t)];
161         int r;
162
163         r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
164                                NULL);
165         if (r < 0)
166                 return r;
167
168         r = parse_sleep_config("hibernate", &hibernate_modes,
169                                &hibernate_states, NULL);
170         if (r < 0)
171                 return r;
172
173         r = read_wakealarm(&orig_time);
174         if (r < 0)
175                 return log_error_errno(errno, "Failed to read time: %d", r);
176
177         orig_time += hibernate_delay_sec / USEC_PER_SEC;
178         xsprintf(time_str, "%" PRIu64, orig_time);
179
180         r = write_wakealarm(time_str);
181         if (r < 0)
182                 return r;
183
184         log_debug("Set RTC wake alarm for %s", time_str);
185
186         r = execute(suspend_modes, suspend_states);
187         if (r < 0)
188                 return r;
189
190         r = read_wakealarm(&cmp_time);
191         if (r < 0)
192                 return log_error_errno(errno, "Failed to read time: %d", r);
193
194         /* reset RTC */
195         r = write_wakealarm("0");
196         if (r < 0)
197                 return r;
198
199         log_debug("Woke up at %"PRIu64, cmp_time);
200
201         /* if woken up after alarm time, hibernate */
202         if (cmp_time >= orig_time)
203                 r = execute(hibernate_modes, hibernate_states);
204
205         return r;
206 }
207
208 #if 0 /// elogind calls execute() by itself and does not need another binary
209 static void help(void) {
210         printf("%s COMMAND\n\n"
211                "Suspend the system, hibernate the system, or both.\n\n"
212                "Commands:\n"
213                "  -h --help            Show this help and exit\n"
214                "  --version            Print version string and exit\n"
215                "  suspend              Suspend the system\n"
216                "  hibernate            Hibernate the system\n"
217                "  hybrid-sleep         Both hibernate and suspend the system\n"
218                "  suspend-then-hibernate Initially suspend and then hibernate\n"
219                "                       the system after a fixed period of time\n"
220                , program_invocation_short_name);
221 }
222
223 static int parse_argv(int argc, char *argv[]) {
224         enum {
225                 ARG_VERSION = 0x100,
226         };
227
228         static const struct option options[] = {
229                 { "help",         no_argument,       NULL, 'h'           },
230                 { "version",      no_argument,       NULL, ARG_VERSION   },
231                 {}
232         };
233
234         int c;
235
236         assert(argc >= 0);
237         assert(argv);
238
239         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
240                 switch(c) {
241                 case 'h':
242                         help();
243                         return 0; /* done */
244
245                 case ARG_VERSION:
246                         return version();
247
248                 case '?':
249                         return -EINVAL;
250
251                 default:
252                         assert_not_reached("Unhandled option");
253                 }
254
255         if (argc - optind != 1) {
256                 log_error("Usage: %s COMMAND",
257                           program_invocation_short_name);
258                 return -EINVAL;
259         }
260
261         arg_verb = argv[optind];
262
263         if (!streq(arg_verb, "suspend") &&
264             !streq(arg_verb, "hibernate") &&
265             !streq(arg_verb, "hybrid-sleep") &&
266             !streq(arg_verb, "suspend-then-hibernate")) {
267                 log_error("Unknown command '%s'.", arg_verb);
268                 return -EINVAL;
269         }
270
271         return 1 /* work to do */;
272 }
273
274 int main(int argc, char *argv[]) {
275         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
276         usec_t delay = 0;
277         int r;
278
279         log_set_target(LOG_TARGET_AUTO);
280         log_parse_environment();
281         log_open();
282
283         r = parse_argv(argc, argv);
284         if (r <= 0)
285                 goto finish;
286
287         r = parse_sleep_config(arg_verb, &modes, &states, &delay);
288         if (r < 0)
289                 goto finish;
290
291         if (streq(arg_verb, "suspend-then-hibernate"))
292                 r = execute_s2h(delay);
293         else
294                 r = execute(modes, states);
295 finish:
296         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
297 }
298 #else
299 int do_sleep(const char *verb, char **modes, char **states) {
300         assert(verb);
301         arg_verb = (char*)verb;
302         return execute(modes, states);
303 }
304 #endif // 0