Merge pull request #9719 from poettering/sleep-fixes

require the kernel image to still exist before allowing hibernation
This commit is contained in:
Lennart Poettering
2018-07-26 14:03:18 +02:00
committed by GitHub
3 changed files with 98 additions and 18 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
# Match string formats:
# sensor:modalias:<parent device modalias>:dmi:<dmi string>
#
# The device modalias can be seen in the `modalias` file
# The device modalias can be seen in the `modalias` file
# of the sensor parent, for example:
# cat /sys/`udevadm info -q path -n /dev/iio:device0`/../modalias
#
+5 -5
View File
@@ -1770,11 +1770,11 @@ static int method_do_shutdown_or_sleep(
if (sleep_verb) {
r = can_sleep(sleep_verb);
if (r == -ENOSPC)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough swap space for hibernation");
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation");
if (r == -ENOMEDIUM)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Kernel image has been removed, can't hibernate");
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Sleep verb \"%s\" not supported", sleep_verb);
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb \"%s\" not supported", sleep_verb);
if (r < 0)
return r;
}
@@ -2199,7 +2199,7 @@ static int method_can_shutdown_or_sleep(
if (sleep_verb) {
r = can_sleep(sleep_verb);
if (IN_SET(r, 0, -ENOSPC))
if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM))
return sd_bus_reply_method_return(message, "s", "na");
if (r < 0)
return r;
+92 -12
View File
@@ -9,9 +9,12 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <unistd.h>
#include "sd-id128.h"
#include "alloc-util.h"
#include "conf-parser.h"
#include "def.h"
@@ -139,12 +142,16 @@ int can_sleep_disk(char **types) {
return true;
/* If /sys is read-only we cannot sleep */
if (access("/sys/power/disk", W_OK) < 0)
if (access("/sys/power/disk", W_OK) < 0) {
log_debug_errno(errno, "/sys/power/disk is not writable: %m");
return false;
}
r = read_one_line_file("/sys/power/disk", &p);
if (r < 0)
if (r < 0) {
log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
@@ -194,27 +201,30 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
"%zu " /* used */
"%*i\n", /* priority */
&dev_field, &type_field, &size_field, &used_field);
if (k == EOF)
break;
if (k != 4) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/swaps:%u", i);
continue;
}
if (streq(type_field, "partition")) {
if (streq(type_field, "file")) {
if (endswith(dev_field, "\\040(deleted)")) {
log_warning("Ignoring deleted swapfile '%s'.", dev_field);
log_warning("Ignoring deleted swap file '%s'.", dev_field);
continue;
}
} else if (streq(type_field, "partition")) {
const char *fn;
fn = path_startswith(dev_field, "/dev/");
if (fn && startswith(fn, "zram")) {
log_debug("Ignoring compressed ram swap device '%s'.", dev_field);
log_debug("Ignoring compressed RAM swap device '%s'.", dev_field);
continue;
}
}
if (device)
*device = TAKE_PTR(dev_field);
if (type)
@@ -245,14 +255,13 @@ static bool enough_swap_for_hibernation(void) {
r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
if (r < 0) {
log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
return false;
}
r = safe_atollu(active, &act);
if (r < 0) {
log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
active);
log_debug_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active);
return false;
}
@@ -263,6 +272,72 @@ static bool enough_swap_for_hibernation(void) {
return r;
}
static int kernel_exists(void) {
struct utsname u;
sd_id128_t m;
int i, r;
/* Do some superficial checks whether the kernel we are currently running is still around. If it isn't we
* shouldn't offer hibernation as we couldn't possible resume from hibernation again. Of course, this check is
* very superficial, as the kernel's mere existance is hardly enough to know whether the hibernate/resume cycle
* will succeed. However, the common case of kernel updates can be caught this way, and it's definitely worth
* covering that. */
for (i = 0;; i++) {
_cleanup_free_ char *path = NULL;
switch (i) {
case 0:
/* First, let's look in /lib/modules/`uname -r`/vmlinuz. This is where current Fedora places
* its RPM-managed kernels. It's a good place, as it means compiled vendor code is monopolized
* in /usr, and then the kernel image is stored along with its modules in the same
* hierarchy. It's also what our 'kernel-install' script is written for. */
if (uname(&u) < 0)
return log_debug_errno(errno, "Failed to acquire kernel release: %m");
path = strjoin("/lib/modules/", u.release, "/vmlinuz");
break;
case 1:
/* Secondly, let's look in /boot/vmlinuz-`uname -r`. This is where older Fedora and other
* distributions tend to place the kernel. */
path = strjoin("/boot/vmlinuz-", u.release);
break;
case 2:
/* For the other cases, we look in the EFI/boot partition, at the place where our
* "kernel-install" script copies the kernel on install by default. */
r = sd_id128_get_machine(&m);
if (r < 0)
return log_debug_errno(r, "Failed to read machine ID: %m");
(void) asprintf(&path, "/efi/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
break;
case 3:
(void) asprintf(&path, "/boot/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
break;
case 4:
(void) asprintf(&path, "/boot/efi/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
break;
default:
return false;
}
if (!path)
return -ENOMEM;
log_debug("Testing whether %s exists.", path);
if (access(path, F_OK) >= 0)
return true;
if (errno != ENOENT)
log_debug_errno(errno, "Failed to determine whether '%s' exists, ignoring: %m", path);
}
}
int read_fiemap(int fd, struct fiemap **ret) {
_cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL;
struct stat statinfo;
@@ -360,7 +435,7 @@ static bool can_s2h(void) {
FOREACH_STRING(p, "suspend", "hibernate") {
r = can_sleep(p);
if (IN_SET(r, 0, -ENOSPC)) {
if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) {
log_debug("Unable to %s system.", p);
return false;
}
@@ -390,6 +465,11 @@ int can_sleep(const char *verb) {
if (streq(verb, "suspend"))
return true;
if (kernel_exists() <= 0) {
log_debug_errno(errno, "Couldn't find kernel, not offering hibernation.");
return -ENOMEDIUM;
}
if (!enough_swap_for_hibernation())
return -ENOSPC;