summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/shared/fileio.c34
-rw-r--r--src/shared/fileio.h2
-rw-r--r--src/shared/logs-show.c2
-rw-r--r--src/shared/sleep-config.c45
-rw-r--r--src/shared/util.c23
-rw-r--r--src/test/test-fileio.c20
-rw-r--r--src/test/test-sleep.c16
7 files changed, 110 insertions, 32 deletions
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
index 77fd05955a..4e2b4442db 100644
--- a/src/shared/fileio.c
+++ b/src/shared/fileio.c
@@ -648,3 +648,37 @@ int executable_is_script(const char *path, char **interpreter) {
*interpreter = ans;
return 1;
}
+
+/**
+ * Retrieve one field from a file like /proc/self/status.
+ * pattern should start with '\n' and end with ':'. Whitespace
+ * after ':' will be skipped. field must be freed afterwards.
+ */
+int get_status_field(const char *filename, const char *pattern, char **field) {
+ _cleanup_free_ char *status = NULL;
+ char *t;
+ size_t len;
+ int r;
+
+ assert(filename);
+ assert(field);
+
+ r = read_full_file(filename, &status, NULL);
+ if (r < 0)
+ return r;
+
+ t = strstr(status, pattern);
+ if (!t)
+ return -ENOENT;
+
+ t += strlen(pattern);
+ t += strspn(t, WHITESPACE);
+
+ len = strcspn(t, WHITESPACE);
+
+ *field = strndup(t, len);
+ if (!*field)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/src/shared/fileio.h b/src/shared/fileio.h
index a0aae28bae..59e41502b1 100644
--- a/src/shared/fileio.h
+++ b/src/shared/fileio.h
@@ -37,3 +37,5 @@ int load_env_file(const char *fname, const char *separator, char ***l);
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
+
+int get_status_field(const char *filename, const char *pattern, char **field);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 87633e7816..f50777c58d 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -201,7 +201,7 @@ static int output_short(
assert(j);
/* Set the threshold to one bigger than the actual print
- * treshold, so that if the line is actually longer than what
+ * threshold, so that if the line is actually longer than what
* we're willing to print, ellipsization will occur. This way
* we won't output a misleading line without any indication of
* truncation.
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index cd3238b405..5ec7cce458 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -163,6 +163,46 @@ int can_sleep_disk(char **types) {
return false;
}
+#define HIBERNATION_SWAP_THRESHOLD 0.98
+
+static bool enough_memory_for_hibernation(void) {
+ _cleanup_free_ char *active = NULL, *swapfree = NULL;
+ unsigned long long act, swap;
+ int r;
+
+ r = get_status_field("/proc/meminfo", "\nSwapFree:", &swapfree);
+ if (r < 0) {
+ log_error("Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r));
+ return false;
+ }
+
+ r = safe_atollu(swapfree, &swap);
+ if (r < 0) {
+ log_error("Failed to parse SwapFree from /proc/meminfo: %s: %s",
+ swapfree, strerror(-r));
+ return false;
+ }
+
+ r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
+ if (r < 0) {
+ log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
+ return false;
+ }
+
+ r = safe_atollu(active, &act);
+ if (r < 0) {
+ log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
+ active, strerror(-r));
+ return false;
+ }
+
+ r = act <= swap * HIBERNATION_SWAP_THRESHOLD;
+ log_debug("Hibernation is %spossible, Active(anon)=%llu kB, SwapFree=%llu kB, threshold=%.2g%%",
+ r ? "" : "im", act, swap, 100*HIBERNATION_SWAP_THRESHOLD);
+
+ return r;
+}
+
int can_sleep(const char *verb) {
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
int r;
@@ -175,5 +215,8 @@ int can_sleep(const char *verb) {
if (r < 0)
return false;
- return can_sleep_state(states) && can_sleep_disk(modes);
+ if (!can_sleep_state(states) || !can_sleep_disk(modes))
+ return false;
+
+ return streq(verb, "suspend") || enough_memory_for_hibernation();
}
diff --git a/src/shared/util.c b/src/shared/util.c
index 9a075fa163..f6f3b18bfc 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -694,9 +694,6 @@ int is_kernel_thread(pid_t pid) {
int get_process_capeff(pid_t pid, char **capeff) {
const char *p;
- _cleanup_free_ char *status = NULL;
- char *t = NULL;
- int r;
assert(capeff);
assert(pid >= 0);
@@ -706,25 +703,7 @@ int get_process_capeff(pid_t pid, char **capeff) {
else
p = procfs_file_alloca(pid, "status");
- r = read_full_file(p, &status, NULL);
- if (r < 0)
- return r;
-
- t = strstr(status, "\nCapEff:\t");
- if (!t)
- return -ENOENT;
-
- for (t += strlen("\nCapEff:\t"); t[0] == '0'; t++)
- continue;
-
- if (t[0] == '\n')
- t--;
-
- *capeff = strndup(t, strchr(t, '\n') - t);
- if (!*capeff)
- return -ENOMEM;
-
- return 0;
+ return get_status_field(p, "\nCapEff:", capeff);
}
int get_process_exe(pid_t pid, char **name) {
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 1184e7e02f..4a4ed79c11 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -229,9 +229,29 @@ static void test_executable_is_script(void) {
unlink(t);
}
+static void test_status_field(void) {
+ _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL;
+ unsigned long long total, buffers;
+
+ assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
+ puts(t);
+ assert_se(streq(t, "1"));
+
+ assert_se(get_status_field("/proc/meminfo", "MemTotal:", &p) == 0);
+ puts(p);
+ assert_se(safe_atollu(p, &total) == 0);
+
+ assert_se(get_status_field("/proc/meminfo", "\nBuffers:", &s) == 0);
+ puts(s);
+ assert_se(safe_atollu(s, &buffers) == 0);
+
+ assert(buffers < total);
+}
+
int main(int argc, char *argv[]) {
test_parse_env_file();
test_parse_multiline_env_file();
test_executable_is_script();
+ test_status_field();
return 0;
}
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index c3cb9c531d..545dfab92c 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -40,14 +40,14 @@ int main(int argc, char* argv[]) {
**shutdown = strv_new("shutdown", NULL),
**freez = strv_new("freeze", NULL);
- log_info("Can Standby: %s", yes_no(can_sleep_state(standby) > 0));
- log_info("Can Suspend: %s", yes_no(can_sleep_state(mem) > 0));
- log_info("Can Hibernate: %s", yes_no(can_sleep_state(disk) > 0));
- log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk(suspend) > 0));
- log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk(reboot) > 0));
- log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk(platform) > 0));
- log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk(shutdown) > 0));
- log_info("Can Freeze: %s", yes_no(can_sleep_disk(freez) > 0));
+ log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0));
+ log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0));
+ log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0));
+ log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(can_sleep_disk(suspend) > 0));
+ log_info("Hibernate+Reboot configured: %s", yes_no(can_sleep_disk(reboot) > 0));
+ log_info("Hibernate+Platform configured: %s", yes_no(can_sleep_disk(platform) > 0));
+ log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0));
+ log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0));
log_info("Suspend configured and possible: %s", yes_no(can_sleep("suspend") > 0));
log_info("Hibernation configured and possible: %s", yes_no(can_sleep("hibernate") > 0));