1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3   Copyright © 2010-2017 Canonical
4   Copyright © 2018 Dell Inc.
5 ***/
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <linux/fiemap.h>
11 #include <poll.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/timerfd.h>
15 #include <unistd.h>
16 
17 #include "sd-messages.h"
18 
19 #include "btrfs-util.h"
20 #include "bus-error.h"
21 #include "def.h"
22 #include "exec-util.h"
23 #include "fd-util.h"
24 #include "fileio.h"
25 #include "format-util.h"
26 #include "io-util.h"
27 #include "log.h"
28 #include "main-func.h"
29 #include "parse-util.h"
30 #include "pretty-print.h"
31 #include "sleep-config.h"
32 #include "stdio-util.h"
33 #include "string-util.h"
34 #include "strv.h"
35 #include "time-util.h"
36 #include "util.h"
37 
38 static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID;
39 
write_hibernate_location_info(const HibernateLocation * hibernate_location)40 static int write_hibernate_location_info(const HibernateLocation *hibernate_location) {
41         char offset_str[DECIMAL_STR_MAX(uint64_t)];
42         char resume_str[DECIMAL_STR_MAX(unsigned) * 2 + STRLEN(":")];
43         int r;
44 
45         assert(hibernate_location);
46         assert(hibernate_location->swap);
47 
48         xsprintf(resume_str, "%u:%u", major(hibernate_location->devno), minor(hibernate_location->devno));
49         r = write_string_file("/sys/power/resume", resume_str, WRITE_STRING_FILE_DISABLE_BUFFER);
50         if (r < 0)
51                 return log_debug_errno(r, "Failed to write partition device to /sys/power/resume for '%s': '%s': %m",
52                                        hibernate_location->swap->device, resume_str);
53 
54         log_debug("Wrote resume= value for %s to /sys/power/resume: %s", hibernate_location->swap->device, resume_str);
55 
56         /* if it's a swap partition, we're done */
57         if (streq(hibernate_location->swap->type, "partition"))
58                 return r;
59 
60         if (!streq(hibernate_location->swap->type, "file"))
61                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
62                                        "Invalid hibernate type: %s", hibernate_location->swap->type);
63 
64         /* Only available in 4.17+ */
65         if (hibernate_location->offset > 0 && access("/sys/power/resume_offset", W_OK) < 0) {
66                 if (errno == ENOENT) {
67                         log_debug("Kernel too old, can't configure resume_offset for %s, ignoring: %" PRIu64,
68                                   hibernate_location->swap->device, hibernate_location->offset);
69                         return 0;
70                 }
71 
72                 return log_debug_errno(errno, "/sys/power/resume_offset not writable: %m");
73         }
74 
75         xsprintf(offset_str, "%" PRIu64, hibernate_location->offset);
76         r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
77         if (r < 0)
78                 return log_debug_errno(r, "Failed to write swap file offset to /sys/power/resume_offset for '%s': '%s': %m",
79                                        hibernate_location->swap->device, offset_str);
80 
81         log_debug("Wrote resume_offset= value for %s to /sys/power/resume_offset: %s", hibernate_location->swap->device, offset_str);
82 
83         return 0;
84 }
85 
write_mode(char ** modes)86 static int write_mode(char **modes) {
87         int r = 0;
88 
89         STRV_FOREACH(mode, modes) {
90                 int k;
91 
92                 k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
93                 if (k >= 0)
94                         return 0;
95 
96                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
97                 if (r >= 0)
98                         r = k;
99         }
100 
101         return r;
102 }
103 
write_state(FILE ** f,char ** states)104 static int write_state(FILE **f, char **states) {
105         int r = 0;
106 
107         assert(f);
108         assert(*f);
109 
110         STRV_FOREACH(state, states) {
111                 int k;
112 
113                 k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER);
114                 if (k >= 0)
115                         return 0;
116                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state);
117                 if (r >= 0)
118                         r = k;
119 
120                 fclose(*f);
121                 *f = fopen("/sys/power/state", "we");
122                 if (!*f)
123                         return -errno;
124         }
125 
126         return r;
127 }
128 
lock_all_homes(void)129 static int lock_all_homes(void) {
130         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
131         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
132         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
133         int r;
134 
135         /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
136          * way the key material required to access these volumes is hopefully removed from memory. */
137 
138         r = sd_bus_open_system(&bus);
139         if (r < 0)
140                 return log_warning_errno(r, "Failed to connect to system bus, ignoring: %m");
141 
142         r = sd_bus_message_new_method_call(
143                         bus,
144                         &m,
145                         "org.freedesktop.home1",
146                         "/org/freedesktop/home1",
147                         "org.freedesktop.home1.Manager",
148                         "LockAllHomes");
149         if (r < 0)
150                 return bus_log_create_error(r);
151 
152         /* If homed is not running it can't have any home directories active either. */
153         r = sd_bus_message_set_auto_start(m, false);
154         if (r < 0)
155                 return log_error_errno(r, "Failed to disable auto-start of LockAllHomes() message: %m");
156 
157         r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC, &error, NULL);
158         if (r < 0) {
159                 if (!bus_error_is_unknown_service(&error))
160                         return log_error_errno(r, "Failed to lock home directories: %s", bus_error_message(&error, r));
161 
162                 log_debug("systemd-homed is not running, locking of home directories skipped.");
163         } else
164                 log_debug("Successfully requested locking of all home directories.");
165         return 0;
166 }
167 
execute(const SleepConfig * sleep_config,SleepOperation operation,const char * action)168 static int execute(
169                 const SleepConfig *sleep_config,
170                 SleepOperation operation,
171                 const char *action) {
172 
173         char *arguments[] = {
174                 NULL,
175                 (char*) "pre",
176                 /* NB: we use 'arg_operation' instead of 'operation' here, as we want to communicate the overall
177                  * operation here, not the specific one, in case of s2h. */
178                 (char*) sleep_operation_to_string(arg_operation),
179                 NULL
180         };
181         static const char* const dirs[] = {
182                 SYSTEM_SLEEP_PATH,
183                 NULL
184         };
185 
186         _cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
187         _cleanup_fclose_ FILE *f = NULL;
188         char **modes, **states;
189         int r;
190 
191         assert(sleep_config);
192         assert(operation >= 0);
193         assert(operation < _SLEEP_OPERATION_MAX);
194         assert(operation != SLEEP_SUSPEND_THEN_HIBERNATE); /* Handled by execute_s2h() instead */
195 
196         states = sleep_config->states[operation];
197         modes = sleep_config->modes[operation];
198 
199         if (strv_isempty(states))
200                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
201                                        "No sleep states configured for sleep operation %s, can't sleep.",
202                                        sleep_operation_to_string(operation));
203 
204         /* This file is opened first, so that if we hit an error,
205          * we can abort before modifying any state. */
206         f = fopen("/sys/power/state", "we");
207         if (!f)
208                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
209 
210         setvbuf(f, NULL, _IONBF, 0);
211 
212         /* Configure hibernation settings if we are supposed to hibernate */
213         if (!strv_isempty(modes)) {
214                 r = find_hibernate_location(&hibernate_location);
215                 if (r < 0)
216                         return log_error_errno(r, "Failed to find location to hibernate to: %m");
217                 if (r == 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
218                                * do it ourselves then. > 0 means: kernel already had a configured hibernation
219                                * location which we shouldn't touch. */
220                         r = write_hibernate_location_info(hibernate_location);
221                         if (r < 0)
222                                 return log_error_errno(r, "Failed to prepare for hibernation: %m");
223                 }
224 
225                 r = write_mode(modes);
226                 if (r < 0)
227                         return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
228         }
229 
230         /* Pass an action string to the call-outs. This is mostly our operation string, except if the
231          * hibernate step of s-t-h fails, in which case we communicate that with a separate action. */
232         if (!action)
233                 action = sleep_operation_to_string(operation);
234 
235         r = setenv("SYSTEMD_SLEEP_ACTION", action, 1);
236         if (r != 0)
237                 log_warning_errno(errno, "Error setting SYSTEMD_SLEEP_ACTION=%s, ignoring: %m", action);
238 
239         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
240         (void) lock_all_homes();
241 
242         log_struct(LOG_INFO,
243                    "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
244                    LOG_MESSAGE("Entering sleep state '%s'...", sleep_operation_to_string(operation)),
245                    "SLEEP=%s", sleep_operation_to_string(arg_operation));
246 
247         r = write_state(&f, states);
248         if (r < 0)
249                 log_struct_errno(LOG_ERR, r,
250                                  "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
251                                  LOG_MESSAGE("Failed to put system to sleep. System resumed again: %m"),
252                                  "SLEEP=%s", sleep_operation_to_string(arg_operation));
253         else
254                 log_struct(LOG_INFO,
255                            "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
256                            LOG_MESSAGE("System returned from sleep state."),
257                            "SLEEP=%s", sleep_operation_to_string(arg_operation));
258 
259         arguments[1] = (char*) "post";
260         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
261 
262         return r;
263 }
264 
execute_s2h(const SleepConfig * sleep_config)265 static int execute_s2h(const SleepConfig *sleep_config) {
266         _cleanup_close_ int tfd = -1;
267         struct itimerspec ts = {};
268         int r;
269 
270         assert(sleep_config);
271 
272         tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
273         if (tfd < 0)
274                 return log_error_errno(errno, "Error creating timerfd: %m");
275 
276         log_debug("Set timerfd wake alarm for %s",
277                   FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
278 
279         timespec_store(&ts.it_value, sleep_config->hibernate_delay_sec);
280 
281         r = timerfd_settime(tfd, 0, &ts, NULL);
282         if (r < 0)
283                 return log_error_errno(errno, "Error setting hibernate timer: %m");
284 
285         r = execute(sleep_config, SLEEP_SUSPEND, NULL);
286         if (r < 0)
287                 return r;
288 
289         r = fd_wait_for_event(tfd, POLLIN, 0);
290         if (r < 0)
291                 return log_error_errno(r, "Error polling timerfd: %m");
292         if (!FLAGS_SET(r, POLLIN)) /* We woke up before the alarm time, we are done. */
293                 return 0;
294 
295         tfd = safe_close(tfd);
296 
297         /* If woken up after alarm time, hibernate */
298         log_debug("Attempting to hibernate after waking from %s timer",
299                   FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
300 
301         r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
302         if (r < 0) {
303                 log_notice("Couldn't hibernate, will try to suspend again.");
304 
305                 r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hibernate");
306                 if (r < 0)
307                         return r;
308         }
309 
310         return 0;
311 }
312 
help(void)313 static int help(void) {
314         _cleanup_free_ char *link = NULL;
315         int r;
316 
317         r = terminal_urlify_man("systemd-suspend.service", "8", &link);
318         if (r < 0)
319                 return log_oom();
320 
321         printf("%s COMMAND\n\n"
322                "Suspend the system, hibernate the system, or both.\n\n"
323                "  -h --help              Show this help and exit\n"
324                "  --version              Print version string and exit\n"
325                "\nCommands:\n"
326                "  suspend                Suspend the system\n"
327                "  hibernate              Hibernate the system\n"
328                "  hybrid-sleep           Both hibernate and suspend the system\n"
329                "  suspend-then-hibernate Initially suspend and then hibernate\n"
330                "                         the system after a fixed period of time\n"
331                "\nSee the %s for details.\n",
332                program_invocation_short_name,
333                link);
334 
335         return 0;
336 }
337 
parse_argv(int argc,char * argv[])338 static int parse_argv(int argc, char *argv[]) {
339         enum {
340                 ARG_VERSION = 0x100,
341         };
342 
343         static const struct option options[] = {
344                 { "help",         no_argument,       NULL, 'h'           },
345                 { "version",      no_argument,       NULL, ARG_VERSION   },
346                 {}
347         };
348 
349         int c;
350 
351         assert(argc >= 0);
352         assert(argv);
353 
354         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
355                 switch (c) {
356                 case 'h':
357                         return help();
358 
359                 case ARG_VERSION:
360                         return version();
361 
362                 case '?':
363                         return -EINVAL;
364 
365                 default:
366                         assert_not_reached();
367                 }
368 
369         if (argc - optind != 1)
370                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
371                                        "Usage: %s COMMAND",
372                                        program_invocation_short_name);
373 
374         arg_operation = sleep_operation_from_string(argv[optind]);
375         if (arg_operation < 0)
376                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command '%s'.", argv[optind]);
377 
378         return 1 /* work to do */;
379 }
380 
run(int argc,char * argv[])381 static int run(int argc, char *argv[]) {
382         _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
383         int r;
384 
385         log_setup();
386 
387         r = parse_argv(argc, argv);
388         if (r <= 0)
389                 return r;
390 
391         r = parse_sleep_config(&sleep_config);
392         if (r < 0)
393                 return r;
394 
395         if (!sleep_config->allow[arg_operation])
396                 return log_error_errno(SYNTHETIC_ERRNO(EACCES),
397                                        "Sleep operation \"%s\" is disabled by configuration, refusing.",
398                                        sleep_operation_to_string(arg_operation));
399 
400         switch (arg_operation) {
401 
402         case SLEEP_SUSPEND_THEN_HIBERNATE:
403                 r = execute_s2h(sleep_config);
404                 break;
405 
406         case SLEEP_HYBRID_SLEEP:
407                 r = execute(sleep_config, SLEEP_HYBRID_SLEEP, NULL);
408                 if (r < 0) {
409                         /* If we can't hybrid sleep, then let's try to suspend at least. After all, the user
410                          * asked us to do both: suspend + hibernate, and it's almost certainly the
411                          * hibernation that failed, hence still do the other thing, the suspend. */
412 
413                         log_notice("Couldn't hybrid sleep, will try to suspend instead.");
414 
415                         r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hybrid-sleep");
416                 }
417 
418                 break;
419 
420         default:
421                 r = execute(sleep_config, arg_operation, NULL);
422                 break;
423         }
424 
425         return r;
426 }
427 
428 DEFINE_MAIN_FUNCTION(run);
429