LCOV - code coverage report
Current view: top level - shared - sleep-config.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 41 122 33.6 %
Date: 2015-07-29 18:47:03 Functions: 4 6 66.7 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2013 Zbigniew Jędrzejewski-Szmek
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <stdio.h>
      23             : 
      24             : #include "conf-parser.h"
      25             : #include "sleep-config.h"
      26             : #include "fileio.h"
      27             : #include "log.h"
      28             : #include "strv.h"
      29             : #include "util.h"
      30             : 
      31             : #define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
      32             : 
      33           3 : int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
      34             : 
      35             :         _cleanup_strv_free_ char
      36           6 :                 **suspend_mode = NULL, **suspend_state = NULL,
      37           6 :                 **hibernate_mode = NULL, **hibernate_state = NULL,
      38           6 :                 **hybrid_mode = NULL, **hybrid_state = NULL;
      39             :         char **modes, **states;
      40             : 
      41           3 :         const ConfigTableItem items[] = {
      42             :                 { "Sleep",   "SuspendMode",      config_parse_strv,  0, &suspend_mode  },
      43             :                 { "Sleep",   "SuspendState",     config_parse_strv,  0, &suspend_state },
      44             :                 { "Sleep",   "HibernateMode",    config_parse_strv,  0, &hibernate_mode  },
      45             :                 { "Sleep",   "HibernateState",   config_parse_strv,  0, &hibernate_state },
      46             :                 { "Sleep",   "HybridSleepMode",  config_parse_strv,  0, &hybrid_mode  },
      47             :                 { "Sleep",   "HybridSleepState", config_parse_strv,  0, &hybrid_state },
      48             :                 {}
      49             :         };
      50             : 
      51           3 :         config_parse_many(PKGSYSCONFDIR "/sleep.conf",
      52             :                           CONF_DIRS_NULSTR("systemd/sleep.conf"),
      53             :                           "Sleep\0", config_item_table_lookup, items,
      54             :                           false, NULL);
      55             : 
      56           3 :         if (streq(verb, "suspend")) {
      57             :                 /* empty by default */
      58           1 :                 USE(modes, suspend_mode);
      59             : 
      60           1 :                 if (suspend_state)
      61           0 :                         USE(states, suspend_state);
      62             :                 else
      63           1 :                         states = strv_new("mem", "standby", "freeze", NULL);
      64             : 
      65           2 :         } else if (streq(verb, "hibernate")) {
      66           1 :                 if (hibernate_mode)
      67           0 :                         USE(modes, hibernate_mode);
      68             :                 else
      69           1 :                         modes = strv_new("platform", "shutdown", NULL);
      70             : 
      71           1 :                 if (hibernate_state)
      72           0 :                         USE(states, hibernate_state);
      73             :                 else
      74           1 :                         states = strv_new("disk", NULL);
      75             : 
      76           1 :         } else if (streq(verb, "hybrid-sleep")) {
      77           1 :                 if (hybrid_mode)
      78           0 :                         USE(modes, hybrid_mode);
      79             :                 else
      80           1 :                         modes = strv_new("suspend", "platform", "shutdown", NULL);
      81             : 
      82           1 :                 if (hybrid_state)
      83           0 :                         USE(states, hybrid_state);
      84             :                 else
      85           1 :                         states = strv_new("disk", NULL);
      86             : 
      87             :         } else
      88           0 :                 assert_not_reached("what verb");
      89             : 
      90           3 :         if ((!modes && !streq(verb, "suspend")) || !states) {
      91           0 :                 strv_free(modes);
      92           0 :                 strv_free(states);
      93           0 :                 return log_oom();
      94             :         }
      95             : 
      96           3 :         *_modes = modes;
      97           3 :         *_states = states;
      98           3 :         return 0;
      99             : }
     100             : 
     101           7 : int can_sleep_state(char **types) {
     102             :         char **type;
     103             :         int r;
     104          14 :         _cleanup_free_ char *p = NULL;
     105             : 
     106           7 :         if (strv_isempty(types))
     107           0 :                 return true;
     108             : 
     109             :         /* If /sys is read-only we cannot sleep */
     110           7 :         if (access("/sys/power/state", W_OK) < 0)
     111           7 :                 return false;
     112             : 
     113           0 :         r = read_one_line_file("/sys/power/state", &p);
     114           0 :         if (r < 0)
     115           0 :                 return false;
     116             : 
     117           0 :         STRV_FOREACH(type, types) {
     118             :                 const char *word, *state;
     119             :                 size_t l, k;
     120             : 
     121           0 :                 k = strlen(*type);
     122           0 :                 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
     123           0 :                         if (l == k && memcmp(word, *type, l) == 0)
     124           0 :                                 return true;
     125             :         }
     126             : 
     127           0 :         return false;
     128             : }
     129             : 
     130           4 : int can_sleep_disk(char **types) {
     131             :         char **type;
     132             :         int r;
     133           8 :         _cleanup_free_ char *p = NULL;
     134             : 
     135           4 :         if (strv_isempty(types))
     136           0 :                 return true;
     137             : 
     138             :         /* If /sys is read-only we cannot sleep */
     139           4 :         if (access("/sys/power/disk", W_OK) < 0)
     140           4 :                 return false;
     141             : 
     142           0 :         r = read_one_line_file("/sys/power/disk", &p);
     143           0 :         if (r < 0)
     144           0 :                 return false;
     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           0 : static int hibernation_partition_size(size_t *size, size_t *used) {
     169           0 :         _cleanup_fclose_ FILE *f;
     170             :         unsigned i;
     171             : 
     172           0 :         assert(size);
     173           0 :         assert(used);
     174             : 
     175           0 :         f = fopen("/proc/swaps", "re");
     176           0 :         if (!f) {
     177           0 :                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
     178             :                          "Failed to retrieve open /proc/swaps: %m");
     179           0 :                 assert(errno > 0);
     180           0 :                 return -errno;
     181             :         }
     182             : 
     183           0 :         (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
     184             : 
     185           0 :         for (i = 1;; i++) {
     186           0 :                 _cleanup_free_ char *dev = NULL, *type = NULL;
     187             :                 size_t size_field, used_field;
     188             :                 int k;
     189             : 
     190           0 :                 k = fscanf(f,
     191             :                            "%ms "   /* device/file */
     192             :                            "%ms "   /* type of swap */
     193             :                            "%zu "   /* swap size */
     194             :                            "%zu "   /* used */
     195             :                            "%*i\n", /* priority */
     196             :                            &dev, &type, &size_field, &used_field);
     197           0 :                 if (k != 4) {
     198           0 :                         if (k == EOF)
     199           0 :                                 break;
     200             : 
     201           0 :                         log_warning("Failed to parse /proc/swaps:%u", i);
     202           0 :                         continue;
     203             :                 }
     204             : 
     205           0 :                 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
     206           0 :                         log_warning("Ignoring deleted swapfile '%s'.", dev);
     207           0 :                         continue;
     208             :                 }
     209             : 
     210           0 :                 *size = size_field;
     211           0 :                 *used = used_field;
     212           0 :                 return 0;
     213           0 :         }
     214             : 
     215           0 :         log_debug("No swap partitions were found.");
     216           0 :         return -ENOSYS;
     217             : }
     218             : 
     219           0 : static bool enough_memory_for_hibernation(void) {
     220           0 :         _cleanup_free_ char *active = NULL;
     221           0 :         unsigned long long act = 0;
     222           0 :         size_t size = 0, used = 0;
     223             :         int r;
     224             : 
     225           0 :         r = hibernation_partition_size(&size, &used);
     226           0 :         if (r < 0)
     227           0 :                 return false;
     228             : 
     229           0 :         r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
     230           0 :         if (r < 0) {
     231           0 :                 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
     232           0 :                 return false;
     233             :         }
     234             : 
     235           0 :         r = safe_atollu(active, &act);
     236           0 :         if (r < 0) {
     237           0 :                 log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
     238             :                                 active);
     239           0 :                 return false;
     240             :         }
     241             : 
     242           0 :         r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
     243           0 :         log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
     244             :                   r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
     245             : 
     246           0 :         return r;
     247             : }
     248             : 
     249           3 : int can_sleep(const char *verb) {
     250           6 :         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
     251             :         int r;
     252             : 
     253           3 :         assert(streq(verb, "suspend") ||
     254             :                streq(verb, "hibernate") ||
     255             :                streq(verb, "hybrid-sleep"));
     256             : 
     257           3 :         r = parse_sleep_config(verb, &modes, &states);
     258           3 :         if (r < 0)
     259           0 :                 return false;
     260             : 
     261           3 :         if (!can_sleep_state(states) || !can_sleep_disk(modes))
     262           3 :                 return false;
     263             : 
     264           0 :         return streq(verb, "suspend") || enough_memory_for_hibernation();
     265             : }

Generated by: LCOV version 1.11