From 45dc7ba3def7699f8e7314af9fcf614e0aa85094 Mon Sep 17 00:00:00 2001 From: Bernhard Heinloth Date: Mon, 24 Oct 2022 11:00:22 +0200 Subject: [PATCH] Add autosuspend conf and scripts (from Dustin Nguyen and Florian Schmaus) --- autosuspend/autosuspend.conf | 41 +++++++++++++++++++ autosuspend/scripts/activity-check | 32 +++++++++++++++ .../inhibit-autosuspend-flag-file | 22 ++++++++++ .../inhibit-autosuspend-flag-file.pre_fcopy | 22 ++++++++++ .../scripts/activity-check.d/logind-idle-hint | 25 +++++++++++ .../logind-idle-hint.pre_fcopy | 26 ++++++++++++ autosuspend/scripts/activity-check.pre_fcopy | 28 +++++++++++++ autosuspend/scripts/suspend | 3 ++ autosuspend/scripts/wakeup | 10 +++++ .../scripts/wakeup-before-scheduled-reboot | 21 ++++++++++ autosuspend/scripts/wakeup-run-fai | 17 ++++++++ autosuspend/scripts/wakeup-run-fai.pre_fcopy | 8 ++++ 12 files changed, 255 insertions(+) create mode 100644 autosuspend/autosuspend.conf create mode 100755 autosuspend/scripts/activity-check create mode 100755 autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file create mode 100755 autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file.pre_fcopy create mode 100644 autosuspend/scripts/activity-check.d/logind-idle-hint create mode 100644 autosuspend/scripts/activity-check.d/logind-idle-hint.pre_fcopy create mode 100755 autosuspend/scripts/activity-check.pre_fcopy create mode 100755 autosuspend/scripts/suspend create mode 100755 autosuspend/scripts/wakeup create mode 100755 autosuspend/scripts/wakeup-before-scheduled-reboot create mode 100755 autosuspend/scripts/wakeup-run-fai create mode 100755 autosuspend/scripts/wakeup-run-fai.pre_fcopy diff --git a/autosuspend/autosuspend.conf b/autosuspend/autosuspend.conf new file mode 100644 index 0000000..67b72fb --- /dev/null +++ b/autosuspend/autosuspend.conf @@ -0,0 +1,41 @@ +[general] +interval = 120 +idle_time = 900 +suspend_cmd = /etc/autosuspend/suspend +wakeup_cmd = /etc/autosuspend/wakeup {timestamp:.0f} +woke_up_file = /var/run/autosuspend-just-woke-up +lock_file = /var/lock/autosuspend.lock +lock_timeout = 30 +# Can be used to call a command before suspending, either with scheduled wake up or not. +# notify_cmd_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system. Wake up at {iso}' +# notify_cmd_no_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system.' + +[check.LogindSessionsIdle] +enabled = true + +# Incoming SSH connections also prevent automatic suspension of the +# system. +[check.ActiveConnection] +enabled = true +ports = 22 + +[check.ExternalCommand] +enabled = true +command = /etc/autosuspend/activity-check + +# Do not interrupt run-fai with a suspend +[check.RunFai] +enabled = true +class = Processes +processes = fai + +[wakeup.RunFai] +# check when the next run of run-fai.service is scheduled and set an apropriate wakeup-time +enabled = true +class = Command +command = /etc/autosuspend/wakeup-run-fai + +[wakeup.BeforeScheduledReboot] +enabled = true +class = Command +command = /etc/autosuspend/wakeup-before-scheduled-reboot diff --git a/autosuspend/scripts/activity-check b/autosuspend/scripts/activity-check new file mode 100755 index 0000000..25e5865 --- /dev/null +++ b/autosuspend/scripts/activity-check @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +ACTIVITY_CHECK_DIR=/etc/autosuspend/activity-check.d + +if [[ ! -d "${ACTIVITY_CHECK_DIR}" ]]; then + # autosuspend assumes that the system is active if the exit status + # is zero, or inactive otherwise. If there is no + # ACTIVITY_CHECK_DIR, then the system is assumed to be inactive, + # hence we return 1 here. + exit 1 +fi + +shopt -s nullglob +for FILE in "${ACTIVITY_CHECK_DIR}"/*; do + if [[ ! -x "${FILE}" ]]; then + continue + fi + + if [[ "${FILE}" == *.pre_fcopy ]]; then + continue + fi + + if "${FILE}"; then + # Executing FILE returned a zero exit status, that means the + # system is active and we do not need to check more scripts. + exit + fi +done + +# No script signalled that the system is active. Return a non-zero +# value to signal that it is inactive. +exit 1 diff --git a/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file b/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file new file mode 100755 index 0000000..687ca8a --- /dev/null +++ b/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly INHIBIT_AUTOSUSPEND_FLAG_FILE="/var/tmp/inhibit-autosuspend" + +if [[ ! -f "${INHIBIT_AUTOSUSPEND_FLAG_FILE}" ]]; then + exit 1 +fi + +FLAG_FILE_LAST_MODIFICATION=$(stat -c "%Y" "${INHIBIT_AUTOSUSPEND_FLAG_FILE}") +NOW=$(date +"%S") + +FLAG_FILE_AGE_SECONDS=$(( ${NOW} - ${FLAG_FILE_LAST_MODIFICATION} )) + +if [[ ${FLAG_FILE_AGE_SECONDS} -gt 259200 ]]; then + echo "${INHIBIT_AUTOSUSPEND_FLAG_FILE} was last touched three days ago. Going to remove and ignore the file" + rm "${INHIBIT_AUTOSUSPEND_FLAG_FILE}" + exit 1 +fi + +echo "${INHIBIT_AUTOSUSPEND_FLAG_FILE} found, signalling that the system is active" +exit 0 diff --git a/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file.pre_fcopy b/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file.pre_fcopy new file mode 100755 index 0000000..4b41376 --- /dev/null +++ b/autosuspend/scripts/activity-check.d/inhibit-autosuspend-flag-file.pre_fcopy @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly INHIBIT_AUTOSUSPEND_FLAG_FILE="/var/tmp/inhibit-autosuspend" + +if [[ ! -f "${INHIBIT_AUTOSUSPEND_FLAG_FILE}" ]]; then + exit 1 +fi + +FLAG_FILE_LAST_MODIFICATION=$(stat -c "%Y" "${INHIBIT_AUTOSUSPEND_FLAG_FILE}") +NOW=$(date +"%S") + +FLAG_FILE_AGE_SECONDS=$(( ${NOW} - ${FLAG_FILE_LAST_MODIFICATION} )) + +if [[ ${FLAG_FILE_AGE_SECONDS} -gt 4320 ]]; then + echo "${INHIBIT_AUTOSUSPEND_FLAG_FILE} was last touched three days ago. Going to remove and ignore the file" + rm "${INHIBIT_AUTOSUSPEND_FLAG_FILE}" + exit 1 +fi + +echo "${INHIBIT_AUTOSUSPEND_FLAG_FILE} found, signalling that the system is active" +exit 0 diff --git a/autosuspend/scripts/activity-check.d/logind-idle-hint b/autosuspend/scripts/activity-check.d/logind-idle-hint new file mode 100644 index 0000000..4d2f7b6 --- /dev/null +++ b/autosuspend/scripts/activity-check.d/logind-idle-hint @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Although autosuspend's LogindSessionsIdle should check the IdleHint, +# this appears to be, at least sometimes, not reliable. I suspect it +# may because autosuspend uses +# if not properties["IdleHint"]: +# return "Login session {} is not idle".format(session_id) +# https://github.com/languitar/autosuspend/blob/c7f06a27db37f2a4ed04b596427da52fd1192935/src/autosuspend/checks/systemd.py#L112-L113 +# which does not check if the value of IdleHint is 'true' or +# 'false'. Until autosuspend's LogindSessionsIdle check is reliable, +# we use the little script below to see if there is a session with +# IdleHint=no. +# See https://github.com/languitar/autosuspend/pull/225 + +for SESSION_ID in $(loginctl list-sessions --no-legend | awk '{ print $1 }'); do + IDLE_HINT=$(loginctl show-session $SESSION_ID -P IdleHint) + + if [[ "${IDLE_HINT}" == no ]]; then + echo "Session $SESSION_ID is not idle" + exit 0 + fi +done + +exit 1 diff --git a/autosuspend/scripts/activity-check.d/logind-idle-hint.pre_fcopy b/autosuspend/scripts/activity-check.d/logind-idle-hint.pre_fcopy new file mode 100644 index 0000000..56f4e3e --- /dev/null +++ b/autosuspend/scripts/activity-check.d/logind-idle-hint.pre_fcopy @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Although autosuspend's LogindSessionsIdle should check the IdleHint, +# this appears to be, at least sometimes, not reliable. I suspect it +# may because autosuspend uses +# if not properties["IdleHint"]: +# return "Login session {} is not idle".format(session_id) +# https://github.com/languitar/autosuspend/blob/c7f06a27db37f2a4ed04b596427da52fd1192935/src/autosuspend/checks/systemd.py#L112-L113 +# which does not check if the value of IdleHint is 'true' or +# 'false'. Until autosuspend's LogindSessionsIdle check is reliable, +# we use the little script below to see if there is a session with +# IdleHint=no. +# See https://github.com/languitar/autosuspend/pull/225 + +for SESSION_ID in $(loginctl list-sessions --no-legend | awk '{ print $1 }'); do + IDLE_HINT=$(loginctl show-session $SESSION_ID -p IdleHint) + IDLE_HINT="${IDLE_HINT#IdleHint=}" + + if [[ "${IDLE_HINT}" == no ]]; then + echo "Session $SESSION_ID is not idle" + exit 0 + fi +done + +exit 1 diff --git a/autosuspend/scripts/activity-check.pre_fcopy b/autosuspend/scripts/activity-check.pre_fcopy new file mode 100755 index 0000000..9ca4474 --- /dev/null +++ b/autosuspend/scripts/activity-check.pre_fcopy @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +ACTIVITY_CHECK_DIR=/etc/autosuspend/activity-check.d + +if [[ ! -d "${ACTIVITY_CHECK_DIR}" ]]; then + # autosuspend assumes that the system is active if the exit status + # is zero, or inactive otherwise. If there is no + # ACTIVITY_CHECK_DIR, then the system is assumed to be inactive, + # hence we return 1 here. + exit 1 +fi + +shopt -s nullglob +for FILE in "${ACTIVITY_CHECK_DIR}"/*; do + if [[ ! -x "${FILE}" ]]; then + continue + fi + + if "${FILE}"; then + # Executing FILE returned a zero exit status, that means the + # system is active and we do not need to check more scripts. + exit + fi +done + +# No script signalled that the system is active. Return a non-zero +# value to signal that it is inactive. +exit 1 diff --git a/autosuspend/scripts/suspend b/autosuspend/scripts/suspend new file mode 100755 index 0000000..042584b --- /dev/null +++ b/autosuspend/scripts/suspend @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +exec /bin/systemctl suspend diff --git a/autosuspend/scripts/wakeup b/autosuspend/scripts/wakeup new file mode 100755 index 0000000..d1f0675 --- /dev/null +++ b/autosuspend/scripts/wakeup @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +TIMESTAMP="${1}" + +readonly WAKEALARM="/sys/class/rtc/rtc0/wakealarm" + +# One has to write '0' to the pseudo file before setting a new alarm +# time. +echo 0 > "${WAKEALARM}" +echo "${TIMESTAMP}" > "${WAKEALARM}" diff --git a/autosuspend/scripts/wakeup-before-scheduled-reboot b/autosuspend/scripts/wakeup-before-scheduled-reboot new file mode 100755 index 0000000..ae0d35c --- /dev/null +++ b/autosuspend/scripts/wakeup-before-scheduled-reboot @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eou pipefail + +# Although this file is called wakeup-before-scheduled-reboot we +# actually check for the next scheduled reboot *or* shutdown. But this +# does not really matter, as we want to wakeup in both cases and we +# only schedule reboots. +SCHEDULED_SHUTDOWN_INFO=$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager ScheduledShutdown) + +declare -i SCHEDULED_SHUTDOWN_SECS +SCHEDULED_SHUTDOWN_SECS=$(echo "${SCHEDULED_SHUTDOWN_INFO}" | cut -d ' ' -f 3) + +if [[ ${SCHEDULED_SHUTDOWN_SECS} -eq 0 ]]; then + # Output nothing to make autosuspend happy. + echo "" + exit +fi + +BEFORE_SCHEDULED_SHUTDOWN_SECS=$(( SCHEDULED_SHUTDOWN_SECS - 180 )) + +echo "${BEFORE_SCHEDULED_SHUTDOWN_SECS}" diff --git a/autosuspend/scripts/wakeup-run-fai b/autosuspend/scripts/wakeup-run-fai new file mode 100755 index 0000000..34c59a7 --- /dev/null +++ b/autosuspend/scripts/wakeup-run-fai @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -eou pipefail + +export LANG=C +export LC_TIME=C + +NEXT_FAI_RUN_AT=$(systemctl show run-fai.timer --property=NextElapseUSecRealtime --value | cut -d' ' -f 2-) + +declare -i NEXT_FAI_RUN_AT_EPOCH_SECS +NEXT_FAI_RUN_AT_EPOCH_SECS=$(date -d "${NEXT_FAI_RUN_AT}" +%s) + +declare -i ADD_DAYS=3 +declare -i EXTRA_SECS=$(( ADD_DAYS * 24 * 60 * 60 )) + +declare -i WAKEUP_EPOCH_SECS=$(( NEXT_FAI_RUN_AT_EPOCH_SECS + EXTRA_SECS )) + +echo "${WAKEUP_EPOCH_SECS}" diff --git a/autosuspend/scripts/wakeup-run-fai.pre_fcopy b/autosuspend/scripts/wakeup-run-fai.pre_fcopy new file mode 100755 index 0000000..9a5b978 --- /dev/null +++ b/autosuspend/scripts/wakeup-run-fai.pre_fcopy @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +export LANG=C +export LC_TIME=C + +NEXT_FAI_RUN_AT=$(systemctl show run-fai.timer --property=NextElapseUSecRealtime --value | cut -d' ' -f 2-) + +date -d "${NEXT_FAI_RUN_AT}" +%s