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