Merge pull request #9719 from poettering/sleep-fixes
require the kernel image to still exist before allowing hibernation
This commit is contained in:
+1
-1
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user