Browse Source

Heed SuspendMode settings; Do not prepare for hibernation when 'suspend' is called

Also can_sleep(): Fix checks when SuspendModes= is set in logind.conf

Signed-off-by: Sven Eden <sven.eden@prydeworx.com>

(cherry picked from commit 59d49281f2)
(cherry picked from commit 4d6b8adbbc)
(cherry picked from commit c052b39698)
pull/181/head
Sven Eden 2 months ago
parent
commit
501b34d5c1
4 changed files with 155 additions and 54 deletions
  1. +3
    -5
      .idea/codeStyles/Project.xml
  2. +1
    -2
      Makefile
  3. +49
    -0
      src/shared/sleep-config.c
  4. +102
    -47
      src/sleep/sleep.c

+ 3
- 5
.idea/codeStyles/Project.xml View File

@ -18,7 +18,7 @@
<option name="TEMPLATE_PARAMETERS_WRAP" value="5" />
<option name="TEMPLATE_PARAMETERS_ALIGN_MULTILINE" value="true" />
<option name="TEMPLATE_PARAMETERS_ALIGN_MULTILINE_PARS" value="true" />
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="1" />
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE_PARS" value="true" />
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="5" />
@ -72,12 +72,10 @@
<option name="SPACE_AROUND_BITWISE_OPERATORS" value="false" />
<option name="SPACE_BEFORE_SEMICOLON" value="true" />
<option name="SPACE_WITHIN_BRACES" value="false" />
<option name="BINARY_OPERATION_WRAP" value="5" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="5" />
<option name="FOR_STATEMENT_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ASSIGNMENT_WRAP" value="5" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="1" />
<option name="DOWHILE_BRACE_FORCE" value="1" />
<option name="WHILE_BRACE_FORCE" value="1" />
<option name="FOR_BRACE_FORCE" value="1" />

+ 1
- 2
Makefile View File

@ -31,7 +31,6 @@ MAKE := $(shell which make)
MESON ?= $(shell which meson)
MKDIR := $(shell which mkdir) -p
RM := $(shell which rm) -f
SED := $(shell which sed)
# Save users/systems choice
envCFLAGS := ${CFLAGS}
@ -88,7 +87,7 @@ install: build
(cd $(BUILDDIR) && DESTDIR=$(DESTDIR) ninja $(NINJA_OPT) install)
justprint: $(CONFIG)
($(MAKE) all JUST_PRINT=YES | $(SED) -e 's, \.\./, ./,g')
$(MAKE) all JUST_PRINT=YES
loginctl: $(CONFIG)
(cd $(BUILDDIR) && ninja $(NINJA_OPT) $@)

+ 49
- 0
src/shared/sleep-config.c View File

