LCOV - code coverage report
Current view: top level - sleep - sleep.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 190 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 11 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 186 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       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 <stdio.h>
      13                 :            : #include <sys/stat.h>
      14                 :            : #include <sys/types.h>
      15                 :            : #include <sys/timerfd.h>
      16                 :            : #include <unistd.h>
      17                 :            : 
      18                 :            : #include "sd-messages.h"
      19                 :            : 
      20                 :            : #include "btrfs-util.h"
      21                 :            : #include "def.h"
      22                 :            : #include "exec-util.h"
      23                 :            : #include "fd-util.h"
      24                 :            : #include "format-util.h"
      25                 :            : #include "fileio.h"
      26                 :            : #include "log.h"
      27                 :            : #include "main-func.h"
      28                 :            : #include "parse-util.h"
      29                 :            : #include "pretty-print.h"
      30                 :            : #include "sleep-config.h"
      31                 :            : #include "stdio-util.h"
      32                 :            : #include "string-util.h"
      33                 :            : #include "strv.h"
      34                 :            : #include "time-util.h"
      35                 :            : #include "util.h"
      36                 :            : 
      37                 :            : static char* arg_verb = NULL;
      38                 :            : 
      39                 :          0 : STATIC_DESTRUCTOR_REGISTER(arg_verb, freep);
      40                 :            : 
      41                 :          0 : static int write_hibernate_location_info(void) {
      42                 :          0 :         _cleanup_free_ char *device = NULL, *type = NULL;
      43                 :          0 :         _cleanup_free_ struct fiemap *fiemap = NULL;
      44                 :            :         char offset_str[DECIMAL_STR_MAX(uint64_t)];
      45                 :            :         char device_str[DECIMAL_STR_MAX(uint64_t)];
      46                 :          0 :         _cleanup_close_ int fd = -1;
      47                 :            :         struct stat stb;
      48                 :            :         uint64_t offset;
      49                 :            :         int r;
      50                 :            : 
      51                 :          0 :         r = find_hibernate_location(&device, &type, NULL, NULL);
      52         [ #  # ]:          0 :         if (r < 0)
      53         [ #  # ]:          0 :                 return log_debug_errno(r, "Unable to find hibernation location: %m");
      54                 :            : 
      55                 :            :         /* if it's a swap partition, we just write the disk to /sys/power/resume */
      56         [ #  # ]:          0 :         if (streq(type, "partition")) {
      57                 :          0 :                 r = write_string_file("/sys/power/resume", device, WRITE_STRING_FILE_DISABLE_BUFFER);
      58         [ #  # ]:          0 :                 if (r < 0)
      59         [ #  # ]:          0 :                         return log_debug_errno(r, "Failed to write partition device to /sys/power/resume: %m");
      60                 :            : 
      61                 :          0 :                 return r;
      62                 :            :         }
      63         [ #  # ]:          0 :         if (!streq(type, "file"))
      64         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
      65                 :            :                                        "Invalid hibernate type: %s", type);
      66                 :            : 
      67                 :            :         /* Only available in 4.17+ */
      68         [ #  # ]:          0 :         if (access("/sys/power/resume_offset", W_OK) < 0) {
      69         [ #  # ]:          0 :                 if (errno == ENOENT) {
      70         [ #  # ]:          0 :                         log_debug("Kernel too old, can't configure resume offset, ignoring.");
      71                 :          0 :                         return 0;
      72                 :            :                 }
      73                 :            : 
      74         [ #  # ]:          0 :                 return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m");
      75                 :            :         }
      76                 :            : 
      77                 :          0 :         fd = open(device, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
      78         [ #  # ]:          0 :         if (fd < 0)
      79         [ #  # ]:          0 :                 return log_debug_errno(errno, "Unable to open '%s': %m", device);
      80                 :          0 :         r = fstat(fd, &stb);
      81         [ #  # ]:          0 :         if (r < 0)
      82         [ #  # ]:          0 :                 return log_debug_errno(errno, "Unable to stat %s: %m", device);
      83                 :            : 
      84                 :          0 :         r = btrfs_is_filesystem(fd);
      85         [ #  # ]:          0 :         if (r < 0)
      86         [ #  # ]:          0 :                 return log_error_errno(r, "Error checking %s for Btrfs filesystem: %m", device);
      87                 :            : 
      88         [ #  # ]:          0 :         if (r)
      89         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
      90                 :            :                                        "Unable to calculate swapfile offset when using Btrfs: %s", device);
      91                 :            : 
      92                 :          0 :         r = read_fiemap(fd, &fiemap);
      93         [ #  # ]:          0 :         if (r < 0)
      94         [ #  # ]:          0 :                 return log_debug_errno(r, "Unable to read extent map for '%s': %m", device);
      95         [ #  # ]:          0 :         if (fiemap->fm_mapped_extents == 0)
      96         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
      97                 :            :                                        "No extents found in '%s'", device);
      98                 :            : 
      99                 :          0 :         offset = fiemap->fm_extents[0].fe_physical / page_size();
     100         [ #  # ]:          0 :         xsprintf(offset_str, "%" PRIu64, offset);
     101                 :          0 :         r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
     102         [ #  # ]:          0 :         if (r < 0)
     103         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str);
     104                 :            : 
     105         [ #  # ]:          0 :         log_debug("Wrote calculated resume_offset value to /sys/power/resume_offset: %s", offset_str);
     106                 :            : 
     107         [ #  # ]:          0 :         xsprintf(device_str, "%lx", (unsigned long)stb.st_dev);
     108                 :          0 :         r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER);
     109         [ #  # ]:          0 :         if (r < 0)
     110         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to write device '%s': %m", device_str);
     111                 :            : 
     112         [ #  # ]:          0 :         log_debug("Wrote device id to /sys/power/resume: %s", device_str);
     113                 :            : 
     114                 :          0 :         return 0;
     115                 :            : }
     116                 :            : 
     117                 :          0 : static int write_mode(char **modes) {
     118                 :          0 :         int r = 0;
     119                 :            :         char **mode;
     120                 :            : 
     121   [ #  #  #  # ]:          0 :         STRV_FOREACH(mode, modes) {
     122                 :            :                 int k;
     123                 :            : 
     124                 :          0 :                 k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
     125         [ #  # ]:          0 :                 if (k >= 0)
     126                 :          0 :                         return 0;
     127                 :            : 
     128         [ #  # ]:          0 :                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
     129         [ #  # ]:          0 :                 if (r >= 0)
     130                 :          0 :                         r = k;
     131                 :            :         }
     132                 :            : 
     133                 :          0 :         return r;
     134                 :            : }
     135                 :            : 
     136                 :          0 : static int write_state(FILE **f, char **states) {
     137                 :            :         char **state;
     138                 :          0 :         int r = 0;
     139                 :            : 
     140   [ #  #  #  # ]:          0 :         STRV_FOREACH(state, states) {
     141                 :            :                 int k;
     142                 :            : 
     143                 :          0 :                 k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER);
     144         [ #  # ]:          0 :                 if (k >= 0)
     145                 :          0 :                         return 0;
     146         [ #  # ]:          0 :                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state);
     147         [ #  # ]:          0 :                 if (r >= 0)
     148                 :          0 :                         r = k;
     149                 :            : 
     150                 :          0 :                 fclose(*f);
     151                 :          0 :                 *f = fopen("/sys/power/state", "we");
     152         [ #  # ]:          0 :                 if (!*f)
     153                 :          0 :                         return -errno;
     154                 :            :         }
     155                 :            : 
     156                 :          0 :         return r;
     157                 :            : }
     158                 :            : 
     159                 :          0 : static int configure_hibernation(void) {
     160                 :          0 :         _cleanup_free_ char *resume = NULL, *resume_offset = NULL;
     161                 :            :         int r;
     162                 :            : 
     163                 :            :         /* check for proper hibernation configuration */
     164                 :          0 :         r = read_one_line_file("/sys/power/resume", &resume);
     165         [ #  # ]:          0 :         if (r < 0)
     166         [ #  # ]:          0 :                 return log_debug_errno(r, "Error reading from /sys/power/resume: %m");
     167                 :            : 
     168                 :          0 :         r = read_one_line_file("/sys/power/resume_offset", &resume_offset);
     169         [ #  # ]:          0 :         if (r < 0)
     170         [ #  # ]:          0 :                 return log_debug_errno(r, "Error reading from /sys/power/resume_offset: %m");
     171                 :            : 
     172   [ #  #  #  # ]:          0 :         if (!streq(resume_offset, "0") && !streq(resume, "0:0")) {
     173         [ #  # ]:          0 :                 log_debug("Hibernating using device id and offset read from /sys/power/resume: %s and /sys/power/resume_offset: %s", resume, resume_offset);
     174                 :          0 :                 return 0;
     175         [ #  # ]:          0 :         } else if (!streq(resume, "0:0")) {
     176         [ #  # ]:          0 :                 log_debug("Hibernating using device id read from /sys/power/resume: %s", resume);
     177                 :          0 :                 return 0;
     178         [ #  # ]:          0 :         } else if (!streq(resume_offset, "0"))
     179         [ #  # ]:          0 :                 log_debug("Found offset in /sys/power/resume_offset: %s; no device id found in /sys/power/resume; ignoring offset", resume_offset);
     180                 :            : 
     181                 :            :         /* if hibernation is not properly configured, attempt to calculate and write values */
     182                 :          0 :         return write_hibernate_location_info();
     183                 :            : }
     184                 :            : 
     185                 :          0 : static int execute(char **modes, char **states) {
     186                 :          0 :         char *arguments[] = {
     187                 :            :                 NULL,
     188                 :            :                 (char*) "pre",
     189                 :            :                 arg_verb,
     190                 :            :                 NULL
     191                 :            :         };
     192                 :            :         static const char* const dirs[] = {
     193                 :            :                 SYSTEM_SLEEP_PATH,
     194                 :            :                 NULL
     195                 :            :         };
     196                 :            : 
     197                 :            :         int r;
     198                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
     199                 :            : 
     200                 :            :         /* This file is opened first, so that if we hit an error,
     201                 :            :          * we can abort before modifying any state. */
     202                 :          0 :         f = fopen("/sys/power/state", "we");
     203         [ #  # ]:          0 :         if (!f)
     204         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
     205                 :            : 
     206                 :          0 :         setvbuf(f, NULL, _IONBF, 0);
     207                 :            : 
     208                 :            :         /* Configure the hibernation mode */
     209         [ #  # ]:          0 :         if (!strv_isempty(modes)) {
     210                 :          0 :                 r = configure_hibernation();
     211         [ #  # ]:          0 :                 if (r < 0)
     212         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to prepare for hibernation: %m");
     213                 :            : 
     214                 :          0 :                 r = write_mode(modes);
     215         [ #  # ]:          0 :                 if (r < 0)
     216         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
     217                 :            :         }
     218                 :            : 
     219                 :          0 :         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
     220                 :            : 
     221                 :          0 :         log_struct(LOG_INFO,
     222                 :            :                    "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
     223                 :            :                    LOG_MESSAGE("Suspending system..."),
     224                 :            :                    "SLEEP=%s", arg_verb);
     225                 :            : 
     226                 :          0 :         r = write_state(&f, states);
     227         [ #  # ]:          0 :         if (r < 0)
     228                 :          0 :                 log_struct_errno(LOG_ERR, r,
     229                 :            :                                  "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
     230                 :            :                                  LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
     231                 :            :                                  "SLEEP=%s", arg_verb);
     232                 :            :         else
     233                 :          0 :                 log_struct(LOG_INFO,
     234                 :            :                            "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
     235                 :            :                            LOG_MESSAGE("System resumed."),
     236                 :            :                            "SLEEP=%s", arg_verb);
     237                 :            : 
     238                 :          0 :         arguments[1] = (char*) "post";
     239                 :          0 :         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
     240                 :            : 
     241                 :          0 :         return r;
     242                 :            : }
     243                 :            : 
     244                 :          0 : static int execute_s2h(const SleepConfig *sleep_config) {
     245                 :          0 :         _cleanup_close_ int tfd = -1;
     246                 :            :         char buf[FORMAT_TIMESPAN_MAX];
     247                 :          0 :         struct itimerspec ts = {};
     248                 :            :         struct pollfd fds;
     249                 :            :         int r;
     250                 :            : 
     251         [ #  # ]:          0 :         assert(sleep_config);
     252                 :            : 
     253                 :          0 :         tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
     254         [ #  # ]:          0 :         if (tfd < 0)
     255         [ #  # ]:          0 :                 return log_error_errno(errno, "Error creating timerfd: %m");
     256                 :            : 
     257         [ #  # ]:          0 :         log_debug("Set timerfd wake alarm for %s",
     258                 :            :                   format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
     259                 :            : 
     260                 :          0 :         timespec_store(&ts.it_value, sleep_config->hibernate_delay_sec);
     261                 :            : 
     262                 :          0 :         r = timerfd_settime(tfd, 0, &ts, NULL);
     263         [ #  # ]:          0 :         if (r < 0)
     264         [ #  # ]:          0 :                 return log_error_errno(errno, "Error setting hibernate timer: %m");
     265                 :            : 
     266                 :          0 :         r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
     267         [ #  # ]:          0 :         if (r < 0)
     268                 :          0 :                 return r;
     269                 :            : 
     270                 :          0 :         fds = (struct pollfd) {
     271                 :            :                 .fd = tfd,
     272                 :            :                 .events = POLLIN,
     273                 :            :         };
     274                 :          0 :         r = poll(&fds, 1, 0);
     275         [ #  # ]:          0 :         if (r < 0)
     276         [ #  # ]:          0 :                 return log_error_errno(errno, "Error polling timerfd: %m");
     277                 :            : 
     278                 :          0 :         tfd = safe_close(tfd);
     279                 :            : 
     280         [ #  # ]:          0 :         if (!FLAGS_SET(fds.revents, POLLIN)) /* We woke up before the alarm time, we are done. */
     281                 :          0 :                 return 0;
     282                 :            : 
     283                 :            :         /* If woken up after alarm time, hibernate */
     284         [ #  # ]:          0 :         log_debug("Attempting to hibernate after waking from %s timer",
     285                 :            :                   format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
     286                 :            : 
     287                 :          0 :         r = execute(sleep_config->hibernate_modes, sleep_config->hibernate_states);
     288         [ #  # ]:          0 :         if (r < 0) {
     289         [ #  # ]:          0 :                 log_notice("Couldn't hibernate, will try to suspend again.");
     290                 :          0 :                 r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
     291         [ #  # ]:          0 :                 if (r < 0) {
     292         [ #  # ]:          0 :                         log_notice("Could neither hibernate nor suspend again, giving up.");
     293                 :          0 :                         return r;
     294                 :            :                 }
     295                 :            :         }
     296                 :            : 
     297                 :          0 :         return 0;
     298                 :            : }
     299                 :            : 
     300                 :          0 : static int help(void) {
     301                 :          0 :         _cleanup_free_ char *link = NULL;
     302                 :            :         int r;
     303                 :            : 
     304                 :          0 :         r = terminal_urlify_man("systemd-suspend.service", "8", &link);
     305         [ #  # ]:          0 :         if (r < 0)
     306                 :          0 :                 return log_oom();
     307                 :            : 
     308                 :          0 :         printf("%s COMMAND\n\n"
     309                 :            :                "Suspend the system, hibernate the system, or both.\n\n"
     310                 :            :                "  -h --help              Show this help and exit\n"
     311                 :            :                "  --version              Print version string and exit\n"
     312                 :            :                "\nCommands:\n"
     313                 :            :                "  suspend                Suspend the system\n"
     314                 :            :                "  hibernate              Hibernate the system\n"
     315                 :            :                "  hybrid-sleep           Both hibernate and suspend the system\n"
     316                 :            :                "  suspend-then-hibernate Initially suspend and then hibernate\n"
     317                 :            :                "                         the system after a fixed period of time\n"
     318                 :            :                "\nSee the %s for details.\n"
     319                 :            :                , program_invocation_short_name
     320                 :            :                , link
     321                 :            :         );
     322                 :            : 
     323                 :          0 :         return 0;
     324                 :            : }
     325                 :            : 
     326                 :          0 : static int parse_argv(int argc, char *argv[]) {
     327                 :            :         enum {
     328                 :            :                 ARG_VERSION = 0x100,
     329                 :            :         };
     330                 :            : 
     331                 :            :         static const struct option options[] = {
     332                 :            :                 { "help",         no_argument,       NULL, 'h'           },
     333                 :            :                 { "version",      no_argument,       NULL, ARG_VERSION   },
     334                 :            :                 {}
     335                 :            :         };
     336                 :            : 
     337                 :            :         int c;
     338                 :            : 
     339         [ #  # ]:          0 :         assert(argc >= 0);
     340         [ #  # ]:          0 :         assert(argv);
     341                 :            : 
     342         [ #  # ]:          0 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     343   [ #  #  #  # ]:          0 :                 switch(c) {
     344                 :          0 :                 case 'h':
     345                 :          0 :                         return help();
     346                 :            : 
     347                 :          0 :                 case ARG_VERSION:
     348                 :          0 :                         return version();
     349                 :            : 
     350                 :          0 :                 case '?':
     351                 :          0 :                         return -EINVAL;
     352                 :            : 
     353                 :          0 :                 default:
     354                 :          0 :                         assert_not_reached("Unhandled option");
     355                 :            :                 }
     356                 :            : 
     357         [ #  # ]:          0 :         if (argc - optind != 1)
     358         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     359                 :            :                                        "Usage: %s COMMAND",
     360                 :            :                                        program_invocation_short_name);
     361                 :            : 
     362                 :          0 :         arg_verb = strdup(argv[optind]);
     363         [ #  # ]:          0 :         if (!arg_verb)
     364                 :          0 :                 return log_oom();
     365                 :            : 
     366         [ #  # ]:          0 :         if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
     367         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     368                 :            :                                        "Unknown command '%s'.", arg_verb);
     369                 :            : 
     370                 :          0 :         return 1 /* work to do */;
     371                 :            : }
     372                 :            : 
     373                 :          0 : static int run(int argc, char *argv[]) {
     374                 :            :         bool allow;
     375                 :          0 :         char **modes = NULL, **states = NULL;
     376                 :          0 :         _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
     377                 :            :         int r;
     378                 :            : 
     379                 :          0 :         log_setup_service();
     380                 :            : 
     381                 :          0 :         r = parse_argv(argc, argv);
     382         [ #  # ]:          0 :         if (r <= 0)
     383                 :          0 :                 return r;
     384                 :            : 
     385                 :          0 :         r = parse_sleep_config(&sleep_config);
     386         [ #  # ]:          0 :         if (r < 0)
     387                 :          0 :                 return r;
     388                 :            : 
     389                 :          0 :         r = sleep_settings(arg_verb, sleep_config, &allow, &modes, &states);
     390         [ #  # ]:          0 :         if (r < 0)
     391                 :          0 :                 return r;
     392                 :            : 
     393         [ #  # ]:          0 :         if (!allow)
     394         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EACCES),
     395                 :            :                                        "Sleep mode \"%s\" is disabled by configuration, refusing.",
     396                 :            :                                        arg_verb);
     397                 :            : 
     398         [ #  # ]:          0 :         if (streq(arg_verb, "suspend-then-hibernate"))
     399                 :          0 :                 return execute_s2h(sleep_config);
     400                 :            :         else
     401                 :          0 :                 return execute(modes, states);
     402                 :            : }
     403                 :            : 
     404                 :          0 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14