WIP: service preventing systemd suspend

This commit is contained in:
Daniel Langbein 2023-11-22 15:32:54 +01:00
parent cfd2abe482
commit b25be12e83
Signed by: langfingaz
GPG Key ID: 6C47C753F0823002
4 changed files with 185 additions and 14 deletions

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
printf '%s%s%s\n' 'START. Can be terminated with "kill -TERM ' "$$" '"'
while :; do
printf '%s\n' 'sleeping 10s'
sleep 10s
printf '%s\n' 'awoke from sleep'
done
printf '%s\n' 'END'
# When executing `kill -TERM $PID` in another terminal,
# this script terminates instantly.

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -eu -o pipefail
function handle_sigterm(){
printf '%s\n' 'Ignored SIGTERM' >&2
}
# Handle SIGTERM
trap 'handle_sigterm' TERM
printf '%s%s%s\n' 'START. Can be terminated with "kill -TERM ' "$$" '"'
while :; do
printf '%s\n' 'sleeping 10s'
sleep 10s
printf '%s\n' 'awoke from sleep'
done
printf '%s\n' 'END'
# When executing `kill -TERM $PID` in another terminal,
# the command `sleep 10s` continues.
# After it has finished, `Ignored SIGTERM` is printed and the endless loop continues.

View File

@ -0,0 +1,90 @@
{ config, pkgs, lib, ... }:
# Relevant references:
# How to prevent systemd suspend from stopping any service with a dont-sleep.service. https://askubuntu.com/q/830442/1002706
# systemctl show test-block-suspend.service -p TimeoutStopUSec
#=> TimeoutStopUSec=infinity
# systemctl cat test-block-suspend.service
# systemctl list-dependencies --before test-block-suspend.service
#=> test-block-suspend.service
#=> ├─shutdown.target
#=> ├─sleep.target
#=> │ ├─systemd-hibernate.service
#=> │ ├─systemd-hybrid-sleep.service
#=> │ ├─systemd-suspend-then-hibernate.service
#=> │ └─systemd-suspend.service
#=> └─suspend.target
#=> └─post-resume.service
{
# Alternative approach to adding `conflicts`, `before` and `ExecStop`.
#
# Immediately before entering system suspend, all executables in /usr/lib/systemd/system-sleep/ are run.
# Execution of the sleep action is not continued until all have finished.
# TODO: This happens after my screen got blank, wifi disabled, screen locker activated, etc.
# environment.etc."systemd/system-sleep/test-block-suspend".source =
# pkgs.writeShellScript "test-block-suspend" ''
# set -eu -o pipefail
# if [ "$1" == "pre" ]; then
# while :; do
# systemctl is-active --quiet test-block-suspend.service || exit 0
# ${pkgs.coreutils}/bin/echo 'Waiting until service has finished ...'
# ${pkgs.coreutils}/bin/sleep 1s
# done
# fi
# '';
systemd.services."test-block-suspend" = {
description = "Inhibit suspend";
# If suspend is initiated, this service is stopped.
conflicts = [ "sleep.target" ];
# Start-up of `sleep.target`
# is delayed until this service has finished starting up.
before = [ "sleep.target" ];
serviceConfig = {
# `simple`:
# As soon as the main process (defined in the `ExecStart`) is started (forked),
# start-up is considered as finished.
# `notify`:
# The service sends a "READY=1" notification message via `sd_notify` when it has finished starting up.
# Requires `NotifyAccess`.
# See https://www.freedesktop.org/software/systemd/man/latest/systemd-notify.html
Type = "notify";
NotifyAccess = "main";
# To stop this service `ExecStop` is run instead of sending SIGTERM to the main process of the service.
# The command specified by `ExecStop` does never end,
# thus stopping this service takes forever.
# TODO: However, the sleep action continues while `ExecStop` is running. As a reslut, the system suspends.
ExecStop = "${pkgs.coreutils}/bin/sleep infinity";
TimeoutStopSec = "infinity";
# This service executes a shell script as main process.
ExecStart = pkgs.writeShellScript "test-block-suspend-execstart" ''
set -eu -o pipefail
#handle_sigterm(){
# #while :; do
# printf '%s\n' 'Ignoring SIGTERM ...' >&2
# # ${pkgs.coreutils}/bin/sleep 5s
# #done
#}
#trap 'handle_sigterm' TERM
#printf '%s\n' 'Start-up 30s ...'
#${pkgs.coreutils}/bin/sleep 30s
systemd-notify READY=1
while :; do
printf '%s\n' 'Still alive'
${pkgs.coreutils}/bin/sleep 5s
done
'';
};
};
}

View File