@ -211,6 +211,48 @@ int can_sleep_disk(char **types) {
return false;
}
#if 1 /// Let's check the mem_sleep so elogind supports suspend modes
static int can_sleep_mem(char **types) {
_cleanup_free_ char *p = NULL;
char **type;
int r;
if (strv_isempty(types))
return true;
/* If /sys is read-only we cannot sleep */
if (access("/sys/power/mem_sleep", W_OK) < 0) {
log_debug_errno(errno, "/sys/power/mem_sleep is not writable: %m");
return false;
}
r = read_one_line_file("/sys/power/mem_sleep", &p);
if (r < 0) {
log_debug_errno(r, "Couldn't read /sys/power/mem_sleep: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
size_t l, k;
k = strlen(*type);
FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
if (l == k && memcmp(word, *type, l) == 0)
return true;
if (l == k + 2 &&
word[0] == '[' &&
memcmp(word + 1, *type, l - 2) == 0 &&
word[l-1] == ']')
return true;
}
}
return false;
}
#endif // 1
#define HIBERNATION_SWAP_THRESHOLD 0.98
SwapEntry* swap_entry_free(SwapEntry *se) {
@ -680,8 +722,15 @@ static int can_sleep_internal(const char *verb, bool check_allowed, const SleepC
if (streq(verb, "suspend-then-hibernate"))
return can_s2h(sleep_config);
#if 0 /// elogind supports setting a suspend mode
if (!can_sleep_state(states) || !can_sleep_disk(modes))
return false;
#else // 0
if (!can_sleep_state(states) ||
!((strcmp("suspend", verb) && can_sleep_disk(modes)) ||
(streq("suspend", verb) && can_sleep_mem(modes))))
return false;
#endif // 0
if (streq(verb, "suspend"))
return true;

+ 102
- 47
src/sleep/sleep.c View File

@ -50,7 +50,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_verb, freep);
static int nvidia_sleep(Manager* m, char const* verb, unsigned* vtnr) {
static char const* drv_suspend = "/proc/driver/nvidia/suspend";
struct stat std;
int r, vt = 0, x11 = 0;
int r, x11 = 0;
unsigned vt = 0;
char** session;
char** sessions;
char* type;
@ -183,15 +184,26 @@ static int write_hibernate_location_info(const HibernateLocation* hibernate_loca
static int write_mode(char **modes) {
int r = 0;
char **mode;
#if 1 /// Heeding elogind configuration for SuspendMode, we need to know where to write what
char const* mode_location = strcmp( arg_verb, "suspend") ? "/sys/power/disk" : "/sys/power/mem_sleep";
#endif // 1
STRV_FOREACH(mode, modes) {
int k;
#if 0 /// elogind uses an adapting target
k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
#else // 0
k = write_string_file(mode_location, *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
#endif // 0
if (k >= 0)
return 0;
#if 0 /// elogind uses an adapting target
log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
#else // 0
log_debug_errno(k, "Failed to write '%s' to %s: %m", *mode, mode_location);
#endif // 0
if (r >= 0)
r = k;
}
@ -270,34 +282,86 @@ static int lock_all_homes(void) {
}
#endif // 0
#if 0 /// elogind uses the values stored in its manager instance
#if 0 /// Changes needed by elogind are too vast to do this inline
static int execute(char **modes, char **states) {
#else // 0
static int execute(Manager* m, char **modes, char **states) {
#endif // 0
char* arguments[] = {
NULL,
(char*) "pre",
arg_verb,
NULL
};
#if 0 /// elogind also supports hook scripts in /etc/elogind/system-sleep
static const char* const dirs[] = {
SYSTEM_SLEEP_PATH,
NULL
};
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
int r;
/* This file is opened first, so that if we hit an error,
* we can abort before modifying any state. */
f = fopen("/sys/power/state", "we");
if (!f)
return log_error_errno(errno, "Failed to open /sys/power/state: %m");
setvbuf(f, NULL, _IONBF, 0);
/* Configure hibernation settings if we are supposed to hibernate */
if (!strv_isempty(modes)) {
r = find_hibernate_location(&hibernate_location);
if (r < 0)
return log_error_errno(r, "Failed to find location to hibernate to: %m");
if (r == 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
* do it ourselves then. > 0 means: kernel already had a configured hibernation
* location which we shouldn't touch. */
r = write_hibernate_location_info(hibernate_location);
if (r < 0)
return log_error_errno(r, "Failed to prepare for hibernation: %m");
}
r = write_mode(modes);
if (r < 0)
return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
}
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
(void) lock_all_homes();
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
LOG_MESSAGE("Suspending system..."),
"SLEEP=%s", arg_verb);
r = write_state(&f, states);
if (r < 0)
log_struct_errno(LOG_ERR, r,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
"SLEEP=%s", arg_verb);
else
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("System resumed."),
"SLEEP=%s", arg_verb);
arguments[1] = (char*) "post";
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_IGNORE_ERRORS);
return r;
}
#else // 0
static int execute(Manager* m, char const* verb, char **modes, char **states) {
char* arguments[] = {
NULL,
(char*) "pre",
(char*) verb,
NULL
};
static const char* const dirs[] = {
SYSTEM_SLEEP_PATH,
PKGSYSCONFDIR "/system-sleep",
NULL
};
#endif // 0
#if 1 /// Extra stuff needed by elogind
int e;
_cleanup_free_ char *l = NULL;
void* gather_args[] = {
[STDOUT_GENERATE] = m,
[STDOUT_COLLECT] = m,
@ -310,7 +374,6 @@ static int execute(Manager* m, char **modes, char **states) {
char const* mode_location = strcmp( verb, "suspend") ? "/sys/power/disk" : "/sys/power/mem_sleep";
assert(m);
#endif // 1
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
@ -326,26 +389,24 @@ static int execute(Manager* m, char **modes, char **states) {
/* Configure hibernation settings if we are supposed to hibernate */
if (!strv_isempty(modes)) {
r = find_hibernate_location(&hibernate_location);
if (r < 0)
return log_error_errno(r, "Failed to find location to hibernate to: %m");
if (r == 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
* do it ourselves then. > 0 means: kernel already had a configured hibernation
* location which we shouldn't touch. */
r = write_hibernate_location_info(hibernate_location);
if (strcmp( verb, "suspend")) {
r = find_hibernate_location(&hibernate_location);
if (r < 0)
return log_error_errno(r, "Failed to prepare for hibernation: %m");
return log_error_errno(r, "Failed to find location to hibernate to: %m");
if (r == 0) {
/* 0 means: no hibernation location was configured in the kernel so far, let's
* do it ourselves then. > 0 means: kernel already had a configured hibernation
* location which we shouldn't touch. */
r = write_hibernate_location_info(hibernate_location);
if (r < 0)
return log_error_errno(r, "Failed to prepare for hibernation: %m");
}
}
r = write_mode(modes);
if (r < 0)
return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
return log_error_errno(r, "Failed to write mode to %s: %m", mode_location);
}
#if 0 /// elogind needs its own callbacks to enable cancellation by erroneous scripts
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
(void) lock_all_homes();
#else // 0
m->callback_failed = false;
m->callback_must_succeed = m->allow_suspend_interrupts;
@ -360,7 +421,7 @@ static int execute(Manager* m, char **modes, char **states) {
e = asprintf(&l, "A sleep script in %s or %s failed! [%d]\n"
"The system %s has been cancelled!",
SYSTEM_SLEEP_PATH, PKGSYSCONFDIR "/system-sleep",
r, arg_verb);
r, verb);
if (e < 0) {
log_oom();
return -ENOMEM;
@ -368,22 +429,20 @@ static int execute(Manager* m, char **modes, char **states) {
utmp_wall(l, "root", "n/a", logind_wall_tty_filter, m);
log_struct_errno(LOG_ERR, r,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
log_struct_errno(LOG_ERR, r, "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("A sleep script in %s or %s failed [%d]: %m\n"
"The system %s has been cancelled!",
SYSTEM_SLEEP_PATH, PKGSYSCONFDIR "/system-sleep",
r, arg_verb),
"SLEEP=%s", arg_verb);
r, verb),
"SLEEP=%s", verb);
return -ECANCELED;
}
#endif // 0
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
LOG_MESSAGE("Suspending system..."),
"SLEEP=%s", arg_verb);
"SLEEP=%s", verb);
/* See whether we have an nvidia card to put to sleep */
have_nvidia = nvidia_sleep(m, verb, &vtnr);
@ -393,31 +452,27 @@ static int execute(Manager* m, char **modes, char **states) {
log_struct_errno(LOG_ERR, r,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
"SLEEP=%s", arg_verb);
"SLEEP=%s", verb);
else
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("System resumed."),
"SLEEP=%s", arg_verb);
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("System resumed."),
"SLEEP=%s", verb);
/* Wakeup a possibly put to sleep nvidia card */
if (have_nvidia)
nvidia_sleep(m, "resume", &vtnr);
arguments[1] = (char*) "post";
#if 0 /// On wakeup the order might matter, so do not have elogind executing the post scripts in parallel!
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
#else // 0
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_IGNORE_ERRORS);
#endif // 0
return r;
}
#endif // 0
#if 0 /// elogind uses the values stored in its manager instance
static int execute_s2h(const SleepConfig *sleep_config) {
#else // 0
static int execute_s2h(Manager *sleep_config) {
#endif // 0
_cleanup_close_ int tfd = -1;
@ -443,7 +498,7 @@ static int execute_s2h(Manager *sleep_config) {
#if 0 /// For the elogind extra stuff, we have to submit the manager instance
r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
#else // 0
r = execute(sleep_config, sleep_config->suspend_modes, sleep_config->suspend_states);
r = execute(sleep_config, "suspend", sleep_config->suspend_modes, sleep_config->suspend_states);
#endif // 0
if (r < 0)
return r;
@ -463,7 +518,7 @@ static int execute_s2h(Manager *sleep_config) {
#if 0 /// For the elogind extra stuff, we have to submit the manager instance
r = execute(sleep_config->hibernate_modes, sleep_config->hibernate_states);
#else // 0
r = execute(sleep_config, sleep_config->hibernate_modes, sleep_config->hibernate_states);
r = execute(sleep_config, "hibernate", sleep_config->hibernate_modes, sleep_config->hibernate_states);
#endif // 0
if (r < 0) {
log_notice_errno(r, "Couldn't hibernate, will try to suspend again: %m");
@ -471,7 +526,7 @@ static int execute_s2h(Manager *sleep_config) {
#if 0 /// For the elogind extra stuff, we have to submit the manager instance
r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
#else // 0
r = execute(sleep_config, sleep_config->suspend_modes, sleep_config->suspend_states);
r = execute(sleep_config, "suspend", sleep_config->suspend_modes, sleep_config->suspend_states);
#endif // 0
if (r < 0)
return log_error_errno(r, "Could neither hibernate nor suspend, giving up: %m");
@ -622,7 +677,7 @@ int do_sleep(Manager *m, const char *verb) {
if (streq(arg_verb, "suspend-then-hibernate"))
r = execute_s2h(m);
else
r = execute(m, modes, states);
r = execute(m, arg_verb, modes, states);
return r;
}

Loading…
Cancel
Save