LCOV - code coverage report
Current view: top level - shared - sleep-config.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 138 264 52.3 %
Date: 2019-08-23 13:36:53 Functions: 9 13 69.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 62 248 25.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2018 Dell Inc.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <errno.h>
       7                 :            : #include <fcntl.h>
       8                 :            : #include <linux/fs.h>
       9                 :            : #include <stdbool.h>
      10                 :            : #include <stddef.h>
      11                 :            : #include <stdio.h>
      12                 :            : #include <string.h>
      13                 :            : #include <sys/ioctl.h>
      14                 :            : #include <sys/stat.h>
      15                 :            : #include <sys/types.h>
      16                 :            : #include <syslog.h>
      17                 :            : #include <unistd.h>
      18                 :            : 
      19                 :            : #include "alloc-util.h"
      20                 :            : #include "conf-parser.h"
      21                 :            : #include "def.h"
      22                 :            : #include "env-util.h"
      23                 :            : #include "errno-util.h"
      24                 :            : #include "fd-util.h"
      25                 :            : #include "fileio.h"
      26                 :            : #include "log.h"
      27                 :            : #include "macro.h"
      28                 :            : #include "parse-util.h"
      29                 :            : #include "path-util.h"
      30                 :            : #include "sleep-config.h"
      31                 :            : #include "string-util.h"
      32                 :            : #include "strv.h"
      33                 :            : #include "time-util.h"
      34                 :            : 
      35                 :         20 : int parse_sleep_config(SleepConfig **ret_sleep_config) {
      36                 :         20 :         _cleanup_(free_sleep_configp) SleepConfig *sc;
      37                 :         20 :         int allow_suspend = -1, allow_hibernate = -1,
      38                 :         20 :             allow_s2h = -1, allow_hybrid_sleep = -1;
      39                 :            : 
      40                 :         20 :         sc = new0(SleepConfig, 1);
      41         [ -  + ]:         20 :         if (!sc)
      42                 :          0 :                 return log_oom();
      43                 :            : 
      44                 :        160 :         const ConfigTableItem items[] = {
      45                 :            :                 { "Sleep", "AllowSuspend",              config_parse_tristate, 0, &allow_suspend },
      46                 :            :                 { "Sleep", "AllowHibernation",          config_parse_tristate, 0, &allow_hibernate },
      47                 :            :                 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
      48                 :            :                 { "Sleep", "AllowHybridSleep",          config_parse_tristate, 0, &allow_hybrid_sleep },
      49                 :            : 
      50                 :         20 :                 { "Sleep", "SuspendMode",               config_parse_strv, 0, &sc->suspend_modes  },
      51                 :         20 :                 { "Sleep", "SuspendState",              config_parse_strv, 0, &sc->suspend_states },
      52                 :         20 :                 { "Sleep", "HibernateMode",             config_parse_strv, 0, &sc->hibernate_modes  },
      53                 :         20 :                 { "Sleep", "HibernateState",            config_parse_strv, 0, &sc->hibernate_states },
      54                 :         20 :                 { "Sleep", "HybridSleepMode",           config_parse_strv, 0, &sc->hybrid_modes  },
      55                 :         20 :                 { "Sleep", "HybridSleepState",          config_parse_strv, 0, &sc->hybrid_states },
      56                 :            : 
      57                 :         20 :                 { "Sleep", "HibernateDelaySec",         config_parse_sec,  0, &sc->hibernate_delay_sec},
      58                 :            :                 {}
      59                 :            :         };
      60                 :            : 
      61                 :         20 :         (void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
      62                 :            :                                         CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
      63                 :            :                                         "Sleep\0", config_item_table_lookup, items,
      64                 :            :                                         CONFIG_PARSE_WARN, NULL);
      65                 :            : 
      66                 :            :         /* use default values unless set */
      67                 :         20 :         sc->allow_suspend = allow_suspend != 0;
      68                 :         20 :         sc->allow_hibernate = allow_hibernate != 0;
      69                 :         40 :         sc->allow_hybrid_sleep = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep
      70   [ +  -  +  -  :         20 :                 : (allow_suspend != 0 && allow_hibernate != 0);
                   +  - ]
      71                 :         40 :         sc->allow_s2h = allow_s2h >= 0 ? allow_s2h
      72   [ +  -  +  -  :         20 :                 : (allow_suspend != 0 && allow_hibernate != 0);
                   +  - ]
      73                 :            : 
      74         [ +  - ]:         20 :         if (!sc->suspend_states)
      75                 :         20 :                 sc->suspend_states = strv_new("mem", "standby", "freeze");
      76         [ +  - ]:         20 :         if (!sc->hibernate_modes)
      77                 :         20 :                 sc->hibernate_modes = strv_new("platform", "shutdown");
      78         [ +  - ]:         20 :         if (!sc->hibernate_states)
      79                 :         20 :                 sc->hibernate_states = strv_new("disk");
      80         [ +  - ]:         20 :         if (!sc->hybrid_modes)
      81                 :         20 :                 sc->hybrid_modes = strv_new("suspend", "platform", "shutdown");
      82         [ +  - ]:         20 :         if (!sc->hybrid_states)
      83                 :         20 :                 sc->hybrid_states = strv_new("disk");
      84         [ +  - ]:         20 :         if (sc->hibernate_delay_sec == 0)
      85                 :         20 :                 sc->hibernate_delay_sec = 2 * USEC_PER_HOUR;
      86                 :            : 
      87                 :            :         /* ensure values set for all required fields */
      88   [ +  -  +  - ]:         20 :         if (!sc->suspend_states || !sc->hibernate_modes
      89   [ +  -  +  -  :         20 :             || !sc->hibernate_states || !sc->hybrid_modes || !sc->hybrid_states)
                   -  + ]
      90                 :          0 :                 return log_oom();
      91                 :            : 
      92                 :         20 :         *ret_sleep_config = TAKE_PTR(sc);
      93                 :            : 
      94                 :         20 :         return 0;
      95                 :            : }
      96                 :            : 
      97                 :         32 : int can_sleep_state(char **types) {
      98                 :            :         char **type;
      99                 :            :         int r;
     100                 :         32 :         _cleanup_free_ char *p = NULL;
     101                 :            : 
     102         [ -  + ]:         32 :         if (strv_isempty(types))
     103                 :          0 :                 return true;
     104                 :            : 
     105                 :            :         /* If /sys is read-only we cannot sleep */
     106         [ +  - ]:         32 :         if (access("/sys/power/state", W_OK) < 0)
     107                 :         32 :                 return false;
     108                 :            : 
     109                 :          0 :         r = read_one_line_file("/sys/power/state", &p);
     110         [ #  # ]:          0 :         if (r < 0)
     111                 :          0 :                 return false;
     112                 :            : 
     113   [ #  #  #  # ]:          0 :         STRV_FOREACH(type, types) {
     114                 :            :                 const char *word, *state;
     115                 :            :                 size_t l, k;
     116                 :            : 
     117                 :          0 :                 k = strlen(*type);
     118         [ #  # ]:          0 :                 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
     119   [ #  #  #  # ]:          0 :                         if (l == k && memcmp(word, *type, l) == 0)
     120                 :          0 :                                 return true;
     121                 :            :         }
     122                 :            : 
     123                 :          0 :         return false;
     124                 :            : }
     125                 :            : 
     126                 :         16 : int can_sleep_disk(char **types) {
     127                 :            :         char **type;
     128                 :            :         int r;
     129                 :         16 :         _cleanup_free_ char *p = NULL;
     130                 :            : 
     131         [ -  + ]:         16 :         if (strv_isempty(types))
     132                 :          0 :                 return true;
     133                 :            : 
     134                 :            :         /* If /sys is read-only we cannot sleep */
     135         [ +  - ]:         16 :         if (access("/sys/power/disk", W_OK) < 0) {
     136         [ +  - ]:         16 :                 log_debug_errno(errno, "/sys/power/disk is not writable: %m");
     137                 :         16 :                 return false;
     138                 :            :         }
     139                 :            : 
     140                 :          0 :         r = read_one_line_file("/sys/power/disk", &p);
     141         [ #  # ]:          0 :         if (r < 0) {
     142         [ #  # ]:          0 :                 log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
     143                 :          0 :                 return false;
     144                 :            :         }
     145                 :            : 
     146   [ #  #  #  # ]:          0 :         STRV_FOREACH(type, types) {
     147                 :            :                 const char *word, *state;
     148                 :            :                 size_t l, k;
     149                 :            : 
     150                 :          0 :                 k = strlen(*type);
     151         [ #  # ]:          0 :                 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
     152   [ #  #  #  # ]:          0 :                         if (l == k && memcmp(word, *type, l) == 0)
     153                 :          0 :                                 return true;
     154                 :            : 
     155         [ #  # ]:          0 :                         if (l == k + 2 &&
     156         [ #  # ]:          0 :                             word[0] == '[' &&
     157         [ #  # ]:          0 :                             memcmp(word + 1, *type, l - 2) == 0 &&
     158         [ #  # ]:          0 :                             word[l-1] == ']')
     159                 :          0 :                                 return true;
     160                 :            :                 }
     161                 :            :         }
     162                 :            : 
     163                 :          0 :         return false;
     164                 :            : }
     165                 :            : 
     166                 :            : #define HIBERNATION_SWAP_THRESHOLD 0.98
     167                 :            : 
     168                 :            : /* entry in /proc/swaps */
     169                 :            : typedef struct SwapEntry {
     170                 :            :         char *device;
     171                 :            :         char *type;
     172                 :            :         uint64_t size;
     173                 :            :         uint64_t used;
     174                 :            :         int priority;
     175                 :            : } SwapEntry;
     176                 :            : 
     177                 :          0 : static SwapEntry* swap_entry_free(SwapEntry *se) {
     178         [ #  # ]:          0 :         if (!se)
     179                 :          0 :                 return NULL;
     180                 :            : 
     181                 :          0 :         free(se->device);
     182                 :          0 :         free(se->type);
     183                 :            : 
     184                 :          0 :         return mfree(se);
     185                 :            : }
     186                 :            : 
     187         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(SwapEntry*, swap_entry_free);
     188                 :            : 
     189                 :          0 : int find_hibernate_location(char **device, char **type, uint64_t *size, uint64_t *used) {
     190                 :          0 :         _cleanup_fclose_ FILE *f;
     191                 :          0 :         _cleanup_(swap_entry_freep) SwapEntry *selected_swap = NULL;
     192                 :            :         unsigned i;
     193                 :            : 
     194                 :          0 :         f = fopen("/proc/swaps", "re");
     195         [ #  # ]:          0 :         if (!f) {
     196   [ #  #  #  # ]:          0 :                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
     197                 :            :                          "Failed to retrieve open /proc/swaps: %m");
     198                 :          0 :                 return negative_errno();
     199                 :            :         }
     200                 :            : 
     201                 :          0 :         (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
     202                 :            : 
     203                 :          0 :         for (i = 1;; i++) {
     204   [ #  #  #  # ]:          0 :                 _cleanup_(swap_entry_freep) SwapEntry *swap = NULL;
     205                 :            :                 int k;
     206                 :            : 
     207                 :          0 :                 swap = new0(SwapEntry, 1);
     208         [ #  # ]:          0 :                 if (!swap)
     209                 :          0 :                         return log_oom();
     210                 :            : 
     211                 :          0 :                 k = fscanf(f,
     212                 :            :                            "%ms "       /* device/file */
     213                 :            :                            "%ms "       /* type of swap */
     214                 :            :                            "%" PRIu64   /* swap size */
     215                 :            :                            "%" PRIu64   /* used */
     216                 :            :                            "%i\n",      /* priority */
     217                 :          0 :                            &swap->device, &swap->type, &swap->size, &swap->used, &swap->priority);
     218         [ #  # ]:          0 :                 if (k == EOF)
     219                 :          0 :                         break;
     220         [ #  # ]:          0 :                 if (k != 5) {
     221         [ #  # ]:          0 :                         log_warning("Failed to parse /proc/swaps:%u", i);
     222                 :          0 :                         continue;
     223                 :            :                 }
     224                 :            : 
     225         [ #  # ]:          0 :                 if (streq(swap->type, "file")) {
     226                 :            : 
     227         [ #  # ]:          0 :                         if (endswith(swap->device, "\\040(deleted)")) {
     228         [ #  # ]:          0 :                                 log_warning("Ignoring deleted swap file '%s'.", swap->device);
     229                 :          0 :                                 continue;
     230                 :            :                         }
     231                 :            : 
     232         [ #  # ]:          0 :                 } else if (streq(swap->type, "partition")) {
     233                 :            :                         const char *fn;
     234                 :            : 
     235                 :          0 :                         fn = path_startswith(swap->device, "/dev/");
     236   [ #  #  #  # ]:          0 :                         if (fn && startswith(fn, "zram")) {
     237         [ #  # ]:          0 :                                 log_debug("Ignoring compressed RAM swap device '%s'.", swap->device);
     238                 :          0 :                                 continue;
     239                 :            :                         }
     240                 :            :                 }
     241                 :            : 
     242                 :            :                 /* prefer highest priority or swap with most remaining space when same priority */
     243   [ #  #  #  # ]:          0 :                 if (!selected_swap || swap->priority > selected_swap->priority
     244         [ #  # ]:          0 :                     || ((swap->priority == selected_swap->priority)
     245         [ #  # ]:          0 :                         && (swap->size - swap->used) > (selected_swap->size - selected_swap->used))) {
     246                 :          0 :                         selected_swap = swap_entry_free(selected_swap);
     247                 :          0 :                         selected_swap = TAKE_PTR(swap);
     248                 :            :                 }
     249                 :            :         }
     250                 :            : 
     251         [ #  # ]:          0 :         if (!selected_swap)
     252         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS), "No swap partitions or files were found.");
     253                 :            : 
     254                 :            :         /* use the swap entry with the highest priority */
     255         [ #  # ]:          0 :         if (device)
     256                 :          0 :                 *device = TAKE_PTR(selected_swap->device);
     257         [ #  # ]:          0 :         if (type)
     258                 :          0 :                 *type = TAKE_PTR(selected_swap->type);
     259         [ #  # ]:          0 :         if (size)
     260                 :          0 :                 *size = selected_swap->size;
     261         [ #  # ]:          0 :         if (used)
     262                 :          0 :                 *used = selected_swap->used;
     263                 :            : 
     264         [ #  # ]:          0 :         log_debug("Highest priority swap entry found %s: %i", selected_swap->device, selected_swap->priority);
     265                 :            : 
     266                 :          0 :         return 0;
     267                 :            : }
     268                 :            : 
     269                 :          0 : static bool enough_swap_for_hibernation(void) {
     270                 :          0 :         _cleanup_free_ char *active = NULL;
     271                 :          0 :         unsigned long long act = 0;
     272                 :          0 :         uint64_t size = 0, used = 0;
     273                 :            :         int r;
     274                 :            : 
     275         [ #  # ]:          0 :         if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
     276                 :          0 :                 return true;
     277                 :            : 
     278                 :          0 :         r = find_hibernate_location(NULL, NULL, &size, &used);
     279         [ #  # ]:          0 :         if (r < 0)
     280                 :          0 :                 return false;
     281                 :            : 
     282                 :          0 :         r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
     283         [ #  # ]:          0 :         if (r < 0) {
     284         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
     285                 :          0 :                 return false;
     286                 :            :         }
     287                 :            : 
     288                 :          0 :         r = safe_atollu(active, &act);
     289         [ #  # ]:          0 :         if (r < 0) {
     290         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active);
     291                 :          0 :                 return false;
     292                 :            :         }
     293                 :            : 
     294                 :          0 :         r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
     295   [ #  #  #  # ]:          0 :         log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%" PRIu64 " kB, used=%" PRIu64 " kB, threshold=%.2g%%",
     296                 :            :                   r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
     297                 :            : 
     298                 :          0 :         return r;
     299                 :            : }
     300                 :            : 
     301                 :          4 : int read_fiemap(int fd, struct fiemap **ret) {
     302                 :          4 :         _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL;
     303                 :            :         struct stat statinfo;
     304                 :          4 :         uint32_t result_extents = 0;
     305                 :          4 :         uint64_t fiemap_start = 0, fiemap_length;
     306                 :          4 :         const size_t n_extra = DIV_ROUND_UP(sizeof(struct fiemap), sizeof(struct fiemap_extent));
     307                 :          4 :         size_t fiemap_allocated = n_extra, result_fiemap_allocated = n_extra;
     308                 :            : 
     309         [ -  + ]:          4 :         if (fstat(fd, &statinfo) < 0)
     310         [ #  # ]:          0 :                 return log_debug_errno(errno, "Cannot determine file size: %m");
     311         [ -  + ]:          4 :         if (!S_ISREG(statinfo.st_mode))
     312                 :          0 :                 return -ENOTTY;
     313                 :          4 :         fiemap_length = statinfo.st_size;
     314                 :            : 
     315                 :            :         /* Zero this out in case we run on a file with no extents */
     316                 :          4 :         fiemap = calloc(n_extra, sizeof(struct fiemap_extent));
     317         [ -  + ]:          4 :         if (!fiemap)
     318                 :          0 :                 return -ENOMEM;
     319                 :            : 
     320                 :          4 :         result_fiemap = malloc_multiply(n_extra, sizeof(struct fiemap_extent));
     321         [ -  + ]:          4 :         if (!result_fiemap)
     322                 :          0 :                 return -ENOMEM;
     323                 :            : 
     324                 :            :         /*  XFS filesystem has incorrect implementation of fiemap ioctl and
     325                 :            :          *  returns extents for only one block-group at a time, so we need
     326                 :            :          *  to handle it manually, starting the next fiemap call from the end
     327                 :            :          *  of the last extent
     328                 :            :          */
     329         [ +  - ]:          4 :         while (fiemap_start < fiemap_length) {
     330                 :          4 :                 *fiemap = (struct fiemap) {
     331                 :            :                         .fm_start = fiemap_start,
     332                 :            :                         .fm_length = fiemap_length,
     333                 :            :                         .fm_flags = FIEMAP_FLAG_SYNC,
     334                 :            :                 };
     335                 :            : 
     336                 :            :                 /* Find out how many extents there are */
     337         [ -  + ]:          4 :                 if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
     338         [ #  # ]:          0 :                         return log_debug_errno(errno, "Failed to read extents: %m");
     339                 :            : 
     340                 :            :                 /* Nothing to process */
     341         [ -  + ]:          4 :                 if (fiemap->fm_mapped_extents == 0)
     342                 :          0 :                         break;
     343                 :            : 
     344                 :            :                 /* Resize fiemap to allow us to read in the extents, result fiemap has to hold all
     345                 :            :                  * the extents for the whole file. Add space for the initial struct fiemap. */
     346         [ -  + ]:          4 :                 if (!greedy_realloc0((void**) &fiemap, &fiemap_allocated,
     347                 :          4 :                                      n_extra + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent)))
     348                 :          0 :                         return -ENOMEM;
     349                 :            : 
     350                 :          4 :                 fiemap->fm_extent_count = fiemap->fm_mapped_extents;
     351                 :          4 :                 fiemap->fm_mapped_extents = 0;
     352                 :            : 
     353         [ -  + ]:          4 :                 if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
     354         [ #  # ]:          0 :                         return log_debug_errno(errno, "Failed to read extents: %m");
     355                 :            : 
     356                 :            :                 /* Resize result_fiemap to allow us to copy in the extents */
     357         [ -  + ]:          4 :                 if (!greedy_realloc((void**) &result_fiemap, &result_fiemap_allocated,
     358                 :          4 :                                     n_extra + result_extents + fiemap->fm_mapped_extents, sizeof(struct fiemap_extent)))
     359                 :          0 :                         return -ENOMEM;
     360                 :            : 
     361                 :          8 :                 memcpy(result_fiemap->fm_extents + result_extents,
     362                 :          4 :                        fiemap->fm_extents,
     363                 :          4 :                        sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents);
     364                 :            : 
     365                 :          4 :                 result_extents += fiemap->fm_mapped_extents;
     366                 :            : 
     367                 :            :                 /* Highly unlikely that it is zero */
     368         [ +  - ]:          4 :                 if (_likely_(fiemap->fm_mapped_extents > 0)) {
     369                 :          4 :                         uint32_t i = fiemap->fm_mapped_extents - 1;
     370                 :            : 
     371                 :          8 :                         fiemap_start = fiemap->fm_extents[i].fe_logical +
     372                 :          4 :                                        fiemap->fm_extents[i].fe_length;
     373                 :            : 
     374         [ +  - ]:          4 :                         if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST)
     375                 :          4 :                                 break;
     376                 :            :                 }
     377                 :            :         }
     378                 :            : 
     379                 :          4 :         memcpy(result_fiemap, fiemap, sizeof(struct fiemap));
     380                 :          4 :         result_fiemap->fm_mapped_extents = result_extents;
     381                 :          4 :         *ret = TAKE_PTR(result_fiemap);
     382                 :          4 :         return 0;
     383                 :            : }
     384                 :            : 
     385                 :            : static int can_sleep_internal(const char *verb, bool check_allowed, const SleepConfig *sleep_config);
     386                 :            : 
     387                 :          4 : static bool can_s2h(const SleepConfig *sleep_config) {
     388                 :            :         const char *p;
     389                 :            :         int r;
     390                 :            : 
     391         [ -  + ]:          4 :         if (!clock_supported(CLOCK_BOOTTIME_ALARM)) {
     392   [ #  #  #  # ]:          0 :                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
     393                 :            :                          "CLOCK_BOOTTIME_ALARM is not supported");
     394                 :          0 :                 return false;
     395                 :            :         }
     396                 :            : 
     397         [ +  - ]:          4 :         FOREACH_STRING(p, "suspend", "hibernate") {
     398                 :          4 :                 r = can_sleep_internal(p, false, sleep_config);
     399   [ +  -  +  - ]:          4 :                 if (IN_SET(r, 0, -ENOSPC, -EADV)) {
     400         [ +  - ]:          4 :                         log_debug("Unable to %s system.", p);
     401                 :          4 :                         return false;
     402                 :            :                 }
     403         [ #  # ]:          0 :                 if (r < 0)
     404         [ #  # ]:          0 :                         return log_debug_errno(r, "Failed to check if %s is possible: %m", p);
     405                 :            :         }
     406                 :            : 
     407                 :          0 :         return true;
     408                 :            : }
     409                 :            : 
     410                 :         20 : static int can_sleep_internal(const char *verb, bool check_allowed, const SleepConfig *sleep_config) {
     411                 :            :         bool allow;
     412                 :         20 :         char **modes = NULL, **states = NULL;
     413                 :            :         int r;
     414                 :            : 
     415         [ -  + ]:         20 :         assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
     416                 :            : 
     417                 :         20 :         r = sleep_settings(verb, sleep_config, &allow, &modes, &states);
     418         [ -  + ]:         20 :         if (r < 0)
     419                 :          0 :                 return false;
     420                 :            : 
     421   [ +  +  -  + ]:         20 :         if (check_allowed && !allow) {
     422         [ #  # ]:          0 :                 log_debug("Sleep mode \"%s\" is disabled by configuration.", verb);
     423                 :          0 :                 return false;
     424                 :            :         }
     425                 :            : 
     426         [ +  + ]:         20 :         if (streq(verb, "suspend-then-hibernate"))
     427                 :          4 :                 return can_s2h(sleep_config);
     428                 :            : 
     429   [ -  +  #  # ]:         16 :         if (!can_sleep_state(states) || !can_sleep_disk(modes))
     430                 :         16 :                 return false;
     431                 :            : 
     432         [ #  # ]:          0 :         if (streq(verb, "suspend"))
     433                 :          0 :                 return true;
     434                 :            : 
     435         [ #  # ]:          0 :         if (!enough_swap_for_hibernation())
     436                 :          0 :                 return -ENOSPC;
     437                 :            : 
     438                 :          0 :         return true;
     439                 :            : }
     440                 :            : 
     441                 :         16 : int can_sleep(const char *verb) {
     442                 :         16 :         _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
     443                 :            :         int r;
     444                 :            : 
     445                 :         16 :         r = parse_sleep_config(&sleep_config);
     446         [ -  + ]:         16 :         if (r < 0)
     447                 :          0 :                 return r;
     448                 :            : 
     449                 :         16 :         return can_sleep_internal(verb, true, sleep_config);
     450                 :            : }
     451                 :            : 
     452                 :         20 : int sleep_settings(const char *verb, const SleepConfig *sleep_config, bool *ret_allow, char ***ret_modes, char ***ret_states) {
     453                 :            : 
     454         [ -  + ]:         20 :         assert(verb);
     455         [ -  + ]:         20 :         assert(sleep_config);
     456         [ -  + ]:         20 :         assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
     457                 :            : 
     458         [ +  + ]:         20 :         if (streq(verb, "suspend")) {
     459                 :          8 :                 *ret_allow = sleep_config->allow_suspend;
     460                 :          8 :                 *ret_modes = sleep_config->suspend_modes;
     461                 :          8 :                 *ret_states = sleep_config->suspend_states;
     462         [ +  + ]:         12 :         } else if (streq(verb, "hibernate")) {
     463                 :          4 :                 *ret_allow = sleep_config->allow_hibernate;
     464                 :          4 :                 *ret_modes = sleep_config->hibernate_modes;
     465                 :          4 :                 *ret_states = sleep_config->hibernate_states;
     466         [ +  + ]:          8 :         } else if (streq(verb, "hybrid-sleep")) {
     467                 :          4 :                 *ret_allow = sleep_config->allow_hybrid_sleep;
     468                 :          4 :                 *ret_modes = sleep_config->hybrid_modes;
     469                 :          4 :                 *ret_states = sleep_config->hybrid_states;
     470         [ +  - ]:          4 :         } else if (streq(verb, "suspend-then-hibernate")) {
     471                 :          4 :                 *ret_allow = sleep_config->allow_s2h;
     472                 :          4 :                 *ret_modes = *ret_states = NULL;
     473                 :            :         }
     474                 :            : 
     475                 :            :         /* suspend modes empty by default */
     476   [ -  +  #  #  :         20 :         if ((!ret_modes && !streq(verb, "suspend")) || !ret_states)
                   -  + ]
     477         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No modes or states set for %s; Check sleep.conf", verb);
     478                 :            : 
     479                 :         20 :         return 0;
     480                 :            : }
     481                 :            : 
     482                 :         20 : void free_sleep_config(SleepConfig *sc) {
     483         [ -  + ]:         20 :         if (!sc)
     484                 :          0 :                 return;
     485                 :            : 
     486                 :         20 :         strv_free(sc->suspend_modes);
     487                 :         20 :         strv_free(sc->suspend_states);
     488                 :            : 
     489                 :         20 :         strv_free(sc->hibernate_modes);
     490                 :         20 :         strv_free(sc->hibernate_states);
     491                 :            : 
     492                 :         20 :         strv_free(sc->hybrid_modes);
     493                 :         20 :         strv_free(sc->hybrid_states);
     494                 :            : 
     495                 :         20 :         free(sc);
     496                 :            : }

Generated by: LCOV version 1.14