]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/sleep-util.c
device-util: Declare iterator variables inline
[thirdparty/systemd.git] / src / shared / sleep-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
19adb8a3 2/***
96b2fb93 3 Copyright © 2018 Dell Inc.
19adb8a3
ZJS
4***/
5
a8fbdf54 6#include <errno.h>
ca78ad1d 7#include <fcntl.h>
17c40b3a 8#include <linux/fs.h>
65ddc2c5 9#include <linux/magic.h>
a8fbdf54
TA
10#include <stdbool.h>
11#include <stddef.h>
36dd5ffd 12#include <sys/ioctl.h>
ca78ad1d
ZJS
13#include <sys/stat.h>
14#include <sys/types.h>
a8fbdf54
TA
15#include <syslog.h>
16#include <unistd.h>
19adb8a3 17
746cf898
SS
18#include "sd-device.h"
19
b5efdb8a 20#include "alloc-util.h"
af4e8e86 21#include "battery-util.h"
52133271 22#include "blockdev-util.h"
7bdf56a2 23#include "btrfs-util.h"
19adb8a3 24#include "conf-parser.h"
28db6fbf 25#include "constants.h"
3d9ca76f 26#include "device-private.h"
746cf898 27#include "device-util.h"
7176f06c 28#include "devnum-util.h"
490d20e6 29#include "env-util.h"
427646ea 30#include "errno-util.h"
3ffd4af2 31#include "fd-util.h"
19adb8a3 32#include "fileio.h"
91ea7ebc
SS
33#include "hexdecoct.h"
34#include "id128-util.h"
19adb8a3 35#include "log.h"
a8fbdf54 36#include "macro.h"
411ae92b 37#include "path-util.h"
91ea7ebc 38#include "siphash24.h"
4014172a 39#include "sleep-util.h"
65ddc2c5 40#include "stat-util.h"
7bdf56a2 41#include "stdio-util.h"
be2a4b0d 42#include "string-table.h"
07630cea 43#include "string-util.h"
19adb8a3 44#include "strv.h"
1bbbefe7 45#include "time-util.h"
19adb8a3 46
91ea7ebc
SS
47#define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour"
48#define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63)
49
746cf898
SS
50static void *CAPACITY_TO_PTR(int capacity) {
51 assert(capacity >= 0);
52 assert(capacity <= 100);
53 return INT_TO_PTR(capacity + 1);
54}
55
56static int PTR_TO_CAPACITY(void *p) {
57 int capacity = PTR_TO_INT(p) - 1;
58 assert(capacity >= 0);
59 assert(capacity <= 100);
60 return capacity;
61}
62
28ca9c24 63int parse_sleep_config(SleepConfig **ret_sleep_config) {
c2b2df60 64 _cleanup_(free_sleep_configp) SleepConfig *sc = NULL;
e8f1d00d
ZJS
65 int allow_suspend = -1, allow_hibernate = -1,
66 allow_s2h = -1, allow_hybrid_sleep = -1;
28ca9c24 67
4f58b656 68 sc = new(SleepConfig, 1);
28ca9c24
ZS
69 if (!sc)
70 return log_oom();
19adb8a3 71
4f58b656
YW
72 *sc = (SleepConfig) {
73 .hibernate_delay_usec = USEC_INFINITY,
74 };
75
19adb8a3 76 const ConfigTableItem items[] = {
c8cd8ca3
LP
77 { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
78 { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
79 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
80 { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
81
82 { "Sleep", "SuspendMode", config_parse_strv, 0, sc->modes + SLEEP_SUSPEND },
83 { "Sleep", "SuspendState", config_parse_strv, 0, sc->states + SLEEP_SUSPEND },
84 { "Sleep", "HibernateMode", config_parse_strv, 0, sc->modes + SLEEP_HIBERNATE },
85 { "Sleep", "HibernateState", config_parse_strv, 0, sc->states + SLEEP_HIBERNATE },
86 { "Sleep", "HybridSleepMode", config_parse_strv, 0, sc->modes + SLEEP_HYBRID_SLEEP },
87 { "Sleep", "HybridSleepState", config_parse_strv, 0, sc->states + SLEEP_HYBRID_SLEEP },
88
3d23df00 89 { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_usec },
4f58b656 90 { "Sleep", "SuspendEstimationSec", config_parse_sec, 0, &sc->suspend_estimation_usec },
34c10968
LP
91 {}
92 };
19adb8a3 93
07e0ffc8
FB
94 (void) config_parse_config_file("sleep.conf", "Sleep\0",
95 config_item_table_lookup, items,
96 CONFIG_PARSE_WARN, NULL);
19adb8a3 97
28ca9c24 98 /* use default values unless set */
c8cd8ca3
LP
99 sc->allow[SLEEP_SUSPEND] = allow_suspend != 0;
100 sc->allow[SLEEP_HIBERNATE] = allow_hibernate != 0;
101 sc->allow[SLEEP_HYBRID_SLEEP] = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep
7874583d 102 : (allow_suspend != 0 && allow_hibernate != 0);
c8cd8ca3 103 sc->allow[SLEEP_SUSPEND_THEN_HIBERNATE] = allow_s2h >= 0 ? allow_s2h
7874583d 104 : (allow_suspend != 0 && allow_hibernate != 0);
28ca9c24 105
c8cd8ca3
LP
106 if (!sc->states[SLEEP_SUSPEND])
107 sc->states[SLEEP_SUSPEND] = strv_new("mem", "standby", "freeze");
108 if (!sc->modes[SLEEP_HIBERNATE])
109 sc->modes[SLEEP_HIBERNATE] = strv_new("platform", "shutdown");
110 if (!sc->states[SLEEP_HIBERNATE])
111 sc->states[SLEEP_HIBERNATE] = strv_new("disk");
112 if (!sc->modes[SLEEP_HYBRID_SLEEP])
113 sc->modes[SLEEP_HYBRID_SLEEP] = strv_new("suspend", "platform", "shutdown");
114 if (!sc->states[SLEEP_HYBRID_SLEEP])
115 sc->states[SLEEP_HYBRID_SLEEP] = strv_new("disk");
4f58b656
YW
116 if (sc->suspend_estimation_usec == 0)
117 sc->suspend_estimation_usec = DEFAULT_SUSPEND_ESTIMATION_USEC;
28ca9c24 118
099810a6 119 /* Ensure values set for all required fields */
c8cd8ca3
LP
120 if (!sc->states[SLEEP_SUSPEND] || !sc->modes[SLEEP_HIBERNATE]
121 || !sc->states[SLEEP_HIBERNATE] || !sc->modes[SLEEP_HYBRID_SLEEP] || !sc->states[SLEEP_HYBRID_SLEEP])
19adb8a3 122 return log_oom();
19adb8a3 123
28ca9c24 124 *ret_sleep_config = TAKE_PTR(sc);
c58493c0 125
19adb8a3
ZJS
126 return 0;
127}
128
31f62bdd 129int get_capacity_by_name(Hashmap *capacities_by_name, const char *name) {
746cf898
SS
130 void *p;
131
132 assert(capacities_by_name);
133 assert(name);
134
135 p = hashmap_get(capacities_by_name, name);
136 if (!p)
137 return -ENOENT;
138
139 return PTR_TO_CAPACITY(p);
96d662fa
SS
140}
141
746cf898
SS
142/* Store current capacity of each battery before suspension and timestamp */
143int fetch_batteries_capacity_by_name(Hashmap **ret) {
144 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
5d2a48da 145 _cleanup_hashmap_free_ Hashmap *batteries_capacity_by_name = NULL;
91ea7ebc
SS
146 int r;
147
91ea7ebc
SS
148 assert(ret);
149
746cf898
SS
150 batteries_capacity_by_name = hashmap_new(&string_hash_ops_free);
151 if (!batteries_capacity_by_name)
152 return log_oom_debug();
153
154 r = battery_enumerator_new(&e);
155 if (r < 0)
156 return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
157
158 FOREACH_DEVICE(e, dev) {
159 _cleanup_free_ char *battery_name_copy = NULL;
160 const char *battery_name;
161 int battery_capacity;
162
319c4648 163 battery_capacity = r = battery_read_capacity_percentage(dev);
099810a6 164 if (r < 0)
746cf898 165 continue;
746cf898
SS
166
167 r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
168 if (r < 0) {
099810a6 169 log_device_debug_errno(dev, r, "Failed to get POWER_SUPPLY_NAME property, ignoring: %m");
746cf898
SS
170 continue;
171 }
172
173 battery_name_copy = strdup(battery_name);
174 if (!battery_name_copy)
175 return log_oom_debug();
176
177 r = hashmap_put(batteries_capacity_by_name, battery_name_copy, CAPACITY_TO_PTR(battery_capacity));
178 if (r < 0)
179 return log_device_debug_errno(dev, r, "Failed to store battery capacity: %m");
180
181 TAKE_PTR(battery_name_copy);
182 }
183
184 *ret = TAKE_PTR(batteries_capacity_by_name);
185
186 return 0;
187}
188
a7795a4e 189static int siphash24_compress_device_sysattr(sd_device *dev, const char *attr, struct siphash *state) {
746cf898
SS
190 const char *x;
191 int r;
192
193 assert(dev);
a7795a4e 194 assert(attr);
746cf898
SS
195 assert(state);
196
a7795a4e
YW
197 r = sd_device_get_sysattr_value(dev, attr, &x);
198 if (r < 0)
199 return log_device_debug_errno(dev, r, "Failed to read '%s' attribute: %m", attr);
200
201 if (!isempty(x))
202 siphash24_compress_string(x, state);
91ea7ebc
SS
203
204 return 0;
205}
206
a7795a4e
YW
207static int siphash24_compress_id128(int (*getter)(sd_id128_t*), const char *name, struct siphash *state) {
208 sd_id128_t id;
209 int r;
210
211 assert(getter);
212 assert(state);
213
214 r = getter(&id);
215 if (r < 0)
216 return log_debug_errno(r, "Failed to get %s ID: %m", name);
217
218 siphash24_compress(&id, sizeof(sd_id128_t), state);
219 return 0;
220}
221
91ea7ebc 222/* Read system and battery identifier from specific location and generate hash of it */
746cf898 223static int get_system_battery_identifier_hash(sd_device *dev, uint64_t *ret) {
91ea7ebc 224 struct siphash state;
91ea7ebc 225
746cf898
SS
226 assert(ret);
227 assert(dev);
91ea7ebc
SS
228
229 siphash24_init(&state, BATTERY_DISCHARGE_RATE_HASH_KEY.bytes);
746cf898 230
a7795a4e
YW
231 (void) siphash24_compress_device_sysattr(dev, "manufacturer", &state);
232 (void) siphash24_compress_device_sysattr(dev, "model_name", &state);
233 (void) siphash24_compress_device_sysattr(dev, "serial_number", &state);
234 (void) siphash24_compress_id128(sd_id128_get_machine, "machine", &state);
235 (void) siphash24_compress_id128(id128_get_product, "product", &state);
91ea7ebc 236
746cf898 237 *ret = siphash24_finalize(&state);
91ea7ebc
SS
238 return 0;
239}
240
099810a6 241/* Return success if battery percentage discharge rate per hour is in the range 1–199 */
91ea7ebc
SS
242static bool battery_discharge_rate_is_valid(int battery_discharge_rate) {
243 return battery_discharge_rate > 0 && battery_discharge_rate < 200;
244}
245
246/* Battery percentage discharge rate per hour is read from specific file. It is stored along with system
247 * and battery identifier hash to maintain the integrity of discharge rate value */
746cf898
SS
248static int get_battery_discharge_rate(sd_device *dev, int *ret) {
249 _cleanup_fclose_ FILE *f = NULL;
250 uint64_t current_hash_id;
91ea7ebc 251 const char *p;
746cf898 252 int r;
91ea7ebc 253
746cf898
SS
254 assert(dev);
255 assert(ret);
91ea7ebc 256
746cf898
SS
257 f = fopen(DISCHARGE_RATE_FILEPATH, "re");
258 if (!f)
259 return log_debug_errno(errno, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
91ea7ebc 260
746cf898 261 r = get_system_battery_identifier_hash(dev, &current_hash_id);
91ea7ebc 262 if (r < 0)
746cf898
SS
263 return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
264
265 for (;;) {
266 _cleanup_free_ char *stored_hash_id = NULL, *stored_discharge_rate = NULL, *line = NULL;
267 uint64_t hash_id;
268 int discharge_rate;
269
270 r = read_line(f, LONG_LINE_MAX, &line);
271 if (r < 0)
272 return log_debug_errno(r, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
273 if (r == 0)
274 break;
275
276 p = line;
277 r = extract_many_words(&p, NULL, 0, &stored_hash_id, &stored_discharge_rate, NULL);
278 if (r < 0)
279 return log_debug_errno(r, "Failed to parse hash_id and discharge_rate read from " DISCHARGE_RATE_FILEPATH ": %m");
280 if (r != 2)
281 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid number of items fetched from " DISCHARGE_RATE_FILEPATH);
282
283 r = safe_atou64(stored_hash_id, &hash_id);
284 if (r < 0)
285 return log_debug_errno(r, "Failed to parse hash ID read from " DISCHARGE_RATE_FILEPATH " location: %m");
286
287 if (current_hash_id != hash_id)
288 /* matching device not found, move to next line */
289 continue;
290
291 r = safe_atoi(stored_discharge_rate, &discharge_rate);
292 if (r < 0)
293 return log_device_debug_errno(dev, r, "Failed to parse discharge rate read from " DISCHARGE_RATE_FILEPATH ": %m");
294
295 if (!battery_discharge_rate_is_valid(discharge_rate))
296 return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery discharge percentage rate per hour: %m");
297
298 *ret = discharge_rate;
299 return 0; /* matching device found, exit iteration */
300 }
301
302 return -ENOENT;
303}
91ea7ebc 304
746cf898
SS
305/* Write battery percentage discharge rate per hour along with system and battery identifier hash to file */
306static int put_battery_discharge_rate(int estimated_battery_discharge_rate, uint64_t system_hash_id, bool trunc) {
307 int r;
91ea7ebc 308
746cf898
SS
309 if (!battery_discharge_rate_is_valid(estimated_battery_discharge_rate))
310 return log_debug_errno(SYNTHETIC_ERRNO(ERANGE),
311 "Invalid battery discharge rate %d%% per hour: %m",
312 estimated_battery_discharge_rate);
91ea7ebc 313
746cf898 314 r = write_string_filef(
3332cfe1
YW
315 DISCHARGE_RATE_FILEPATH,
316 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_MKDIR_0755 | (trunc ? WRITE_STRING_FILE_TRUNCATE : 0),
317 "%"PRIu64" %d",
318 system_hash_id,
319 estimated_battery_discharge_rate);
91ea7ebc 320 if (r < 0)
746cf898 321 return log_debug_errno(r, "Failed to update %s: %m", DISCHARGE_RATE_FILEPATH);
91ea7ebc 322
746cf898 323 log_debug("Estimated discharge rate %d%% per hour successfully saved to %s", estimated_battery_discharge_rate, DISCHARGE_RATE_FILEPATH);
91ea7ebc 324
746cf898 325 return 0;
91ea7ebc
SS
326}
327
746cf898
SS
328/* Estimate battery discharge rate using stored previous and current capacity over timestamp difference */
329int estimate_battery_discharge_rate_per_hour(
330 Hashmap *last_capacity,
331 Hashmap *current_capacity,
332 usec_t before_timestamp,
333 usec_t after_timestamp) {
334
335 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
746cf898 336 bool trunc = true;
91ea7ebc
SS
337 int r;
338
746cf898
SS
339 assert(last_capacity);
340 assert(current_capacity);
341 assert(before_timestamp < after_timestamp);
91ea7ebc 342
746cf898 343 r = battery_enumerator_new(&e);
91ea7ebc 344 if (r < 0)
746cf898
SS
345 return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
346
347 FOREACH_DEVICE(e, dev) {
348 int battery_last_capacity, battery_current_capacity, battery_discharge_rate;
349 const char *battery_name;
350 uint64_t system_hash_id;
351
352 r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
353 if (r < 0) {
354 log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
355 continue;
356 }
357
358 battery_last_capacity = get_capacity_by_name(last_capacity, battery_name);
359 if (battery_last_capacity < 0)
360 continue;
361
362 battery_current_capacity = get_capacity_by_name(current_capacity, battery_name);
363 if (battery_current_capacity < 0)
364 continue;
365
366 if (battery_current_capacity >= battery_last_capacity) {
367 log_device_debug(dev, "Battery was not discharged during suspension");
368 continue;
369 }
370
371 r = get_system_battery_identifier_hash(dev, &system_hash_id);
372 if (r < 0)
373 return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
374
375 log_device_debug(dev,
376 "%d%% was discharged in %s. Estimating discharge rate...",
377 battery_last_capacity - battery_current_capacity,
378 FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_SEC));
379
380 battery_discharge_rate = (battery_last_capacity - battery_current_capacity) * USEC_PER_HOUR / (after_timestamp - before_timestamp);
381 r = put_battery_discharge_rate(battery_discharge_rate, system_hash_id, trunc);
382 if (r < 0)
383 log_device_warning_errno(dev, r, "Failed to update battery discharge rate, ignoring: %m");
384 else
385 trunc = false;
386 }
91ea7ebc 387
746cf898
SS
388 return 0;
389}
390
099810a6 391/* Calculate the suspend interval for each battery and then return their sum */
746cf898
SS
392int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret) {
393 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
394 usec_t total_suspend_interval = 0;
746cf898
SS
395 int r;
396
397 assert(last_capacity);
398 assert(ret);
399
400 r = battery_enumerator_new(&e);
91ea7ebc 401 if (r < 0)
746cf898
SS
402 return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
403
404 FOREACH_DEVICE(e, dev) {
405 int battery_last_capacity, previous_discharge_rate = 0;
406 const char *battery_name;
407 usec_t suspend_interval;
408
409 r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
410 if (r < 0) {
411 log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
412 continue;
413 }
414
7ebbe4a5 415 battery_last_capacity = get_capacity_by_name(last_capacity, battery_name);
746cf898
SS
416 if (battery_last_capacity <= 0)
417 continue;
418
419 r = get_battery_discharge_rate(dev, &previous_discharge_rate);
420 if (r < 0) {
421 log_device_debug_errno(dev, r, "Failed to get discharge rate, ignoring: %m");
422 continue;
423 }
424
425 if (previous_discharge_rate == 0)
426 continue;
427
428 if (battery_last_capacity * 2 <= previous_discharge_rate) {
429 log_device_debug(dev, "Current battery capacity percentage too low compared to discharge rate");
430 continue;
431 }
432 suspend_interval = battery_last_capacity * USEC_PER_HOUR / previous_discharge_rate;
433
434 total_suspend_interval = usec_add(total_suspend_interval, suspend_interval);
435 }
099810a6
ZJS
436 /* Previous discharge rate is stored in per hour basis converted to usec.
437 * Subtract 30 minutes from the result to keep a buffer of 30 minutes before battery gets critical */
746cf898
SS
438 total_suspend_interval = usec_sub_unsigned(total_suspend_interval, 30 * USEC_PER_MINUTE);
439 if (total_suspend_interval == 0)
440 return -ENOENT;
441
442 *ret = total_suspend_interval;
91ea7ebc 443
91ea7ebc
SS
444 return 0;
445}
446
1afe3d71
SS
447/* Return true if all batteries have acpi_btp support */
448int battery_trip_point_alarm_exists(void) {
449 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
1afe3d71
SS
450 int r;
451
452 r = battery_enumerator_new(&e);
453 if (r < 0)
454 return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
455
456 FOREACH_DEVICE(e, dev) {
457 int battery_alarm;
458 const char *s;
459
460 r = sd_device_get_sysattr_value(dev, "alarm", &s);
461 if (r < 0)
462 return log_device_debug_errno(dev, r, "Failed to read battery alarm: %m");
463
464 r = safe_atoi(s, &battery_alarm);
465 if (r < 0)
466 return log_device_debug_errno(dev, r, "Failed to parse battery alarm: %m");
467 if (battery_alarm <= 0)
468 return false;
469 }
470
471 return true;
472}
473
474/* Return true if wakeup type is APM timer */
475int check_wakeup_type(void) {
c1583ca1 476 static const char dmi_object_path[] = "/sys/firmware/dmi/entries/1-0/raw";
1afe3d71 477 uint8_t wakeup_type_byte, tablesize;
c1583ca1
LP
478 _cleanup_free_ char *buf = NULL;
479 size_t bufsize;
1afe3d71
SS
480 int r;
481
482 /* implementation via dmi/entries */
c1583ca1 483 r = read_full_virtual_file(dmi_object_path, &buf, &bufsize);
1afe3d71 484 if (r < 0)
c1583ca1
LP
485 return log_debug_errno(r, "Unable to read %s: %m", dmi_object_path);
486 if (bufsize < 25)
487 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Only read %zu bytes from %s (expected 25)", bufsize, dmi_object_path);
1afe3d71
SS
488
489 /* index 1 stores the size of table */
c1583ca1 490 tablesize = (uint8_t) buf[1];
1afe3d71
SS
491 if (tablesize < 25)
492 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Table size lesser than the index[0x18] where waketype byte is available.");
493
c1583ca1 494 wakeup_type_byte = (uint8_t) buf[24];
1afe3d71
SS
495 /* 0 is Reserved and 8 is AC Power Restored. As per table 12 in
496 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf */
497 if (wakeup_type_byte >= 128)
498 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Expected value in range 0-127");
499
500 if (wakeup_type_byte == 3) {
501 log_debug("DMI BIOS System Information indicates wakeup type is APM Timer");
502 return true;
503 }
504
505 return false;
506}
507
fd6ec01d 508int can_sleep_state(char **requested_types) {
434fef6d 509 _cleanup_free_ char *text = NULL;
19adb8a3 510 int r;
19adb8a3 511
fd6ec01d 512 if (strv_isempty(requested_types))
19adb8a3
ZJS
513 return true;
514
515 /* If /sys is read-only we cannot sleep */
c0d8fbfa
LP
516 if (access("/sys/power/state", W_OK) < 0) {
517 log_debug_errno(errno, "/sys/power/state is not writable, cannot sleep: %m");
19adb8a3 518 return false;
c0d8fbfa 519 }
19adb8a3 520
434fef6d 521 r = read_one_line_file("/sys/power/state", &text);
c0d8fbfa
LP
522 if (r < 0) {
523 log_debug_errno(r, "Failed to read /sys/power/state, cannot sleep: %m");
19adb8a3 524 return false;
c0d8fbfa 525 }
19adb8a3 526
434fef6d 527 const char *found;
fd6ec01d 528 r = string_contains_word_strv(text, NULL, requested_types, &found);
434fef6d
ZJS
529 if (r < 0)
530 return log_debug_errno(r, "Failed to parse /sys/power/state: %m");
531 if (r > 0)
532 log_debug("Sleep mode \"%s\" is supported by the kernel.", found);
533 else if (DEBUG_LOGGING) {
fd6ec01d 534 _cleanup_free_ char *t = strv_join(requested_types, "/");
c550cb7f
ZJS
535 log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t));
536 }
434fef6d 537 return r;
19adb8a3
ZJS
538}
539
540int can_sleep_disk(char **types) {
434fef6d 541 _cleanup_free_ char *text = NULL;
19adb8a3 542 int r;
19adb8a3
ZJS
543
544 if (strv_isempty(types))
545 return true;
546
547 /* If /sys is read-only we cannot sleep */
7474f15b
LP
548 if (access("/sys/power/disk", W_OK) < 0) {
549 log_debug_errno(errno, "/sys/power/disk is not writable: %m");
19adb8a3 550 return false;
7474f15b 551 }
19adb8a3 552
434fef6d 553 r = read_one_line_file("/sys/power/disk", &text);
7474f15b
LP
554 if (r < 0) {
555 log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
19adb8a3 556 return false;
7474f15b 557 }
19adb8a3 558
434fef6d
ZJS
559 for (const char *p = text;;) {
560 _cleanup_free_ char *word = NULL;
19adb8a3 561
434fef6d
ZJS
562 r = extract_first_word(&p, &word, NULL, 0);
563 if (r < 0)
564 return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
565 if (r == 0)
566 break;
19adb8a3 567
434fef6d
ZJS
568 char *s = word;
569 size_t l = strlen(s);
570 if (s[0] == '[' && s[l-1] == ']') {
571 s[l-1] = '\0';
572 s++;
573 }
574
575 if (strv_contains(types, s)) {
576 log_debug("Disk sleep mode \"%s\" is supported by the kernel.", s);
577 return true;
19adb8a3
ZJS
578 }
579 }
580
434fef6d
ZJS
581 if (DEBUG_LOGGING) {
582 _cleanup_free_ char *t = strv_join(types, "/");
583 log_debug("Disk sleep mode %s not supported by the kernel, sorry.", strnull(t));
584 }
19adb8a3
ZJS
585 return false;
586}
587
69ab8088
ZJS
588#define HIBERNATION_SWAP_THRESHOLD 0.98
589
7bdf56a2 590SwapEntry* swap_entry_free(SwapEntry *se) {
88bc86fc
ZS
591 if (!se)
592 return NULL;
593
8601ecbc 594 free(se->path);
88bc86fc
ZS
595
596 return mfree(se);
597}
598
7bdf56a2
ZS
599HibernateLocation* hibernate_location_free(HibernateLocation *hl) {
600 if (!hl)
601 return NULL;
602
603 swap_entry_free(hl->swap);
7bdf56a2
ZS
604
605 return mfree(hl);
606}
607
3595a9b7 608static int swap_device_to_devnum(const SwapEntry *swap, dev_t *ret_dev) {
98034eb0 609 _cleanup_close_ int fd = -EBADF;
7bdf56a2 610 struct stat sb;
7bdf56a2
ZS
611 int r;
612
613 assert(swap);
8601ecbc 614 assert(swap->path);
7bdf56a2 615
8601ecbc 616 fd = open(swap->path, O_CLOEXEC|O_PATH);
98034eb0
LP
617 if (fd < 0)
618 return -errno;
619
620 if (fstat(fd, &sb) < 0)
6f9120ad 621 return -errno;
7bdf56a2 622
1296b427 623 if (swap->type == SWAP_BLOCK) {
e9f0c5d0 624 if (!S_ISBLK(sb.st_mode))
52133271 625 return -ENOTBLK;
6f9120ad 626
e9f0c5d0
ZJS
627 *ret_dev = sb.st_rdev;
628 return 0;
6f9120ad 629 }
7bdf56a2 630
08e0ed30
LP
631 r = stat_verify_regular(&sb);
632 if (r < 0)
633 return r;
634
98034eb0 635 return get_block_device_fd(fd, ret_dev);
7bdf56a2
ZS
636}
637
52133271 638/*
162392b7 639 * Attempt to calculate the swap file offset on supported filesystems. On unsupported
8efc2c16 640 * filesystems, a debug message is logged and ret_offset is set to UINT64_MAX.
52133271 641 */
7bdf56a2 642static int calculate_swap_file_offset(const SwapEntry *swap, uint64_t *ret_offset) {
254d1313 643 _cleanup_close_ int fd = -EBADF;
7bdf56a2 644 _cleanup_free_ struct fiemap *fiemap = NULL;
65ddc2c5 645 int r;
7bdf56a2
ZS
646
647 assert(swap);
8601ecbc 648 assert(swap->path);
1296b427
LP
649 assert(swap->type == SWAP_FILE);
650 assert(ret_offset);
7bdf56a2 651
8601ecbc 652 fd = open(swap->path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
e97c3691 653 if (fd < 0)
8601ecbc 654 return log_debug_errno(errno, "Failed to open swap file %s to determine on-disk offset: %m", swap->path);
7bdf56a2 655
1cf78c8f
LP
656 r = fd_verify_regular(fd);
657 if (r < 0)
658 return log_debug_errno(r, "Selected swap file is not a regular file.");
7bdf56a2 659
65ddc2c5
ZJS
660 r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
661 if (r < 0)
8601ecbc 662 return log_debug_errno(r, "Error checking %s for Btrfs filesystem: %m", swap->path);
65ddc2c5 663 if (r > 0) {
8601ecbc 664 log_debug("%s: detection of swap file offset on Btrfs is not supported", swap->path);
8efc2c16 665 *ret_offset = UINT64_MAX;
7bdf56a2
ZS
666 return 0;
667 }
668
669 r = read_fiemap(fd, &fiemap);
670 if (r < 0)
8601ecbc 671 return log_debug_errno(r, "Unable to read extent map for '%s': %m", swap->path);
7bdf56a2
ZS
672
673 *ret_offset = fiemap->fm_extents[0].fe_physical / page_size();
7bdf56a2
ZS
674 return 0;
675}
88bc86fc 676
52133271
ZS
677static int read_resume_files(dev_t *ret_resume, uint64_t *ret_resume_offset) {
678 _cleanup_free_ char *resume_str = NULL, *resume_offset_str = NULL;
1296b427 679 uint64_t resume_offset;
c02540dc 680 dev_t resume;
7bdf56a2
ZS
681 int r;
682
1296b427
LP
683 assert(ret_resume);
684 assert(ret_resume_offset);
685
52133271 686 r = read_one_line_file("/sys/power/resume", &resume_str);
7bdf56a2 687 if (r < 0)
5021735f 688 return log_debug_errno(r, "Error reading /sys/power/resume: %m");
7bdf56a2 689
7176f06c 690 r = parse_devnum(resume_str, &resume);
52133271
ZS
691 if (r < 0)
692 return log_debug_errno(r, "Error parsing /sys/power/resume device: %s: %m", resume_str);
693
7bdf56a2 694 r = read_one_line_file("/sys/power/resume_offset", &resume_offset_str);
1296b427 695 if (r == -ENOENT) {
c02540dc 696 log_debug_errno(r, "Kernel does not support resume_offset; swap file offset detection will be skipped.");
1296b427
LP
697 resume_offset = 0;
698 } else if (r < 0)
5021735f 699 return log_debug_errno(r, "Error reading /sys/power/resume_offset: %m");
b72571e0 700 else {
7bdf56a2
ZS
701 r = safe_atou64(resume_offset_str, &resume_offset);
702 if (r < 0)
c02540dc 703 return log_debug_errno(r, "Failed to parse value in /sys/power/resume_offset \"%s\": %m", resume_offset_str);
7bdf56a2
ZS
704 }
705
8f817cb8
ZJS
706 if (resume_offset > 0 && resume == 0)
707 log_debug("Warning: found /sys/power/resume_offset==%" PRIu64 ", but /sys/power/resume unset. Misconfiguration?",
5021735f 708 resume_offset);
7bdf56a2 709
52133271 710 *ret_resume = resume;
7bdf56a2 711 *ret_resume_offset = resume_offset;
7bdf56a2
ZS
712 return 0;
713}
714
52133271
ZS
715/*
716 * Determine if the HibernateLocation matches the resume= (device) and resume_offset= (file).
717 */
718static bool location_is_resume_device(const HibernateLocation *location, dev_t sys_resume, uint64_t sys_offset) {
719 if (!location)
720 return false;
721
8efc2c16
ZJS
722 return sys_resume > 0 &&
723 sys_resume == location->devno &&
724 (sys_offset == location->offset || (sys_offset > 0 && location->offset == UINT64_MAX));
7bdf56a2
ZS
725}
726
727/*
728 * Attempt to find the hibernation location by parsing /proc/swaps, /sys/power/resume, and
729 * /sys/power/resume_offset.
730 *
14941724
CAM
731 * Beware:
732 * Never use a device or file as location that hasn't been somehow specified by a user that would also be
733 * entrusted with full system memory access (for example via /sys/power/resume) or that isn't an already
734 * active swap area!
735 * Otherwise various security attacks might become possible, for example an attacker could silently attach
736 * such a device and circumvent full disk encryption when it would be automatically used for hibernation.
737 * Also, having a swap area on top of encryption is not per se enough to protect from all such attacks.
738 *
7bdf56a2 739 * Returns:
52133271
ZS
740 * 1 - Values are set in /sys/power/resume and /sys/power/resume_offset.
741 * ret_hibernate_location will represent matching /proc/swap entry if identified or NULL if not.
742 *
743 * 0 - No values are set in /sys/power/resume and /sys/power/resume_offset.
744 ret_hibernate_location will represent the highest priority swap with most remaining space discovered in /proc/swaps.
745 *
746 * Negative value in the case of error.
7bdf56a2
ZS
747 */
748int find_hibernate_location(HibernateLocation **ret_hibernate_location) {
b72571e0 749 _cleanup_fclose_ FILE *f = NULL;
7bdf56a2 750 _cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
aff81b18 751 dev_t sys_resume = 0; /* Unnecessary initialization to appease gcc */
7bdf56a2 752 uint64_t sys_offset = 0;
8efc2c16 753 bool resume_match = false;
7bdf56a2
ZS
754 int r;
755
756 /* read the /sys/power/resume & /sys/power/resume_offset values */
757 r = read_resume_files(&sys_resume, &sys_offset);
758 if (r < 0)
759 return r;
9fb3675e 760
c8a202b7 761 f = fopen("/proc/swaps", "re");
9fb3675e 762 if (!f) {
c02540dc
LP
763 log_debug_errno(errno, "Failed to open /proc/swaps: %m");
764 return errno == ENOENT ? -EOPNOTSUPP : -errno; /* Convert swap not supported to a recognizable error */
9fb3675e 765 }
69ab8088 766
9fb3675e 767 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
8efc2c16 768 for (unsigned i = 1;; i++) {
88bc86fc 769 _cleanup_(swap_entry_freep) SwapEntry *swap = NULL;
1296b427 770 _cleanup_free_ char *type = NULL;
7bdf56a2 771 uint64_t swap_offset = 0;
9fb3675e
ZJS
772 int k;
773
1296b427 774 swap = new(SwapEntry, 1);
88bc86fc 775 if (!swap)
c02540dc 776 return -ENOMEM;
88bc86fc 777
1296b427
LP
778 *swap = (SwapEntry) {
779 .type = _SWAP_TYPE_INVALID,
780 };
781
9fb3675e 782 k = fscanf(f,
8601ecbc 783 "%ms " /* device/file path */
88bc86fc
ZS
784 "%ms " /* type of swap */
785 "%" PRIu64 /* swap size */
786 "%" PRIu64 /* used */
787 "%i\n", /* priority */
8601ecbc 788 &swap->path, &type, &swap->size, &swap->used, &swap->priority);
3dea6886
LP
789 if (k == EOF)
790 break;
88bc86fc 791 if (k != 5) {
c02540dc 792 log_debug("Failed to parse /proc/swaps:%u, ignoring", i);
9fb3675e
ZJS
793 continue;
794 }
795
1296b427
LP
796 if (streq(type, "file")) {
797
8601ecbc
LP
798 if (endswith(swap->path, "\\040(deleted)")) {
799 log_debug("Ignoring deleted swap file '%s'.", swap->path);
411ae92b
AJ
800 continue;
801 }
802
1296b427
LP
803 swap->type = SWAP_FILE;
804
7bdf56a2
ZS
805 r = calculate_swap_file_offset(swap, &swap_offset);
806 if (r < 0)
807 return r;
52133271 808
1296b427 809 } else if (streq(type, "partition")) {
411ae92b 810 const char *fn;
3dea6886 811
8601ecbc 812 fn = path_startswith(swap->path, "/dev/");
411ae92b 813 if (fn && startswith(fn, "zram")) {
8601ecbc 814 log_debug("%s: ignoring zram swap", swap->path);
411ae92b
AJ
815 continue;
816 }
8efc2c16 817
1296b427
LP
818 swap->type = SWAP_BLOCK;
819
7bdf56a2 820 } else {
8601ecbc 821 log_debug("%s: swap type %s is unsupported for hibernation, ignoring", swap->path, type);
7bdf56a2 822 continue;
9fb3675e 823 }
3dea6886 824
7bdf56a2 825 /* prefer resume device or highest priority swap with most remaining space */
936a7cb6
E
826 if (sys_resume == 0) {
827 if (hibernate_location && swap->priority < hibernate_location->swap->priority) {
8601ecbc 828 log_debug("%s: ignoring device with lower priority", swap->path);
936a7cb6
E
829 continue;
830 }
831 if (hibernate_location &&
832 (swap->priority == hibernate_location->swap->priority
833 && swap->size - swap->used < hibernate_location->swap->size - hibernate_location->swap->used)) {
8601ecbc 834 log_debug("%s: ignoring device with lower usable space", swap->path);
936a7cb6
E
835 continue;
836 }
8efc2c16 837 }
7bdf56a2 838
3595a9b7
LP
839 dev_t swap_devno;
840 r = swap_device_to_devnum(swap, &swap_devno);
8efc2c16 841 if (r < 0)
8601ecbc 842 return log_debug_errno(r, "%s: failed to query device number: %m", swap->path);
3595a9b7 843 if (swap_devno == 0)
8601ecbc 844 return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "%s: not backed by block device.", swap->path);
7bdf56a2 845
8efc2c16
ZJS
846 hibernate_location = hibernate_location_free(hibernate_location);
847 hibernate_location = new(HibernateLocation, 1);
848 if (!hibernate_location)
c02540dc 849 return -ENOMEM;
7bdf56a2 850
8efc2c16 851 *hibernate_location = (HibernateLocation) {
3595a9b7 852 .devno = swap_devno,
8efc2c16
ZJS
853 .offset = swap_offset,
854 .swap = TAKE_PTR(swap),
855 };
7bdf56a2 856
8efc2c16
ZJS
857 /* if the swap is the resume device, stop the loop */
858 if (location_is_resume_device(hibernate_location, sys_resume, sys_offset)) {
8601ecbc 859 log_debug("%s: device matches configured resume settings.", hibernate_location->swap->path);
8efc2c16
ZJS
860 resume_match = true;
861 break;
88bc86fc 862 }
8efc2c16 863
8601ecbc 864 log_debug("%s: is a candidate device.", hibernate_location->swap->path);
69ab8088
ZJS
865 }
866
8efc2c16
ZJS
867 /* We found nothing at all */
868 if (!hibernate_location)
869 return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS),
870 "No possible swap partitions or files suitable for hibernation were found in /proc/swaps.");
52133271 871
8efc2c16
ZJS
872 /* resume= is set but a matching /proc/swaps entry was not found */
873 if (sys_resume != 0 && !resume_match)
874 return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS),
875 "No swap partitions or files matching resume config were found in /proc/swaps.");
52133271 876
8efc2c16
ZJS
877 if (hibernate_location->offset == UINT64_MAX) {
878 if (sys_offset == 0)
879 return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS), "Offset detection failed and /sys/power/resume_offset is not set.");
7bdf56a2 880
8efc2c16
ZJS
881 hibernate_location->offset = sys_offset;
882 }
7bdf56a2 883
52133271
ZS
884 if (resume_match)
885 log_debug("Hibernation will attempt to use swap entry with path: %s, device: %u:%u, offset: %" PRIu64 ", priority: %i",
8601ecbc 886 hibernate_location->swap->path, major(hibernate_location->devno), minor(hibernate_location->devno),
52133271
ZS
887 hibernate_location->offset, hibernate_location->swap->priority);
888 else
8efc2c16 889 log_debug("/sys/power/resume is not configured; attempting to hibernate with path: %s, device: %u:%u, offset: %" PRIu64 ", priority: %i",
8601ecbc 890 hibernate_location->swap->path, major(hibernate_location->devno), minor(hibernate_location->devno),
52133271 891 hibernate_location->offset, hibernate_location->swap->priority);
88bc86fc 892
7bdf56a2 893 *ret_hibernate_location = TAKE_PTR(hibernate_location);
88bc86fc 894
52133271 895 if (resume_match)
7bdf56a2 896 return 1;
88bc86fc
ZS
897
898 return 0;
9fb3675e
ZJS
899}
900
4638cd39 901static bool enough_swap_for_hibernation(void) {
9fb3675e 902 _cleanup_free_ char *active = NULL;
7bdf56a2 903 _cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
39883f62 904 unsigned long long act = 0;
9fb3675e
ZJS
905 int r;
906
490d20e6
VV
907 if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
908 return true;
909
7bdf56a2 910 r = find_hibernate_location(&hibernate_location);
9fb3675e 911 if (r < 0)
69ab8088 912 return false;
69ab8088 913
52133271
ZS
914 /* If /sys/power/{resume,resume_offset} is configured but a matching entry
915 * could not be identified in /proc/swaps, user is likely using Btrfs with a swapfile;
916 * return true and let the system attempt hibernation.
917 */
918 if (r > 0 && !hibernate_location) {
919 log_debug("Unable to determine remaining swap space; hibernation may fail");
920 return true;
921 }
922
923 if (!hibernate_location)
924 return false;
925
c4cd1d4d 926 r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
69ab8088 927 if (r < 0) {
904865b8 928 log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
69ab8088
ZJS
929 return false;
930 }
931
932 r = safe_atollu(active, &act);
933 if (r < 0) {
904865b8 934 log_debug_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active);
69ab8088
ZJS
935 return false;
936 }
937
7bdf56a2 938 r = act <= (hibernate_location->swap->size - hibernate_location->swap->used) * HIBERNATION_SWAP_THRESHOLD;
88bc86fc 939 log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%" PRIu64 " kB, used=%" PRIu64 " kB, threshold=%.2g%%",
7bdf56a2 940 r ? "Enough" : "Not enough", act, hibernate_location->swap->size, hibernate_location->swap->used, 100*HIBERNATION_SWAP_THRESHOLD);
69ab8088
ZJS
941
942 return r;
943}
944
17c40b3a
ML
945int read_fiemap(int fd, struct fiemap **ret) {
946 _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL;
17c40b3a
ML
947 struct stat statinfo;
948 uint32_t result_extents = 0;
949 uint64_t fiemap_start = 0, fiemap_length;
6524f1a8 950 const size_t n_extra = DIV_ROUND_UP(sizeof(struct fiemap), sizeof(struct fiemap_extent));
17c40b3a
ML
951
952 if (fstat(fd, &statinfo) < 0)
953 return log_debug_errno(errno, "Cannot determine file size: %m");
954 if (!S_ISREG(statinfo.st_mode))
955 return -ENOTTY;
956 fiemap_length = statinfo.st_size;
957
6524f1a8
ZJS
958 /* Zero this out in case we run on a file with no extents */
959 fiemap = calloc(n_extra, sizeof(struct fiemap_extent));
17c40b3a
ML
960 if (!fiemap)
961 return -ENOMEM;
962
6524f1a8 963 result_fiemap = malloc_multiply(n_extra, sizeof(struct fiemap_extent));
17c40b3a
ML
964 if (!result_fiemap)
965 return -ENOMEM;
966
967 /* XFS filesystem has incorrect implementation of fiemap ioctl and
968 * returns extents for only one block-group at a time, so we need
969 * to handle it manually, starting the next fiemap call from the end
970 * of the last extent
971 */
972 while (fiemap_start < fiemap_length) {
973 *fiemap = (struct fiemap) {
974 .fm_start = fiemap_start,
975 .fm_length = fiemap_length,
976 .fm_flags = FIEMAP_FLAG_SYNC,
977 };
978
979 /* Find out how many extents there are */
980 if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
981 return log_debug_errno(errno, "Failed to read extents: %m");
982
983 /* Nothing to process */
984 if (fiemap->fm_mapped_extents == 0)
985 break;
986
6524f1a8
ZJS
987 /* Resize fiemap to allow us to read in the extents, result fiemap has to hold all
988 * the extents for the whole file. Add space for the initial struct fiemap. */
319a4f4b 989 if (!greedy_realloc0((void**) &fiemap, n_extra + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent)))
17c40b3a
ML
990 return -ENOMEM;
991
992 fiemap->fm_extent_count = fiemap->fm_mapped_extents;
993 fiemap->fm_mapped_extents = 0;
994
995 if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
996 return log_debug_errno(errno, "Failed to read extents: %m");
997
6524f1a8 998 /* Resize result_fiemap to allow us to copy in the extents */
319a4f4b 999 if (!greedy_realloc((void**) &result_fiemap,
6524f1a8 1000 n_extra + result_extents + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent)))
17c40b3a
ML
1001 return -ENOMEM;
1002
1003 memcpy(result_fiemap->fm_extents + result_extents,
1004 fiemap->fm_extents,
1005 sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents);
1006
1007 result_extents += fiemap->fm_mapped_extents;
1008
1009 /* Highly unlikely that it is zero */
6524f1a8 1010 if (_likely_(fiemap->fm_mapped_extents > 0)) {
17c40b3a
ML
1011 uint32_t i = fiemap->fm_mapped_extents - 1;
1012
1013 fiemap_start = fiemap->fm_extents[i].fe_logical +
1014 fiemap->fm_extents[i].fe_length;
1015
1016 if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST)
1017 break;
1018 }
1019 }
1020
1021 memcpy(result_fiemap, fiemap, sizeof(struct fiemap));
1022 result_fiemap->fm_mapped_extents = result_extents;
1023 *ret = TAKE_PTR(result_fiemap);
1024 return 0;
1025}
1026
1923373a
MY
1027int write_resume_config(dev_t devno, uint64_t offset, const char *device) {
1028 char offset_str[DECIMAL_STR_MAX(uint64_t)];
1029 _cleanup_free_ char *path = NULL;
1030 const char *devno_str;
1031 int r;
1032
1033 devno_str = FORMAT_DEVNUM(devno);
1034 xsprintf(offset_str, "%" PRIu64, offset);
1035
1036 if (!device) {
1037 r = device_path_make_canonical(S_IFBLK, devno, &path);
1038 if (r < 0)
1039 return log_error_errno(r,
1040 "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
1041 DEVNUM_FORMAT_VAL(devno));
1042 device = path;
1043 }
1044
1045 /* We write the offset first since it's safer. Note that this file is only available in 4.17+, so
1046 * fail gracefully if it doesn't exist and we're only overwriting it with 0. */
1047 r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
1048 if (r == -ENOENT) {
1049 if (offset != 0)
1050 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1051 "Can't configure hibernation offset %" PRIu64 ", kernel does not support /sys/power/resume_offset. Refusing.",
1052 offset);
1053
1054 log_warning_errno(r, "/sys/power/resume_offset is unavailable, skipping writing swap file offset.");
1055 } else if (r < 0)
1056 return log_error_errno(r,
1057 "Failed to write swap file offset %s to /sys/power/resume_offset for device '%s': %m",
1058 offset_str, device);
1059 else
1060 log_debug("Wrote resume_offset=%s for device '%s' to /sys/power/resume_offset.",
1061 offset_str, device);
1062
1063 r = write_string_file("/sys/power/resume", devno_str, WRITE_STRING_FILE_DISABLE_BUFFER);
1064 if (r < 0)
1065 return log_error_errno(r,
1066 "Failed to write device '%s' (%s) to /sys/power/resume: %m",
1067 device, devno_str);
1068 log_debug("Wrote resume=%s for device '%s' to /sys/power/resume.", devno_str, device);
1069
1070 return 0;
1071}
1072
c8cd8ca3 1073static int can_sleep_internal(const SleepConfig *sleep_config, SleepOperation operation, bool check_allowed);
e8f1d00d 1074
28ca9c24 1075static bool can_s2h(const SleepConfig *sleep_config) {
c8cd8ca3
LP
1076
1077 static const SleepOperation operations[] = {
1078 SLEEP_SUSPEND,
1079 SLEEP_HIBERNATE,
1080 };
1081
c58493c0
ML
1082 int r;
1083
1bbbefe7 1084 if (!clock_supported(CLOCK_BOOTTIME_ALARM)) {
c732e879 1085 log_debug("CLOCK_BOOTTIME_ALARM is not supported.");
c58493c0
ML
1086 return false;
1087 }
1088
c8cd8ca3
LP
1089 for (size_t i = 0; i < ELEMENTSOF(operations); i++) {
1090 r = can_sleep_internal(sleep_config, operations[i], false);
887b2019 1091 if (IN_SET(r, 0, -ENOSPC)) {
c8cd8ca3 1092 log_debug("Unable to %s system.", sleep_operation_to_string(operations[i]));
c863dc05
ZJS
1093 return false;
1094 }
b71c9758 1095 if (r < 0)
c8cd8ca3 1096 return log_debug_errno(r, "Failed to check if %s is possible: %m", sleep_operation_to_string(operations[i]));
c58493c0
ML
1097 }
1098
1099 return true;
1100}
1101
c8cd8ca3
LP
1102static int can_sleep_internal(
1103 const SleepConfig *sleep_config,
1104 SleepOperation operation,
1105 bool check_allowed) {
c58493c0 1106
c8cd8ca3
LP
1107 assert(operation >= 0);
1108 assert(operation < _SLEEP_OPERATION_MAX);
19adb8a3 1109
c8cd8ca3
LP
1110 if (check_allowed && !sleep_config->allow[operation]) {
1111 log_debug("Sleep mode \"%s\" is disabled by configuration.", sleep_operation_to_string(operation));
e8f1d00d
ZJS
1112 return false;
1113 }
1114
c8cd8ca3 1115 if (operation == SLEEP_SUSPEND_THEN_HIBERNATE)
28ca9c24 1116 return can_s2h(sleep_config);
e8f1d00d 1117
61dc8481
LP
1118 if (can_sleep_state(sleep_config->states[operation]) <= 0 ||
1119 can_sleep_disk(sleep_config->modes[operation]) <= 0)
69ab8088
ZJS
1120 return false;
1121
c8cd8ca3 1122 if (operation == SLEEP_SUSPEND)
b71c9758
ZJS
1123 return true;
1124
4638cd39 1125 if (!enough_swap_for_hibernation())
b71c9758
ZJS
1126 return -ENOSPC;
1127
1128 return true;
19adb8a3 1129}
e8f1d00d 1130
c8cd8ca3 1131int can_sleep(SleepOperation operation) {
28ca9c24
ZS
1132 _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
1133 int r;
1134
1135 r = parse_sleep_config(&sleep_config);
1136 if (r < 0)
1137 return r;
1138
c8cd8ca3 1139 return can_sleep_internal(sleep_config, operation, true);
28ca9c24
ZS
1140}
1141
1326de01 1142SleepConfig* free_sleep_config(SleepConfig *sc) {
28ca9c24 1143 if (!sc)
1326de01 1144 return NULL;
28ca9c24 1145
c8cd8ca3
LP
1146 for (SleepOperation i = 0; i < _SLEEP_OPERATION_MAX; i++) {
1147 strv_free(sc->modes[i]);
1148 strv_free(sc->states[i]);
1149 }
28ca9c24 1150
1326de01 1151 return mfree(sc);
e8f1d00d 1152}
be2a4b0d
LP
1153
1154static const char* const sleep_operation_table[_SLEEP_OPERATION_MAX] = {
1155 [SLEEP_SUSPEND] = "suspend",
1156 [SLEEP_HIBERNATE] = "hibernate",
1157 [SLEEP_HYBRID_SLEEP] = "hybrid-sleep",
1158 [SLEEP_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
1159};
1160
1161DEFINE_STRING_TABLE_LOOKUP(sleep_operation, SleepOperation);