diff options
| author | 2026-05-29 11:18:13 +0100 | |
|---|---|---|
| committer | 2026-05-29 11:18:13 +0100 | |
| commit | 01df321e907b6c8568bb8622eb44a5c1486a0631 (patch) | |
| tree | bb9d76bf9bddd982fc079e045414e138154fa0ef | |
| parent | 263f39704ae5e8f44a79d64dce2be048009b4df6 (diff) | |
| download | dotfiles-01df321e907b6c8568bb8622eb44a5c1486a0631.tar.gz dotfiles-01df321e907b6c8568bb8622eb44a5c1486a0631.tar.bz2 dotfiles-01df321e907b6c8568bb8622eb44a5c1486a0631.zip | |
feat(suspend): hold inhibit lock while any zellij session exists
The SSH-shell inhibitor in dot_zprofile is bound to the lifetime of the
login shell, so it disappears the moment the user detaches a zellij
session and disconnects — defeating the whole point of using zellij for
persistent remote work.
Add a user-scope path+service+watcher trio that ties the inhibit lock
to the existence of zellij sessions instead:
- dot_local/bin/executable_zellij-inhibit-watcher
Polls `zellij list-sessions --short` every 15s, exits when none
remain. Override poll interval via $ZELLIJ_INHIBIT_POLL.
- dot_config/systemd/user/zellij-inhibit-suspend.service
Wraps the watcher in `systemd-inhibit --what=sleep:idle:handle-lid-switch
--mode=block`. When the watcher exits, the service stops and the
lock is released.
- dot_config/systemd/user/zellij-inhibit-suspend.path
Activates the service whenever $XDG_RUNTIME_DIR/zellij becomes
non-empty (i.e. zellij creates its first session socket). Re-fires
on every empty→non-empty transition.
Enable via systemd-units/user.txt (the .path unit; the service is
on-demand).
The existing SSH-shell inhibitor is kept as a backstop for non-zellij
remote sessions and is now documented as such.
VM (nix/vm.nix) deliberately not updated: the Ubuntu remote-dev VM
never suspends, so the inhibit machinery would be inert there.
| -rw-r--r-- | dot_config/systemd/user/zellij-inhibit-suspend.path | 12 | ||||
| -rw-r--r-- | dot_config/systemd/user/zellij-inhibit-suspend.service | 19 | ||||
| -rw-r--r-- | dot_config/zsh/dot_zprofile | 13 | ||||
| -rwxr-xr-x | dot_local/bin/executable_zellij-inhibit-watcher | 20 | ||||
| -rw-r--r-- | systemd-units/user.txt | 6 |
5 files changed, 65 insertions, 5 deletions
diff --git a/dot_config/systemd/user/zellij-inhibit-suspend.path b/dot_config/systemd/user/zellij-inhibit-suspend.path new file mode 100644 index 0000000..72c509c --- /dev/null +++ b/dot_config/systemd/user/zellij-inhibit-suspend.path @@ -0,0 +1,12 @@ +[Unit] +Description=Activate suspend inhibitor whenever zellij has a live session + +[Path] +# %t expands to $XDG_RUNTIME_DIR (typically /run/user/$UID); zellij keeps +# its per-version session sockets under this directory. Whenever the dir +# transitions from empty to non-empty, the service is (re)activated. +DirectoryNotEmpty=%t/zellij +Unit=zellij-inhibit-suspend.service + +[Install] +WantedBy=default.target diff --git a/dot_config/systemd/user/zellij-inhibit-suspend.service b/dot_config/systemd/user/zellij-inhibit-suspend.service new file mode 100644 index 0000000..9e9ab40 --- /dev/null +++ b/dot_config/systemd/user/zellij-inhibit-suspend.service @@ -0,0 +1,19 @@ +[Unit] +Description=Hold a systemd-inhibit lock while zellij sessions exist +Documentation=man:systemd-inhibit(1) man:zellij(1) +# Independent of any graphical session: this is meant to run on +# headless SSH-attached hosts too. + +[Service] +Type=simple +ExecStart=systemd-inhibit \ + --what=sleep:idle:handle-lid-switch \ + --who=zellij \ + --why=Active\x20zellij\x20sessions \ + --mode=block \ + %h/.local/bin/zellij-inhibit-watcher +# Don't auto-restart: the .path unit reactivates us on the next session. +Restart=no + +[Install] +WantedBy=default.target diff --git a/dot_config/zsh/dot_zprofile b/dot_config/zsh/dot_zprofile index 02991cb..a26209b 100644 --- a/dot_config/zsh/dot_zprofile +++ b/dot_config/zsh/dot_zprofile @@ -151,11 +151,14 @@ case $(uname -n) in esac # ── SSH: inhibit suspend/idle while connected ──────────────────────────────── -# A remote session is useless if the laptop suspends mid-command. Wrap the -# login shell in `systemd-inhibit` so a lock is held for the entire SSH -# session lifetime (lid close, idle timeout, swayidle, lid-switch handlers -# all honour it). The lock is released the instant the shell exits, so a -# clean disconnect restores normal power behaviour. +# Backstop for SSH sessions that don't use zellij. (The dedicated +# zellij-inhibit-suspend.path user unit already covers any host that has +# at least one live zellij session — that one survives detach/disconnect, +# which this in-shell inhibitor does not.) +# +# Wrap the login shell in `systemd-inhibit` so a lock is held for the +# entire SSH session lifetime; the lock is released the instant the +# shell exits. if [[ -n $SSH_CONNECTION && -z $__SSH_SUSPEND_INHIBITED ]] \ && command -v systemd-inhibit >/dev/null 2>&1; then export __SSH_SUSPEND_INHIBITED=1 diff --git a/dot_local/bin/executable_zellij-inhibit-watcher b/dot_local/bin/executable_zellij-inhibit-watcher new file mode 100755 index 0000000..0af20dd --- /dev/null +++ b/dot_local/bin/executable_zellij-inhibit-watcher @@ -0,0 +1,20 @@ +#!/bin/sh +# Block until no zellij sessions remain. +# +# Used as the ExecStart payload of zellij-inhibit-suspend.service: the +# service wraps this script with `systemd-inhibit`, so the inhibit lock +# is held for exactly the lifetime of this process. When the last zellij +# session ends, this script exits 0, the service stops, and the lock is +# released. +# +# A user-level `.path` unit re-activates the service whenever the zellij +# socket directory becomes non-empty again, so the lock is automatically +# reacquired on the next `zellij` invocation. +set -eu + +poll=${ZELLIJ_INHIBIT_POLL:-15} + +while sessions=$(zellij list-sessions --short 2>/dev/null) && + [ -n "$sessions" ]; do + sleep "$poll" +done diff --git a/systemd-units/user.txt b/systemd-units/user.txt index 009b2b3..77ba788 100644 --- a/systemd-units/user.txt +++ b/systemd-units/user.txt @@ -18,3 +18,9 @@ wob.service # --- mail (overridden via drop-ins in dot_config/systemd/user/) --- protonmail-bridge.service + +# --- suspend inhibitor: keep host awake while any zellij session exists --- +# Enable the .path unit; it activates the service on-demand when zellij's +# socket dir becomes non-empty. The service wraps a poller in +# systemd-inhibit and exits when no sessions remain, releasing the lock. +zellij-inhibit-suspend.path |