@ -1,19 +1,30 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
# TODO Note: One can specify ExecStart and ExecStop. Maybe to pause some script during shutdown/suspend? This would be nice for backups. Just finish the current snapshot then pause. # TODO Note: One can specify ExecStart and ExecStop. Maybe to pause some script during shutdown/suspend? This would be nice for backups. Just finish the current snapshot then pause.
#
# ExecStop=
# Commands to execute to stop the service started via ExecStart=.
# The command that asks the service to stop should wait for it to do so!
# After the commands configured in this option are run, it is implied that the service is stopped, and any processes remaining for it are terminated.
#
# If this option is not specified, the process is terminated when service stop is requested.
# TODO: https://unix.stackexchange.com/questions/619987/stop-systemd-service-before-suspend-start-again-after-resume # TODO: https://unix.stackexchange.com/questions/619987/stop-systemd-service-before-suspend-start-again-after-resume
# Suspend, hibernate, shutdown
#
# Some of the upstream system units:
# https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/system/boot/systemd.nix#L104
# - suspend.target
# - hibernate.target
# - sleep.target
# - hybrid-sleep.target
# https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/system/boot/systemd.nix#L115
# - reboot.target
# - poweroff.target
#
# BTRFS scrub prevents suspend2ram and proper shutdown
# https://github.com/NixOS/nixpkgs/blob/5c43dee215c279dceee7861eec85862ad85cc330/nixos/modules/tasks/filesystems/btrfs.nix#L131
# conflicts = [ "shutdown.target" "sleep.target" ];
# before = [ "shutdown.target" "sleep.target" ];
# Benefits of Systemd Timers over cron: https://wiki.archlinux.org/title/Systemd/Timers#Benefits # Benefits of Systemd Timers over cron: https://wiki.archlinux.org/title/Systemd/Timers#Benefits
# Example: https://nixos.wiki/wiki/Nix_Cookbook#Creating_periodic_services # Timer example: https://nixos.wiki/wiki/Nix_Cookbook#Creating_periodic_services
# Example: https://nixos.wiki/wiki/Systemd/Timers # Timer example: https://nixos.wiki/wiki/Systemd/Timers
{ {
systemd.timers."hello-world" = { systemd.timers."hello-world" = {
# description = "My Timer"; # description = "My Timer";
@ -46,12 +57,19 @@
systemd.services."hello-world" = { systemd.services."hello-world" = {
# description = "My Oneshot Service"; # description = "My Oneshot Service";
# TODO: Prevents suspend2ram or proper shutdown? # Prevent suspend2ram and proper shutdown.
# https://github.com/NixOS/nixpkgs/blob/e9b4b56e5a20ac322c0c01ccab7ec697ab076ea0/nixos/modules/tasks/filesystems/btrfs.nix#L128-L130 #
# TODO Additionally:
# Option I:
# Use ExecStart and ExecStop. ExecStop initiates an early stop of the service and waits until this has finished.
# In `serviceConfig`, set `Type` to `simple` (and not `oneshot`, otherwise ExecStop is not used).
# Option II:
# Set `TimeoutSec` to `infinity`.
# #
# If the specified units are started, then this unit is stopped and vice versa. # If the specified units are started, then this unit is stopped and vice versa.
conflicts = [ "shutdown.target" "sleep.target" ]; conflicts = [ "shutdown.target" "sleep.target" ];
# If the specified units are started at the same time as this unit, delay them until this unit has started. # If this unit and one from `before` are being started,
# the start-up of the latter is delayed until this service has finished starting up.
before = [ "shutdown.target" "sleep.target" ]; before = [ "shutdown.target" "sleep.target" ];
# https://man.archlinux.org/man/systemd.service.5.en#OPTIONS # https://man.archlinux.org/man/systemd.service.5.en#OPTIONS
@ -60,6 +78,27 @@
# Example: https://github.com/NixOS/nixpkgs/blob/e9b4b56e5a20ac322c0c01ccab7ec697ab076ea0/nixos/modules/tasks/filesystems/btrfs.nix#L132-L142 # Example: https://github.com/NixOS/nixpkgs/blob/e9b4b56e5a20ac322c0c01ccab7ec697ab076ea0/nixos/modules/tasks/filesystems/btrfs.nix#L132-L142
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
# Takes a boolean value that specifies whether the service shall be considered active even when all its processes exited.
# Defaults to `false`.
#RemainAfterExit = true;
# A shorthand for configuring both TimeoutStartSec= and TimeoutStopSec= to the specified value.
# TimeoutStopSec:
# If no ExecStop= commands are specified, the service gets the SIGTERM immediately.
# It configures the time to wait for the service itself to stop.
# If it doesn't terminate in the specified time, it will be forcibly terminated by SIGKILL (see KillMode).
# SIGTERM and SIGKILL:
# The SIGTERM signal is a generic signal used to cause program termination.
# Unlike SIGKILL, this signal can be blocked, handled, and ignored.
# It is the normal way to politely ask a program to terminate.
# SIGINT:
# The SIGINT (“program interrupt”) signal is sent when the user types the INTR character (normally C-c).
# Example:
# grow-partition.nix conflicts with `shutdown.target` and has an infinite `TimeoutStopSec` to make sure that resizing a partition is not interrupted.
# https://github.com/NixOS/nixpkgs/blob/008d84ab67c4f4ccbd7c0996005e46c8bd32d675/nixos/modules/system/boot/grow-partition.nix#L34
#TimeoutSec = "infinity";
PrivateTmp = true; PrivateTmp = true;
#User = "myuser"; #User = "myuser";
@ -69,7 +108,16 @@
# Takes one of the strings realtime, best-effort or idle. # Takes one of the strings realtime, best-effort or idle.
IOSchedulingClass = "idle"; IOSchedulingClass = "idle";
#ExecStart = "${pkgs.python3.withPackages my-python-packages}/bin/netcup-dns"; #ExecStart = "${pkgs.coreutils}/bin/echo 'Hello World'";
# Commands to execute to stop the service started via ExecStart=.
# The command that asks the service to stop should wait for it to do so!
# After the commands configured in this option are run, it is implied that the service is stopped, and any processes remaining for it are terminated.
#
# If this option is not specified, the process is terminated when service stop is requested.
#ExecStop = pkgs.writeShellScript "cancel-hello-world" ''
# set -eu -o pipefail
# echo 'Cancelling execution of hello-world'
#'';
}; };
# Packages required for the script. # Packages required for the script.
@ -87,7 +135,7 @@
# Shell commands executed as the service's main process. # Shell commands executed as the service's main process.
script = '' script = ''
set -eu -o pipefail set -eu -o pipefail
${pkgs.coreutils}/bin/echo "Hello World" ${pkgs.coreutils}/bin/echo 'Hello World'
''; '';
}; };
} }