Add cachy-auto-update: unattended background updates for CachyOS

A root systemd service applies pacman, AUR, Flatpak and AppImage updates on
its own, gated on battery state, gaming activity and whether anybody else is
using the package system. The CLI is deliberately two switches plus status.

No user password is stored anywhere: pacman runs as root directly, and the AUR
step - which makepkg forbids running as root - drops to a locked system account
that sudoers permits to call pacman without a password.
This commit is contained in:
Felitendo committed 2026-08-08 02:27:04 +02:00
1 parent 327da1dae3
commit ceb024d7be
28 files changed
+3856 -1

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
# shellcheck shell=bash
#
# Staying out of the way of a human using pacman/yay/paru.
#
# The contract this file implements: the machine's owner may run any package
# manager at any time and must never see a lock error caused by us. We can only
# guarantee that in one direction - by never *starting* while somebody else is
# mid-transaction - so the checks here run before anything is touched, and a
# refusal simply defers the run to the next hourly tick.
# Package managers that take /var/lib/pacman/db.lck. checkupdates is absent on
# purpose: it works against a private temporary database and never touches the
# real lock.
CAU_PACKAGE_MANAGERS=(
pacman pacman-static
yay paru pikaur aurman trizen
pamac pamac-daemon pamac-manager pamac-tray
octopi octopi-helper
makepkg
)
# cau_acquire_lock
# Guards against two runs overlapping (a slow run still going when the next
# tick fires). Non-blocking: if another run holds it, this one is pointless.
cau_acquire_lock() {
mkdir -p "$CAU_RUNDIR" 2>/dev/null || return 1
exec {CAU_LOCK_FD}> "$CAU_LOCKFILE" || return 1
flock -n "$CAU_LOCK_FD" || return 1
return 0
}
# cau_pacman_lock_holder
# Prints the pids currently holding pacman's database lock, if any.
cau_pacman_lock_holder() {
[[ -e $CAU_PACMAN_LOCK ]] || return 1
cau_have fuser || return 0 # cannot tell; assume it is held
fuser "$CAU_PACMAN_LOCK" 2>/dev/null | tr -s ' ' '\n' | grep -E '^[0-9]+$' || return 0
}
# cau_package_manager_busy
# True when somebody else is using the package system right now. Sets
# CAU_SKIP_REASON.
cau_package_manager_busy() {
local pm
if [[ -e $CAU_PACMAN_LOCK ]]; then
CAU_SKIP_REASON="$(cau_msg "pacman's database is locked by another process")"
return 0
fi
for pm in "${CAU_PACKAGE_MANAGERS[@]}"; do
if cau_process_running "$pm"; then
CAU_SKIP_REASON="$(cau_msg "%s is currently running" "$pm")"
return 0
fi
done
return 1
}
# cau_track_stale_lock
# A db.lck with no process behind it is left over from a crashed transaction.
# Removing it automatically would be reckless - if the guess is wrong it
# corrupts a live transaction - so instead it is counted, and after enough
# consecutive sightings the user is told to clean it up.
CAU_STALE_LOCK_RUNS=3
cau_track_stale_lock() {
local count
if [[ ! -e $CAU_PACMAN_LOCK ]]; then
cau_state_clear stale_lock_count
return 1
fi
if [[ -n "$(cau_pacman_lock_holder)" ]]; then
# genuinely held; not stale
cau_state_clear stale_lock_count
return 1
fi
count="$(cau_state_read stale_lock_count 0)"
[[ $count =~ ^[0-9]+$ ]] || count=0
count=$(( count + 1 ))
cau_state_write stale_lock_count "$count"
(( count >= CAU_STALE_LOCK_RUNS ))
}
# cau_inhibit_prefix
# Builds the command prefix that keeps logind from suspending or shutting the
# machine down mid-transaction. Falls back to running unprotected rather than
# refusing to update at all.
cau_inhibit_prefix() {
if cau_have systemd-inhibit; then
printf '%s\n' systemd-inhibit
printf '%s\n' --what=sleep:shutdown
printf '%s\n' --mode=block
printf '%s\n' "--who=$CAU_PRETTY"
printf '%s\n' "--why=$(cau_msg_in C "applying system updates")"
printf '%s\n' --
fi
}