1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2010-2017 Canonical
4 Copyright © 2018 Dell Inc.
10 #include <linux/fiemap.h>
13 #include <sys/types.h>
14 #include <sys/timerfd.h>
18 #include "sd-messages.h"
20 #include "btrfs-util.h"
22 #include "bus-error.h"
23 #include "bus-locator.h"
25 #include "constants.h"
26 #include "exec-util.h"
29 #include "format-util.h"
32 #include "main-func.h"
33 #include "parse-util.h"
34 #include "pretty-print.h"
35 #include "sleep-config.h"
37 #include "stdio-util.h"
38 #include "string-util.h"
40 #include "time-util.h"
42 static SleepOperation arg_operation
= _SLEEP_OPERATION_INVALID
;
44 static int write_hibernate_location_info(const HibernateLocation
*hibernate_location
) {
45 char offset_str
[DECIMAL_STR_MAX(uint64_t)];
46 char resume_str
[DECIMAL_STR_MAX(unsigned) * 2 + STRLEN(":")];
49 assert(hibernate_location
);
50 assert(hibernate_location
->swap
);
52 xsprintf(resume_str
, "%u:%u", major(hibernate_location
->devno
), minor(hibernate_location
->devno
));
53 r
= write_string_file("/sys/power/resume", resume_str
, WRITE_STRING_FILE_DISABLE_BUFFER
);
55 return log_debug_errno(r
, "Failed to write partition device to /sys/power/resume for '%s': '%s': %m",
56 hibernate_location
->swap
->device
, resume_str
);
58 log_debug("Wrote resume= value for %s to /sys/power/resume: %s", hibernate_location
->swap
->device
, resume_str
);
60 /* if it's a swap partition, we're done */
61 if (streq(hibernate_location
->swap
->type
, "partition"))
64 if (!streq(hibernate_location
->swap
->type
, "file"))
65 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
66 "Invalid hibernate type: %s", hibernate_location
->swap
->type
);
68 /* Only available in 4.17+ */
69 if (hibernate_location
->offset
> 0 && access("/sys/power/resume_offset", W_OK
) < 0) {
70 if (errno
== ENOENT
) {
71 log_debug("Kernel too old, can't configure resume_offset for %s, ignoring: %" PRIu64
,
72 hibernate_location
->swap
->device
, hibernate_location
->offset
);
76 return log_debug_errno(errno
, "/sys/power/resume_offset not writable: %m");
79 xsprintf(offset_str
, "%" PRIu64
, hibernate_location
->offset
);
80 r
= write_string_file("/sys/power/resume_offset", offset_str
, WRITE_STRING_FILE_DISABLE_BUFFER
);
82 return log_debug_errno(r
, "Failed to write swap file offset to /sys/power/resume_offset for '%s': '%s': %m",
83 hibernate_location
->swap
->device
, offset_str
);
85 log_debug("Wrote resume_offset= value for %s to /sys/power/resume_offset: %s", hibernate_location
->swap
->device
, offset_str
);
90 static int write_mode(char **modes
) {
93 STRV_FOREACH(mode
, modes
) {
96 k
= write_string_file("/sys/power/disk", *mode
, WRITE_STRING_FILE_DISABLE_BUFFER
);
100 log_debug_errno(k
, "Failed to write '%s' to /sys/power/disk: %m", *mode
);
108 static int write_state(FILE **f
, char **states
) {
114 STRV_FOREACH(state
, states
) {
117 k
= write_string_stream(*f
, *state
, WRITE_STRING_FILE_DISABLE_BUFFER
);
120 log_debug_errno(k
, "Failed to write '%s' to /sys/power/state: %m", *state
);
125 *f
= fopen("/sys/power/state", "we");
133 static int lock_all_homes(void) {
134 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
135 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
136 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
139 /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
140 * way the key material required to access these volumes is hopefully removed from memory. */
142 r
= sd_bus_open_system(&bus
);
144 return log_warning_errno(r
, "Failed to connect to system bus, ignoring: %m");
146 r
= sd_bus_message_new_method_call(
149 "org.freedesktop.home1",
150 "/org/freedesktop/home1",
151 "org.freedesktop.home1.Manager",
154 return bus_log_create_error(r
);
156 /* If homed is not running it can't have any home directories active either. */
157 r
= sd_bus_message_set_auto_start(m
, false);
159 return log_error_errno(r
, "Failed to disable auto-start of LockAllHomes() message: %m");
161 r
= sd_bus_call(bus
, m
, DEFAULT_TIMEOUT_USEC
, &error
, NULL
);
163 if (!bus_error_is_unknown_service(&error
))
164 return log_error_errno(r
, "Failed to lock home directories: %s", bus_error_message(&error
, r
));
166 log_debug("systemd-homed is not running, locking of home directories skipped.");
168 log_debug("Successfully requested locking of all home directories.");
173 const SleepConfig
*sleep_config
,
174 SleepOperation operation
,
175 const char *action
) {
177 char *arguments
[] = {
180 /* NB: we use 'arg_operation' instead of 'operation' here, as we want to communicate the overall
181 * operation here, not the specific one, in case of s2h. */
182 (char*) sleep_operation_to_string(arg_operation
),
185 static const char* const dirs
[] = {
190 _cleanup_(hibernate_location_freep
) HibernateLocation
*hibernate_location
= NULL
;
191 _cleanup_fclose_
FILE *f
= NULL
;
192 char **modes
, **states
;
195 assert(sleep_config
);
196 assert(operation
>= 0);
197 assert(operation
< _SLEEP_OPERATION_MAX
);
198 assert(operation
!= SLEEP_SUSPEND_THEN_HIBERNATE
); /* Handled by execute_s2h() instead */
200 states
= sleep_config
->states
[operation
];
201 modes
= sleep_config
->modes
[operation
];
203 if (strv_isempty(states
))
204 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
205 "No sleep states configured for sleep operation %s, can't sleep.",
206 sleep_operation_to_string(operation
));
208 /* This file is opened first, so that if we hit an error,
209 * we can abort before modifying any state. */
210 f
= fopen("/sys/power/state", "we");
212 return log_error_errno(errno
, "Failed to open /sys/power/state: %m");
214 setvbuf(f
, NULL
, _IONBF
, 0);
216 /* Configure hibernation settings if we are supposed to hibernate */
217 if (!strv_isempty(modes
)) {
218 r
= find_hibernate_location(&hibernate_location
);
220 return log_error_errno(r
, "Failed to find location to hibernate to: %m");
221 if (r
== 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
222 * do it ourselves then. > 0 means: kernel already had a configured hibernation
223 * location which we shouldn't touch. */
224 r
= write_hibernate_location_info(hibernate_location
);
226 return log_error_errno(r
, "Failed to prepare for hibernation: %m");
229 r
= write_mode(modes
);
231 return log_error_errno(r
, "Failed to write mode to /sys/power/disk: %m");
234 /* Pass an action string to the call-outs. This is mostly our operation string, except if the
235 * hibernate step of s-t-h fails, in which case we communicate that with a separate action. */
237 action
= sleep_operation_to_string(operation
);
239 r
= setenv("SYSTEMD_SLEEP_ACTION", action
, 1);
241 log_warning_errno(errno
, "Error setting SYSTEMD_SLEEP_ACTION=%s, ignoring: %m", action
);
243 (void) execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
, EXEC_DIR_PARALLEL
| EXEC_DIR_IGNORE_ERRORS
);
244 (void) lock_all_homes();
247 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR
,
248 LOG_MESSAGE("Entering sleep state '%s'...", sleep_operation_to_string(operation
)),
249 "SLEEP=%s", sleep_operation_to_string(arg_operation
));
251 r
= write_state(&f
, states
);
253 log_struct_errno(LOG_ERR
, r
,
254 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
255 LOG_MESSAGE("Failed to put system to sleep. System resumed again: %m"),
256 "SLEEP=%s", sleep_operation_to_string(arg_operation
));
259 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
260 LOG_MESSAGE("System returned from sleep state."),
261 "SLEEP=%s", sleep_operation_to_string(arg_operation
));
263 arguments
[1] = (char*) "post";
264 (void) execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
, EXEC_DIR_PARALLEL
| EXEC_DIR_IGNORE_ERRORS
);
269 static int custom_timer_suspend(const SleepConfig
*sleep_config
) {
270 _cleanup_hashmap_free_ Hashmap
*last_capacity
= NULL
, *current_capacity
= NULL
;
273 assert(sleep_config
);
275 while (battery_is_low() == 0) {
276 _cleanup_close_
int tfd
= -1;
277 struct itimerspec ts
= {};
278 usec_t suspend_interval
= sleep_config
->hibernate_delay_sec
, before_timestamp
= 0, after_timestamp
= 0, total_suspend_interval
;
281 tfd
= timerfd_create(CLOCK_BOOTTIME_ALARM
, TFD_NONBLOCK
|TFD_CLOEXEC
);
283 return log_error_errno(errno
, "Error creating timerfd: %m");
285 /* Store current battery capacity and current time before suspension */
286 r
= fetch_batteries_capacity_by_name(&last_capacity
);
288 before_timestamp
= now(CLOCK_BOOTTIME
);
289 else if (r
== -ENOENT
)
290 /* In case of no battery, system suspend interval will be set to HibernateDelaySec=. */
291 log_debug_errno(r
, "Suspend Interval value set to %s: %m", FORMAT_TIMESPAN(suspend_interval
, USEC_PER_SEC
));
293 return log_error_errno(r
, "Error fetching battery capacity percentage: %m");
295 r
= get_total_suspend_interval(last_capacity
, &total_suspend_interval
);
297 log_debug_errno(r
, "Failed to estimate suspend interval using previous discharge rate, ignoring: %m");
299 suspend_interval
= total_suspend_interval
;
301 log_debug("Set timerfd wake alarm for %s", FORMAT_TIMESPAN(suspend_interval
, USEC_PER_SEC
));
302 /* Wake alarm for system with or without battery to hibernate or estimate discharge rate whichever is applicable */
303 timespec_store(&ts
.it_value
, suspend_interval
);
305 if (timerfd_settime(tfd
, 0, &ts
, NULL
) < 0)
306 return log_error_errno(errno
, "Error setting battery estimate timer: %m");
308 r
= execute(sleep_config
, SLEEP_SUSPEND
, NULL
);
312 r
= fd_wait_for_event(tfd
, POLLIN
, 0);
314 return log_error_errno(r
, "Error polling timerfd: %m");
315 /* Store fd_wait status */
316 woken_by_timer
= FLAGS_SET(r
, POLLIN
);
318 r
= fetch_batteries_capacity_by_name(¤t_capacity
);
320 /* In case of no battery or error while getting charge level, no need to measure
321 * discharge rate. Instead system should wakeup if it is manual wakeup or
322 * hibernate if this is a timer wakeup. */
323 log_debug_errno(r
, "Battery capacity percentage unavailable, cannot estimate discharge rate: %m");
329 after_timestamp
= now(CLOCK_BOOTTIME
);
330 log_debug("Attempting to estimate battery discharge rate after wakeup from %s sleep", FORMAT_TIMESPAN(after_timestamp
- before_timestamp
, USEC_PER_HOUR
));
332 if (after_timestamp
!= before_timestamp
) {
333 r
= estimate_battery_discharge_rate_per_hour(last_capacity
, current_capacity
, before_timestamp
, after_timestamp
);
335 log_warning_errno(r
, "Failed to estimate and update battery discharge rate, ignoring: %m");
337 log_debug("System woke up too early to estimate discharge rate");
340 /* Return as manual wakeup done. This also will return in case battery was charged during suspension */
343 r
= check_wakeup_type();
345 log_debug_errno(r
, "Failed to check hardware wakeup type, ignoring: %m");
347 log_debug("wakeup type is APM timer");
348 /* system should hibernate */
356 /* Freeze when invoked and thaw on cleanup */
357 static int freeze_thaw_user_slice(const char **method
) {
358 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
359 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
362 if (!method
|| !*method
)
365 r
= bus_connect_system_systemd(&bus
);
367 return log_debug_errno(r
, "Failed to open connection to systemd: %m");
369 /* Wait for 1.5 seconds at maximum for freeze operation */
370 (void) sd_bus_set_method_call_timeout(bus
, 1500 * USEC_PER_MSEC
);
372 r
= bus_call_method(bus
, bus_systemd_mgr
, *method
, &error
, NULL
, "s", SPECIAL_USER_SLICE
);
374 return log_debug_errno(r
, "Failed to execute operation: %s", bus_error_message(&error
, r
));
379 static int execute_s2h(const SleepConfig
*sleep_config
) {
380 _unused_
_cleanup_(freeze_thaw_user_slice
) const char *auto_method_thaw
= "ThawUnit";
383 assert(sleep_config
);
385 r
= freeze_thaw_user_slice(&(const char*) { "FreezeUnit" });
387 log_debug_errno(r
, "Failed to freeze unit user.slice, ignoring: %m");
389 r
= check_wakeup_type();
391 log_debug_errno(r
, "Failed to check hardware wakeup type, ignoring: %m");
393 k
= battery_trip_point_alarm_exists();
395 log_debug_errno(k
, "Failed to check whether acpi_btp support is enabled or not, ignoring: %m");
397 if (r
>= 0 && k
> 0) {
398 log_debug("Attempting to suspend...");
399 r
= execute(sleep_config
, SLEEP_SUSPEND
, NULL
);
403 r
= check_wakeup_type();
405 return log_debug_errno(r
, "Failed to check hardware wakeup type: %m");
408 /* For APM Timer wakeup, system should hibernate else wakeup */
411 r
= custom_timer_suspend(sleep_config
);
413 return log_debug_errno(r
, "Suspend cycle with manual battery discharge rate estimation failed: %m");
418 /* For above custom timer, if 1 is returned, system will directly hibernate */
420 log_debug("Attempting to hibernate");
421 r
= execute(sleep_config
, SLEEP_HIBERNATE
, NULL
);
423 log_notice("Couldn't hibernate, will try to suspend again.");
425 r
= execute(sleep_config
, SLEEP_SUSPEND
, "suspend-after-failed-hibernate");
433 static int help(void) {
434 _cleanup_free_
char *link
= NULL
;
437 r
= terminal_urlify_man("systemd-suspend.service", "8", &link
);
441 printf("%s COMMAND\n\n"
442 "Suspend the system, hibernate the system, or both.\n\n"
443 " -h --help Show this help and exit\n"
444 " --version Print version string and exit\n"
446 " suspend Suspend the system\n"
447 " hibernate Hibernate the system\n"
448 " hybrid-sleep Both hibernate and suspend the system\n"
449 " suspend-then-hibernate Initially suspend and then hibernate\n"
450 " the system after a fixed period of time\n"
451 "\nSee the %s for details.\n",
452 program_invocation_short_name
,
458 static int parse_argv(int argc
, char *argv
[]) {
463 static const struct option options
[] = {
464 { "help", no_argument
, NULL
, 'h' },
465 { "version", no_argument
, NULL
, ARG_VERSION
},
474 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
486 assert_not_reached();
489 if (argc
- optind
!= 1)
490 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
492 program_invocation_short_name
);
494 arg_operation
= sleep_operation_from_string(argv
[optind
]);
495 if (arg_operation
< 0)
496 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown command '%s'.", argv
[optind
]);
498 return 1 /* work to do */;
501 static int run(int argc
, char *argv
[]) {
502 _cleanup_(free_sleep_configp
) SleepConfig
*sleep_config
= NULL
;
507 r
= parse_argv(argc
, argv
);
511 r
= parse_sleep_config(&sleep_config
);
515 if (!sleep_config
->allow
[arg_operation
])
516 return log_error_errno(SYNTHETIC_ERRNO(EACCES
),
517 "Sleep operation \"%s\" is disabled by configuration, refusing.",
518 sleep_operation_to_string(arg_operation
));
520 switch (arg_operation
) {
522 case SLEEP_SUSPEND_THEN_HIBERNATE
:
523 r
= execute_s2h(sleep_config
);
526 case SLEEP_HYBRID_SLEEP
:
527 r
= execute(sleep_config
, SLEEP_HYBRID_SLEEP
, NULL
);
529 /* If we can't hybrid sleep, then let's try to suspend at least. After all, the user
530 * asked us to do both: suspend + hibernate, and it's almost certainly the
531 * hibernation that failed, hence still do the other thing, the suspend. */
533 log_notice("Couldn't hybrid sleep, will try to suspend instead.");
535 r
= execute(sleep_config
, SLEEP_SUSPEND
, "suspend-after-failed-hybrid-sleep");
541 r
= execute(sleep_config
, arg_operation
, NULL
);
548 DEFINE_MAIN_FUNCTION(run
);