In modern systemd systems, you can also use the ssh.socket service together with systemd-inhibit. Instead of running the usual ssh.service standalone, you can let systemd manage the incoming connections. Each open session now gets handled by ssh.socket which creates the instance units based on ssh@.service, so it is just a matter creating a service that binds directly to that. This has the added benefit of tracking and logging each ssh connection with systemd. We can then use this identifier to provide more useful information when blocking sleep. This method also prevents degrading systemd with a failing unit like some other examples.
First you need to check to make sure to enable ssh.socket if you are not already using it.
systemctl is-active ssh.socket
Should say active otherwise you will need to enable it:
sudo systemctl disable ssh
sudo systemctl stop ssh
sudo systemctl enable ssh.socket
sudo systemctl start ssh.socket
Then you just need is to create /etc/systemd/system/ssh-no-sleep@.service with
[Unit]
Description=ssh no sleep
BindsTo=ssh@%i.service
[Service]
ExecStart=/usr/bin/systemd-inhibit --mode block --what sleep --who "ssh session "%I --why "session still active" /usr/bin/sleep infinity
[Install]
WantedBy=ssh@.service
And enable it with:
sudo systemctl enable ssh-no-sleep@
In order for the ssh@.service alias (represented with the @) to be triggered you have to make sure your ssh.socket service has the [Socket] setting Accept=yes, otherwise the no sleep service will never trigger for each new connection. You can test your systemd default settings by running:
sudo systemctl show --no-pager --property Accept ssh.socket
If this returns no, you will need to override it to yes by creating a conf file /etc/systemd/system/ssh.socket.d/accept.conf with:
[Socket]
Accept=yes
If your system is missing ssh@.service or ssh.socket, here is the one from Debian Bullseye as an example that you can work off of:
/etc/systemd/system/ssh@.service
[Unit]
Description=OpenBSD Secure Shell server per-connection daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=auditd.service
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=-/usr/sbin/sshd -i $SSHD_OPTS
StandardInput=socket
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
/etc/systemd/system/ssh.socket
[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Socket]
ListenStream=22
Accept=no
[Install]
WantedBy=sockets.target
After saving this, running systemctl daemon-reload and systemctl restart ssh.socket , you can re-run the prior show command to confirm Accept=yes
Now when someone tries to suspend, they will get a useful message of all the blocking ssh sessions
sudo systemctl suspend
Will then return something like:
Operation inhibited by "ssh session 1/127.0.0.1:22/127.0.0.1:12345" (PID 12345 "systemd-inhibit", user root), reason is "session still active".
Operation inhibited by "ssh session 2/2001:db8:1::1:22/2001:db8:2::1:12345" (PID 54321 "systemd-inhibit", user root), reason is "session still active".
Please retry operation after closing inhibitors and logging out other users.
Alternatively, ignore inhibitors and users with 'systemctl suspend -i'.
Added benefit of ssh.socket is that you can also use:
systemctl status ssh.socket
Which returns active users and system stats:
* ssh.socket - OpenBSD Secure Shell server socket
Loaded: loaded (/lib/systemd/system/ssh.socket; enabled; vendor preset: enabled)
Active: active (listening) since Mon 2021-12-20 14:46:05 EST; 1h 6min ago
Triggers: * ssh@2-2001:db8:1::1:22-2001:db8:2::1:12345.service
* ssh@1-127.0.0.1:22-127.0.0.1:12345.service
Listen: [::]:22 (Stream)
Accepted: 2; Connected: 2;
Tasks: 0 (limit: 18775)
Memory: 16.0K
CPU: 2ms
CGroup: /system.slice/ssh.socket
grep -cv :0
instead of passing throughwc
? – terdon Sep 08 '14 at 18:49[
if you writeif who | grep -qv :0; then
(assuming you have a POSIX conforminggrep
like GNU grep). – David Foerster Sep 30 '14 at 23:53