My desktop computer, which runs GNOME, is configured to suspend on 20 minutes of inactivity (desired behavior). However, at various time I tend to remote to it from my laptop for development.
What’s the best approach to disable suspend of computer while SSH sessions are active (i.e., not idle)?
Seeing as nobody knows, then i will theory-ize , then you going to have ‘tickle’ gnome in some, more than likekly with a dbus message, to tell the gnome suspend function that ‘something is happening’'.
Disable suspend and hibernation
For systems which should never attempt any type of suspension, these targets can be disabled at the systemd level with the following:
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
To re-enable hibernate and suspend use the following command:
sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target
so , maybe something on a cronjob, which checks if users are still logged in, you may need ignore users logged into actually local tty’s and local xsessons.
in the system profile, that runs when all users logging in do.
last | grep "still logged in"
if [[ $? -eq 1 ]]
then
#To re-enable hibernate and suspend use the following command:
sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target
fi
which renables suspend service , when all users are logged out , again don’t forget to exclude local users on tty’s :-).
have fun.
This post was brought you by the ACME instant shell scripting service. Thank you for using ACME.
[b0ef@nixos:~]$ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
[sudo] password for b0ef:
Failed to mask unit: File /etc/systemd/system/sleep.target already exists and is a symlink to /nix/store/9gzw98jc64qkwd17a6qqm63w25zysi57-systemd-253.6/example/systemd/system/sleep.target.
In case it helps anyone else, this is how I solved it this afternoon. Could be cleaned up a bit, but it seems to work.
I put this in an ssh.nix file and imported into my configuration.nix. It works by forking a program that just sleeps forever upon first SSH session login and killing it when the last session is closed.
{ config, options, lib, pkgs, ... }:
let
# Prevent sleeping on active SSH
sleep_script = pkgs.writeScript "infinite-sleep"
''
#!/bin/sh
echo $$ >/tmp/ssh_sleep_block.pid
sleep infinity
'';
sleep_wrapper = pkgs.writeScript "sleep-wrapper"
''
#!/bin/sh
setsid systemd-inhibit --what=sleep --why="Active SSH session" --mode=block ${sleep_script} 0>&- &> /tmp/inhibit.out &
'';
ssh_script = pkgs.writeScript "ssh-session-handler"
''
#!/bin/sh
#
# This script runs when an ssh session opens/closes, and masks/unmasks
# systemd sleep and hibernate targets, respectively.
#
# Inspired by: https://unix.stackexchange.com/a/136552/84197 and
# https://askubuntu.com/a/954943/388360
num_ssh=$(netstat -nt | awk '$4 ~ /:22$/ && $6 == "ESTABLISHED"' | wc -l)
# echo "User id is $UID, num_ssh is $num_ssh, pam type $PAM_TYPE" > /tmp/ssh_user
case "$PAM_TYPE" in
open_session)
if [ "$num_ssh" -gt 1 ]; then
exit
fi
logger "Starting sleep inhibitor"
${sleep_wrapper}
logger "Sleep inhibitor started with PID `cat /tmp/ssh_sleep_block.pid`"
;;
close_session)
if [ "$num_ssh" -ne 0 ]; then
exit
fi
logger "Killing sleep inhibitor PID `cat /tmp/ssh_sleep_block.pid`"
kill -9 `cat /tmp/ssh_sleep_block.pid` && rm /tmp/ssh_sleep_block.pid
;;
*)
exit
esac
'';
in {
security.pam.services.sshd.text = pkgs.lib.mkDefault(
pkgs.lib.mkAfter
"# Prevent sleep on active SSH\nsession optional pam_exec.so quiet ${ssh_script}"
);
}
Thanks for a fantastic answer @cbrauchli! I am new to Nix & NixOS and learned a lot by digging into how this works.
I made a couple slight modifications to your expression. I found that the PID of the sleep inhibitor process was not being logged because /tmp/ssh_sleep_block.pid does not exist at the time of logging. I solved this by creating a named pipe in ssh_script that sleep_script writes to. I don’t know if this is the best solution, but it works for me! I also found that the setsid command is not necessary because the process is run in the background anyway. My version (including a few renamed variables and the such) is below, but thanks again for solving this problem for me!
# This expression was written by `cbrauchli` at https://discourse.nixos.org/t/disable-suspend-if-ssh-sessions-are-active/11655/4
# with minor modifications by Dominic Mayhew
{ config, options, lib, pkgs, ... }:
let
PID_PATH = "/tmp/ssh_sleep_block.pid";
PID_PIPE = "pid_pipe";
# Prevent sleeping on active SSH
sleep_script = pkgs.writeScript "infinite-sleep"
''
#!/bin/sh
echo $$ >${PID_PATH}
echo $$ >${PID_PIPE}
sleep infinity
'';
inhibit_script = pkgs.writeScript "inhibit_script"
''
#!/bin/sh
systemd-inhibit --what=sleep --why="Active SSH session" --mode=block ${sleep_script} 0>&- &> /tmp/inhibit.out &
'';
ssh_script = pkgs.writeScript "ssh-session-handler"
''
#!/bin/sh
#
# This script runs when an ssh session opens/closes, and masks/unmasks
# systemd sleep and hibernate targets, respectively.
#
# Inspired by: https://unix.stackexchange.com/a/136552/84197 and
# https://askubuntu.com/a/954943/388360
num_ssh=$(netstat -nt | awk '$4 ~ /:22$/ && $6 == "ESTABLISHED"' | wc -l)
# echo "User id is $UID, num_ssh is $num_ssh, pam type $PAM_TYPE" > /tmp/ssh_user
case "$PAM_TYPE" in
open_session)
if [ "$num_ssh" -gt 1 ]; then
exit
fi
logger "Starting sleep inhibitor"
mkfifo ${PID_PIPE}
${inhibit_script}
logger "Sleep inhibitor started with PID $(cat ${PID_PIPE})"
rm ${PID_PIPE}
;;
close_session)
if [ "$num_ssh" -ne 0 ]; then
exit
fi
logger "Killing sleep inhibitor PID $(cat ${PID_PATH})"
kill -9 $(cat ${PID_PATH}) && rm ${PID_PATH}
;;
*)
exit
esac
'';
in {
security.pam.services.sshd.text = pkgs.lib.mkDefault(
pkgs.lib.mkAfter
"# Prevent sleep on active SSH\nsession optional pam_exec.so quiet ${ssh_script}"
);
}