]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sleep/sleep.c
Merge pull request #27386 from dtardon/test-cleanup
[thirdparty/systemd.git] / src / sleep / sleep.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2010-2017 Canonical
4 Copyright © 2018 Dell Inc.
5 ***/
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <linux/fiemap.h>
11 #include <poll.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/timerfd.h>
15 #include <unistd.h>
16
17 #include "sd-bus.h"
18 #include "sd-messages.h"
19
20 #include "btrfs-util.h"
21 #include "build.h"
22 #include "bus-error.h"
23 #include "bus-locator.h"
24 #include "bus-util.h"
25 #include "constants.h"
26 #include "devnum-util.h"
27 #include "exec-util.h"
28 #include "fd-util.h"
29 #include "fileio.h"
30 #include "format-util.h"
31 #include "io-util.h"
32 #include "log.h"
33 #include "main-func.h"
34 #include "parse-util.h"
35 #include "pretty-print.h"
36 #include "sleep-config.h"
37 #include "special.h"
38 #include "stdio-util.h"
39 #include "string-util.h"
40 #include "strv.h"
41 #include "time-util.h"
42
43 #define DEFAULT_HIBERNATE_DELAY_USEC_NO_BATTERY (2 * USEC_PER_HOUR)
44
45 static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID;
46
47 static int write_hibernate_location_info(const HibernateLocation *hibernate_location) {
48 char offset_str[DECIMAL_STR_MAX(uint64_t)];
49 const char *resume_str;
50 int r;
51
52 assert(hibernate_location);
53 assert(hibernate_location->swap);
54
55 resume_str = FORMAT_DEVNUM(hibernate_location->devno);
56
57 r = write_string_file("/sys/power/resume", resume_str, WRITE_STRING_FILE_DISABLE_BUFFER);
58 if (r < 0)
59 return log_debug_errno(r, "Failed to write partition device to /sys/power/resume for '%s': '%s': %m",
60 hibernate_location->swap->device, resume_str);
61
62 log_debug("Wrote resume= value for %s to /sys/power/resume: %s", hibernate_location->swap->device, resume_str);
63
64 /* if it's a swap partition, we're done */
65 if (streq(hibernate_location->swap->type, "partition"))
66 return r;
67
68 if (!streq(hibernate_location->swap->type, "file"))
69 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
70 "Invalid hibernate type: %s", hibernate_location->swap->type);
71
72 /* Only available in 4.17+ */
73 if (hibernate_location->offset > 0 && access("/sys/power/resume_offset", W_OK) < 0) {
74 if (errno == ENOENT) {
75 log_debug("Kernel too old, can't configure resume_offset for %s, ignoring: %" PRIu64,
76 hibernate_location->swap->device, hibernate_location->offset);
77 return 0;
78 }
79
80 return log_debug_errno(errno, "/sys/power/resume_offset not writable: %m");
81 }
82
83 xsprintf(offset_str, "%" PRIu64, hibernate_location->offset);
84 r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
85 if (r < 0)
86 return log_debug_errno(r, "Failed to write swap file offset to /sys/power/resume_offset for '%s': '%s': %m",
87 hibernate_location->swap->device, offset_str);
88
89 log_debug("Wrote resume_offset= value for %s to /sys/power/resume_offset: %s", hibernate_location->swap->device, offset_str);
90
91 return 0;
92 }
93
94 static int write_mode(char **modes) {
95 int r = 0;
96
97 STRV_FOREACH(mode, modes) {
98 int k;
99
100 k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
101 if (k >= 0)
102 return 0;
103
104 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
105 if (r >= 0)
106 r = k;
107 }
108
109 return r;
110 }
111
112 static int write_state(FILE **f, char **states) {
113 int r = 0;
114
115 assert(f);
116 assert(*f);
117
118 STRV_FOREACH(state, states) {
119 int k;
120
121 k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER);
122 if (k >= 0)
123 return 0;
124 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state);
125 if (r >= 0)
126 r = k;
127
128 fclose(*f);
129 *f = fopen("/sys/power/state", "we");
130 if (!*f)
131 return -errno;
132 }
133
134 return r;
135 }
136
137 static int lock_all_homes(void) {
138 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
139 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
140 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
141 int r;
142
143 /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
144 * way the key material required to access these volumes is hopefully removed from memory. */
145
146 r = sd_bus_open_system(&bus);
147 if (r < 0)
148 return log_warning_errno(r, "Failed to connect to system bus, ignoring: %m");
149
150 r = sd_bus_message_new_method_call(
151 bus,
152 &m,
153 "org.freedesktop.home1",
154 "/org/freedesktop/home1",
155 "org.freedesktop.home1.Manager",
156 "LockAllHomes");
157 if (r < 0)
158 return bus_log_create_error(r);
159
160 /* If homed is not running it can't have any home directories active either. */
161 r = sd_bus_message_set_auto_start(m, false);
162 if (r < 0)
163 return log_error_errno(r, "Failed to disable auto-start of LockAllHomes() message: %m");
164
165 r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC, &error, NULL);
166 if (r < 0) {
167 if (!bus_error_is_unknown_service(&error))
168 return log_error_errno(r, "Failed to lock home directories: %s", bus_error_message(&error, r));
169
170 log_debug("systemd-homed is not running, locking of home directories skipped.");
171 } else
172 log_debug("Successfully requested locking of all home directories.");
173 return 0;
174 }
175
176 static int execute(
177 const SleepConfig *sleep_config,
178 SleepOperation operation,
179 const char *action) {
180
181 char *arguments[] = {
182 NULL,
183 (char*) "pre",
184 /* NB: we use 'arg_operation' instead of 'operation' here, as we want to communicate the overall
185 * operation here, not the specific one, in case of s2h. */
186 (char*) sleep_operation_to_string(arg_operation),
187 NULL
188 };
189 static const char* const dirs[] = {
190 SYSTEM_SLEEP_PATH,
191 NULL
192 };
193
194 _cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
195 _cleanup_fclose_ FILE *f = NULL;
196 char **modes, **states;
197 int r;
198
199 assert(sleep_config);
200 assert(operation >= 0);
201 assert(operation < _SLEEP_OPERATION_MAX);
202 assert(operation != SLEEP_SUSPEND_THEN_HIBERNATE); /* Handled by execute_s2h() instead */
203
204 states = sleep_config->states[operation];
205 modes = sleep_config->modes[operation];
206
207 if (strv_isempty(states))
208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
209 "No sleep states configured for sleep operation %s, can't sleep.",
210 sleep_operation_to_string(operation));
211
212 /* This file is opened first, so that if we hit an error,
213 * we can abort before modifying any state. */
214 f = fopen("/sys/power/state", "we");
215 if (!f)
216 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
217
218 setvbuf(f, NULL, _IONBF, 0);
219
220 /* Configure hibernation settings if we are supposed to hibernate */
221 if (!strv_isempty(modes)) {
222 r = find_hibernate_location(&hibernate_location);
223 if (r < 0)
224 return log_error_errno(r, "Failed to find location to hibernate to: %m");
225 if (r == 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
226 * do it ourselves then. > 0 means: kernel already had a configured hibernation
227 * location which we shouldn't touch. */
228 r = write_hibernate_location_info(hibernate_location);
229 if (r < 0)
230 return log_error_errno(r, "Failed to prepare for hibernation: %m");
231 }
232
233 r = write_mode(modes);
234 if (r < 0)
235 return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
236 }
237
238 /* Pass an action string to the call-outs. This is mostly our operation string, except if the
239 * hibernate step of s-t-h fails, in which case we communicate that with a separate action. */
240 if (!action)
241 action = sleep_operation_to_string(operation);
242
243 r = setenv("SYSTEMD_SLEEP_ACTION", action, 1);
244 if (r != 0)
245 log_warning_errno(errno, "Error setting SYSTEMD_SLEEP_ACTION=%s, ignoring: %m", action);
246
247 (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
248 (void) lock_all_homes();
249
250 log_struct(LOG_INFO,
251 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
252 LOG_MESSAGE("Entering sleep state '%s'...", sleep_operation_to_string(operation)),
253 "SLEEP=%s", sleep_operation_to_string(arg_operation));
254
255 r = write_state(&f, states);
256 if (r < 0)
257 log_struct_errno(LOG_ERR, r,
258 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
259 LOG_MESSAGE("Failed to put system to sleep. System resumed again: %m"),
260 "SLEEP=%s", sleep_operation_to_string(arg_operation));
261 else
262 log_struct(LOG_INFO,
263 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
264 LOG_MESSAGE("System returned from sleep state."),
265 "SLEEP=%s", sleep_operation_to_string(arg_operation));
266
267 arguments[1] = (char*) "post";
268 (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
269
270 return r;
271 }
272
273 static int custom_timer_suspend(const SleepConfig *sleep_config) {
274 usec_t hibernate_timestamp;
275 int r;
276
277 assert(sleep_config);
278
279 hibernate_timestamp = usec_add(now(CLOCK_BOOTTIME), sleep_config->hibernate_delay_usec);
280
281 while (battery_is_discharging_and_low() == 0) {
282 _cleanup_hashmap_free_ Hashmap *last_capacity = NULL, *current_capacity = NULL;
283 _cleanup_close_ int tfd = -EBADF;
284 struct itimerspec ts = {};
285 usec_t suspend_interval;
286 bool woken_by_timer;
287
288 tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
289 if (tfd < 0)
290 return log_error_errno(errno, "Error creating timerfd: %m");
291
292 /* Store current battery capacity before suspension */
293 r = fetch_batteries_capacity_by_name(&last_capacity);
294 if (r < 0)
295 return log_error_errno(r, "Error fetching battery capacity percentage: %m");
296
297 if (hashmap_isempty(last_capacity))
298 /* In case of no battery, system suspend interval will be set to HibernateDelaySec= or 2 hours. */
299 suspend_interval = timestamp_is_set(hibernate_timestamp)
300 ? sleep_config->hibernate_delay_usec : DEFAULT_HIBERNATE_DELAY_USEC_NO_BATTERY;
301 else {
302 r = get_total_suspend_interval(last_capacity, &suspend_interval);
303 if (r < 0) {
304 log_debug_errno(r, "Failed to estimate suspend interval using previous discharge rate, ignoring: %m");
305 /* In case of any errors, especially when we do not know the battery
306 * discharging rate, system suspend interval will be set to
307 * SuspendEstimationSec=. */
308 suspend_interval = sleep_config->suspend_estimation_usec;
309 }
310 }
311
312 /* Do not suspend more than HibernateDelaySec= */
313 usec_t before_timestamp = now(CLOCK_BOOTTIME);
314 suspend_interval = MIN(suspend_interval, usec_sub_unsigned(hibernate_timestamp, before_timestamp));
315 if (suspend_interval <= 0)
316 break; /* system should hibernate */
317
318 log_debug("Set timerfd wake alarm for %s", FORMAT_TIMESPAN(suspend_interval, USEC_PER_SEC));
319 /* Wake alarm for system with or without battery to hibernate or estimate discharge rate whichever is applicable */
320 timespec_store(&ts.it_value, suspend_interval);
321
322 if (timerfd_settime(tfd, 0, &ts, NULL) < 0)
323 return log_error_errno(errno, "Error setting battery estimate timer: %m");
324
325 r = execute(sleep_config, SLEEP_SUSPEND, NULL);
326 if (r < 0)
327 return r;
328
329 r = fd_wait_for_event(tfd, POLLIN, 0);
330 if (r < 0)
331 return log_error_errno(r, "Error polling timerfd: %m");
332 /* Store fd_wait status */
333 woken_by_timer = FLAGS_SET(r, POLLIN);
334
335 r = fetch_batteries_capacity_by_name(&current_capacity);
336 if (r < 0 || hashmap_isempty(current_capacity)) {
337 /* In case of no battery or error while getting charge level, no need to measure
338 * discharge rate. Instead the system should wake up if it is manual wakeup or
339 * hibernate if this is a timer wakeup. */
340 if (r < 0)
341 log_debug_errno(r, "Battery capacity percentage unavailable, cannot estimate discharge rate: %m");
342 else
343 log_debug("No battery found.");
344 if (!woken_by_timer)
345 return 0;
346 break;
347 }
348
349 usec_t after_timestamp = now(CLOCK_BOOTTIME);
350 log_debug("Attempting to estimate battery discharge rate after wakeup from %s sleep",
351 FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_HOUR));
352
353 if (after_timestamp != before_timestamp) {
354 r = estimate_battery_discharge_rate_per_hour(last_capacity, current_capacity, before_timestamp, after_timestamp);
355 if (r < 0)
356 log_warning_errno(r, "Failed to estimate and update battery discharge rate, ignoring: %m");
357 } else
358 log_debug("System woke up too early to estimate discharge rate");
359
360 if (!woken_by_timer)
361 /* Return as manual wakeup done. This also will return in case battery was charged during suspension */
362 return 0;
363
364 r = check_wakeup_type();
365 if (r < 0)
366 log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m");
367 if (r > 0) {
368 log_debug("wakeup type is APM timer");
369 /* system should hibernate */
370 break;
371 }
372 }
373
374 return 1;
375 }
376
377 /* Freeze when invoked and thaw on cleanup */
378 static int freeze_thaw_user_slice(const char **method) {
379 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
380 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
381 int r;
382
383 if (!method || !*method)
384 return 0;
385
386 r = bus_connect_system_systemd(&bus);
387 if (r < 0)
388 return log_debug_errno(r, "Failed to open connection to systemd: %m");
389
390 (void) sd_bus_set_method_call_timeout(bus, FREEZE_TIMEOUT);
391
392 r = bus_call_method(bus, bus_systemd_mgr, *method, &error, NULL, "s", SPECIAL_USER_SLICE);
393 if (r < 0)
394 return log_debug_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
395
396 return 1;
397 }
398
399 static int execute_s2h(const SleepConfig *sleep_config) {
400 _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = "ThawUnit";
401 int r;
402
403 assert(sleep_config);
404
405 r = freeze_thaw_user_slice(&(const char*) { "FreezeUnit" });
406 if (r < 0)
407 log_debug_errno(r, "Failed to freeze unit user.slice, ignoring: %m");
408
409 /* Only check if we have automated battery alarms if HibernateDelaySec= is not set, as in that case
410 * we'll busy poll for the configured interval instead */
411 if (!timestamp_is_set(sleep_config->hibernate_delay_usec)) {
412 r = check_wakeup_type();
413 if (r < 0)
414 log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m");
415 else {
416 r = battery_trip_point_alarm_exists();
417 if (r < 0)
418 log_debug_errno(r, "Failed to check whether acpi_btp support is enabled or not, ignoring: %m");
419 }
420 } else
421 r = 0; /* Force fallback path */
422
423 if (r > 0) { /* If we have both wakeup alarms and battery trip point support, use them */
424 log_debug("Attempting to suspend...");
425 r = execute(sleep_config, SLEEP_SUSPEND, NULL);
426 if (r < 0)
427 return r;
428
429 r = check_wakeup_type();
430 if (r < 0)
431 return log_debug_errno(r, "Failed to check hardware wakeup type: %m");
432
433 if (r == 0)
434 /* For APM Timer wakeup, system should hibernate else wakeup */
435 return 0;
436 } else {
437 r = custom_timer_suspend(sleep_config);
438 if (r < 0)
439 return log_debug_errno(r, "Suspend cycle with manual battery discharge rate estimation failed: %m");
440 if (r == 0)
441 /* manual wakeup */
442 return 0;
443 }
444 /* For above custom timer, if 1 is returned, system will directly hibernate */
445
446 log_debug("Attempting to hibernate");
447 r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
448 if (r < 0) {
449 log_notice("Couldn't hibernate, will try to suspend again.");
450
451 r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hibernate");
452 if (r < 0)
453 return r;
454 }
455
456 return 0;
457 }
458
459 static int help(void) {
460 _cleanup_free_ char *link = NULL;
461 int r;
462
463 r = terminal_urlify_man("systemd-suspend.service", "8", &link);
464 if (r < 0)
465 return log_oom();
466
467 printf("%s COMMAND\n\n"
468 "Suspend the system, hibernate the system, or both.\n\n"
469 " -h --help Show this help and exit\n"
470 " --version Print version string and exit\n"
471 "\nCommands:\n"
472 " suspend Suspend the system\n"
473 " hibernate Hibernate the system\n"
474 " hybrid-sleep Both hibernate and suspend the system\n"
475 " suspend-then-hibernate Initially suspend and then hibernate\n"
476 " the system after a fixed period of time\n"
477 "\nSee the %s for details.\n",
478 program_invocation_short_name,
479 link);
480
481 return 0;
482 }
483
484 static int parse_argv(int argc, char *argv[]) {
485 enum {
486 ARG_VERSION = 0x100,
487 };
488
489 static const struct option options[] = {
490 { "help", no_argument, NULL, 'h' },
491 { "version", no_argument, NULL, ARG_VERSION },
492 {}
493 };
494
495 int c;
496
497 assert(argc >= 0);
498 assert(argv);
499
500 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
501 switch (c) {
502 case 'h':
503 return help();
504
505 case ARG_VERSION:
506 return version();
507
508 case '?':
509 return -EINVAL;
510
511 default:
512 assert_not_reached();
513 }
514
515 if (argc - optind != 1)
516 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
517 "Usage: %s COMMAND",
518 program_invocation_short_name);
519
520 arg_operation = sleep_operation_from_string(argv[optind]);
521 if (arg_operation < 0)
522 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command '%s'.", argv[optind]);
523
524 return 1 /* work to do */;
525 }
526
527 static int run(int argc, char *argv[]) {
528 _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
529 int r;
530
531 log_setup();
532
533 r = parse_argv(argc, argv);
534 if (r <= 0)
535 return r;
536
537 r = parse_sleep_config(&sleep_config);
538 if (r < 0)
539 return r;
540
541 if (!sleep_config->allow[arg_operation])
542 return log_error_errno(SYNTHETIC_ERRNO(EACCES),
543 "Sleep operation \"%s\" is disabled by configuration, refusing.",
544 sleep_operation_to_string(arg_operation));
545
546 switch (arg_operation) {
547
548 case SLEEP_SUSPEND_THEN_HIBERNATE:
549 r = execute_s2h(sleep_config);
550 break;
551
552 case SLEEP_HYBRID_SLEEP:
553 r = execute(sleep_config, SLEEP_HYBRID_SLEEP, NULL);
554 if (r < 0) {
555 /* If we can't hybrid sleep, then let's try to suspend at least. After all, the user
556 * asked us to do both: suspend + hibernate, and it's almost certainly the
557 * hibernation that failed, hence still do the other thing, the suspend. */
558
559 log_notice("Couldn't hybrid sleep, will try to suspend instead.");
560
561 r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hybrid-sleep");
562 }
563
564 break;
565
566 default:
567 r = execute(sleep_config, arg_operation, NULL);
568 break;
569 }
570
571 return r;
572 }
573
574 DEFINE_MAIN_FUNCTION(run);