Dotfiles/desktopenvs/hyprlua/scripts/presence-detect.sh

98 lines
4.3 KiB
Bash
Executable File

#!/bin/bash
# FIDO key presence detection daemon.
# Checks whether a FIDO2/U2F security key is plugged in, every 20s flat, and
# shares caffeine's systemd-inhibit idle lock while a key is present, so
# hypridle never fires during an active session.
#
# Detection: `fido2-token -L` (libfido2) enumerates connected FIDO CTAP
# devices by USB HID usage page — no touch/tap required, so this is a pure
# presence check, not an authentication. Non-empty output = a key is present.
#
# This replaced an earlier webcam motion-detection design: simpler, no camera
# contention with video calls/howdy, and no OpenCV dependency.
# Shared with caffeine.sh: both the manual toggle and this daemon drive the
# same systemd-inhibit lock, so caffeine-status.sh reflects either source.
PID_FILE="/tmp/caffeine-inhibit.pid"
# Marks that *this daemon* (not the manual caffeine toggle) currently holds
# the lock, so a "key removed" tick never releases a manually-started session.
OWNED_FLAG="/tmp/presence-inhibit-owned"
# Records whether a FIDO key is present on the last tick. presence-status.sh
# reads this so the Eww widget can show presence as a distinct signal from
# the idle lock. During a MANUAL caffeine session the daemon still checks (the
# check is cheap and doesn't touch any device other apps might want), so this
# stays live even then — only the inhibit ownership differs.
PRESENCE_FLAG="/tmp/presence-detected"
INTERVAL=20 # seconds between checks, flat regardless of system load
# True if any FIDO2/U2F authenticator is currently enumerable over USB.
# fido2-token -L lists one line per connected device and needs no PIN, touch,
# or tap — it's a pure USB HID enumeration, so this never prompts the key.
_fido_key_present() {
[[ -n "$(fido2-token -L 2>/dev/null)" ]]
}
# Returns true if the inhibitor sentinel process is still alive.
_inhibit_running() {
[[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null
}
_start_inhibit() {
# Guard: don't start a second inhibitor if one is already active — whether
# it's ours from a previous tick or a manually-started caffeine session.
_inhibit_running && return
# --what=idle: target the logind idle-inhibit lock that hypridle polls.
# "sleep infinity" is the sentinel; its PID is saved so we can kill it later.
systemd-inhibit --what=idle --who="presence-detect" \
--why="FIDO key present" --mode=block \
sleep infinity &
echo $! > "$PID_FILE"
touch "$OWNED_FLAG"
# logger writes to the system journal — visible via `journalctl -t presence-detect`.
logger -t presence-detect "FIDO key present — idle inhibited"
}
_stop_inhibit() {
_inhibit_running || return
# Never release a lock we didn't start — that would be a manual caffeine
# session, which must persist regardless of presence.
[[ -f "$OWNED_FLAG" ]] || return
# Killing the sleep process releases the systemd-inhibit lock automatically.
kill "$(cat "$PID_FILE")" 2>/dev/null
rm -f "$PID_FILE" "$OWNED_FLAG"
logger -t presence-detect "FIDO key removed — idle inhibit released"
}
_cleanup() {
# On daemon stop (systemd unit stop, user logout, etc.), release the idle
# lock (only if we're the one holding it) and clear the presence flag —
# with the daemon gone, nothing is watching.
_stop_inhibit
rm -f "$PRESENCE_FLAG"
exit 0
}
# Intercept termination signals to ensure the inhibitor PID is never orphaned.
trap _cleanup SIGTERM SIGINT SIGHUP
while true; do
# When idle is inhibited MANUALLY (a caffeine toggle, not this daemon), the
# lock is already held regardless of presence. Detected the same way
# caffeine-manual-status.sh does it: the shared lock is alive but we don't
# own it. We still update PRESENCE_FLAG below since checking is cheap and
# doesn't touch a device other apps care about — only inhibit ownership
# differs during a manual session.
if _fido_key_present; then
touch "$PRESENCE_FLAG"
# No-op if a manual caffeine session already holds the lock — only
# _stop_inhibit needs the ownership check; starting is always safe
# since _start_inhibit itself no-ops when a lock is already held.
_start_inhibit
else
rm -f "$PRESENCE_FLAG"
_stop_inhibit
fi
sleep "$INTERVAL"
done