From 4e2cfb778b9ed7f22ee98f48f28cf8678d25ad32 Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Wed, 20 Apr 2022 10:13:43 +0200 Subject: [PATCH 1/2] logind: remember our idle state and use it to detect idle level transitions Fixes #16391 --- src/login/logind.c | 23 +++++++++++++++++++---- src/login/logind.h | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/login/logind.c b/src/login/logind.c index aa85480548..d14a17274b 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -963,18 +963,33 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us n = now(CLOCK_MONOTONIC); r = manager_get_idle_hint(m, &since); - if (r <= 0) + if (r <= 0) { /* Not idle. Let's check if after a timeout it might be idle then. */ elapse = n + m->idle_action_usec; - else { + m->was_idle = false; + } else { + /* Idle! Let's see if it's time to do something, or if * we shall sleep for longer. */ if (n >= since.monotonic + m->idle_action_usec && (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) { - log_info("System idle. Will %s now.", handle_action_verb_to_string(m->idle_action)); + bool is_edge = false; - manager_handle_action(m, 0, m->idle_action, false, false); + /* We weren't idle previously or some activity happened while we were sleeping, and now we are + * idle. Let's remember that for the next time and make this an edge transition. */ + if (!m->was_idle || since.monotonic >= m->idle_action_not_before_usec) { + is_edge = true; + m->was_idle = true; + } + + if (m->idle_action == HANDLE_LOCK && !is_edge) + /* We are idle and we were before so we are actually not taking any action. */ + log_debug("System idle."); + else + log_info("System idle. Will %s now.", handle_action_verb_to_string(m->idle_action)); + + manager_handle_action(m, 0, m->idle_action, false, is_edge); m->idle_action_not_before_usec = n; } diff --git a/src/login/logind.h b/src/login/logind.h index 27f9e9729f..58677c9491 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -94,6 +94,7 @@ struct Manager { usec_t idle_action_usec; usec_t idle_action_not_before_usec; HandleAction idle_action; + bool was_idle; HandleAction handle_power_key; HandleAction handle_power_key_long_press; From 181656fc0faa885d69bc34822b8e9b5de3fdf6bf Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Fri, 1 Jul 2022 12:59:57 +0200 Subject: [PATCH 2/2] tests: verify that Lock D-Bus signal is sent when IdleAction=lock --- test/test-functions | 7 ++++ test/units/testsuite-35.sh | 84 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/test/test-functions b/test/test-functions index 57b6017dc4..e60a654af0 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1736,6 +1736,13 @@ install_basic_tools() { image_install -o ldconfig.real # for TEST-35-LOGIN image_install -o evemu-device evemu-event + if command -v expect >/dev/null && command -v tclsh >/dev/null ; then + # shellcheck disable=SC2016 + version="$(tclsh <<< 'puts $tcl_version')" + + image_install expect + inst_recursive /usr/lib64/tcl"$version" /usr/share/tcl"$version" + fi } install_debug_tools() { diff --git a/test/units/testsuite-35.sh b/test/units/testsuite-35.sh index 21cb01c16f..50bd364b95 100755 --- a/test/units/testsuite-35.sh +++ b/test/units/testsuite-35.sh @@ -360,6 +360,89 @@ EOF fi } +setup_idle_action_lock() { + useradd testuser ||: + + mkdir -p /run/systemd/logind.conf.d/ + cat >/run/systemd/logind.conf.d/idle-action-lock.conf </dev/null ; then + echo >&2 "expect not installed, skiping test ${FUNCNAME[0]}" + return 0 + fi + + setup_idle_action_lock + trap teardown_idle_action_lock RETURN + + if loginctl --no-legend | awk '{ print $3; }' | sort -u | grep -q testuser ; then + echo >&2 "Session of the \'testuser\' is already present." + return 1 + fi + + # IdleActionSec is set 1s but the accuracy of associated timer is 30s so we + # need to sleep in worst case for 31s to make sure timer elapsed. We sleep + # here for 35s to accomodate for any possible scheudling delays. + cat > /tmp/test.exp < dbus.log & + + expect /tmp/test.exp & + + # Sleep a bit to give expect time to spawn systemd-run before we check for + # the presence of resulting session. + sleep 1 + if [ "$(loginctl --no-legend | awk '{ print $3; }' | sort -u | grep -c testuser)" != 1 ] ; then + echo >&2 "\'testuser\' is expected to have exactly one session running." + return 1 + fi + + wait %2 + kill %1 + + # We slept for 35s , in that interval all sessions should have become idle + # and "Lock" signal should have been sent out. Then we wrote to tty to make + # session active again and next we slept for another 35s so sessions have + # become idle again. 'Lock' signal is sent out for each session, we have at + # least one session, so minimum of 2 "Lock" signals must have been sent. + if [ "$(grep -c Member=Lock dbus.log)" -lt 2 ]; then + echo >&2 "Too few 'Lock' D-Bus signal sent, expected at least 2." + return 1 + fi + + journalctl -b -u systemd-logind.service --since="$ts" > logind.log + if [ "$(grep -c 'System idle. Will be locked now.' logind.log)" -lt 2 ]; then + echo >&2 "System haven't entered idle state at least 2 times." + return 1 + fi + + rm -f dbus.log logind.log +} + : >/failed test_enable_debug @@ -368,6 +451,7 @@ test_started test_suspend_on_lid test_shutdown test_session +test_lock_idle_action touch /testok rm /failed