Commit 446cbe1e by PLN (Algolia)

tools: music-mode switch arms protection before any bring-up

parent bdb29c99
......@@ -331,6 +331,9 @@ launch_bin(){ # launch_bin "<label>" <binary names...> — first found, detache
# ---------------------------------------------------------------------------
echo
info "gig-up — ordered launch from $DIR"
# Arm root protection before any sound process starts. The service itself stays
# parked on work days; a failed arm must fail the launch loudly.
"$DIR/tools/music-mode.sh" on || exit 1
ensure_perf
cut_gpu
......
......@@ -192,6 +192,9 @@ read -r SC ARD <<<"$(rig_state)"
# broken. A healthy rig converges to a no-op: nothing restarts, nothing makes a sound,
# no MIDI is sent, and CC 77-84 / CC 93 are never touched by anything here.
if (( CONVERGE )); then
# This older CLI path can still start SuperDirt; arm protection first just as
# the desktop/Bridge launcher does.
tools/music-mode.sh on || exit 1
(( QUIET )) || { echo " ${D}converge — starting what is missing${Z}"; }
did=0
......
#!/usr/bin/env bash
# install-protect — put parvagues-protect where root can safely run it.
#
# sudo tools/install-protect.sh install + enable + start
# sudo tools/install-protect.sh install + enable parked watcher
# sudo tools/install-protect.sh --uninstall
#
# WHY THE SCRIPT IS COPIED INSTEAD OF SYMLINKED
......@@ -46,6 +46,7 @@ systemctl enable parvagues-protect.service
# Always restart; this daemon holds no ports and makes no sound, so a restart
# costs nothing.
systemctl restart parvagues-protect.service
echo "Protection watcher installed. It stays parked until tools/music-mode.sh on."
sleep 3
echo
......
#!/usr/bin/env bash
# The user-owned switch read by the root-owned ParVagues protector.
set -euo pipefail
dir="/run/user/$(id -u)/parvagues"
flag="$dir/protection-on"
installed=/usr/local/bin/parvagues-protect
mode_supported() {
[[ -r "$installed" ]] && grep -q '^MODE_FILE=' "$installed"
}
music_running() {
local path name
for path in /proc/[0-9]*/comm; do
IFS= read -r name < "$path" 2>/dev/null || continue
case "$name" in
sclang|scsynth|supernova|ardour|ardour[0-9]*|ardour-[0-9]*|ArdourGUI)
return 0 ;;
esac
done
return 1
}
case "${1:-status}" in
on)
if ! systemctl is-active --quiet parvagues-protect.service; then
echo "ParVagues protection is not running; run: sudo tools/install-protect.sh" >&2
exit 1
fi
mkdir -p -m 700 "$dir"
chmod 700 "$dir"
: > "$flag"
echo "Music mode on: protection armed."
;;
off)
if music_running; then
echo "Close SuperCollider/Ardour before leaving music mode." >&2
exit 1
fi
rm -f "$flag"
if ! mode_supported; then
echo "Mode flag cleared, but the installed protector still scans continuously." >&2
echo "Deploy the mode-aware copy: sudo tools/install-protect.sh" >&2
exit 1
fi
echo "Music mode off: protection parked."
;;
status)
if [[ -f "$flag" ]]; then state=on; else state=off; fi
if mode_supported; then policy=mode-aware; else policy=legacy-always-on; fi
printf 'music mode: %s; protector: %s; policy: %s\n' "$state" \
"$(systemctl is-active parvagues-protect.service 2>/dev/null || true)" "$policy"
;;
*) echo "Usage: music-mode.sh [on|off|status]" >&2; exit 2 ;;
esac
......@@ -73,6 +73,11 @@
set -uo pipefail
INTERVAL="${PARVAGUES_PROTECT_INTERVAL:-2}"
# The system service stays installed and enabled, but does no /proc sweep until
# the user explicitly enters music mode. /run/user is cleared at reboot, so a
# work session starts parked even after an unclean shutdown during a gig.
MODE_OWNER="${PARVAGUES_MODE_OWNER:-pln}"
MODE_FILE="/run/user/$(id -u "$MODE_OWNER")/parvagues/protection-on"
# name:rtprio:comm-regex — the priorities perf.sh established and PLN has
# played on. scsynth is the audio server, sclang feeds it, Ardour records the
......@@ -89,7 +94,12 @@ INTERVAL="${PARVAGUES_PROTECT_INTERVAL:-2}"
TARGETS=(
"scsynth:90:scsynth"
"sclang:85:sclang"
"ardour:80:ardour|ardour[0-9]+|ArdourGUI|ardour-[0-9.]+"
# 0 = OOM shield only, NO realtime promotion. Ardour's AudioEngine thread
# already gets FIFO/83 from the backend; promoting the GUI thread to FIFO/80
# killed Ardour outright via RLIMIT_RTTIME on desktop-icon launches (200ms,
# inherited from gnome-shell) and starved the audio thread via RT bandwidth
# throttling. See tools/fix-ardour-rt.sh for the measurement. 2026-09-24.
"ardour:0:ardour|ardour[0-9]+|ArdourGUI|ardour-[0-9.]+"
)
OOM_TARGET=-1000
......@@ -230,7 +240,8 @@ protect_pid() {
# Only promote. A process already at a HIGHER realtime priority than we ask
# for was tuned by someone who knew more than this script does.
sched_of "$pid" || return 0
# A prio of 0 means OOM shield only. Never promote Ardour's GUI thread.
if [ "${prio:-0}" -gt 0 ] && sched_of "$pid"; then
if [ "$S_POL" != "SCHED_FIFO" ] || [ "${S_PRIO:-0}" -lt "$prio" ]; then
if chrt -f -p "$prio" "$pid" 2>/dev/null; then
out+=" sched:${S_POL:-?}/${S_PRIO:-?}->FIFO/${prio}"
......@@ -238,6 +249,7 @@ protect_pid() {
out+=" sched:FAILED"
fi
fi
fi
# Locked memory can never be paged out — with no swap on this box it mostly
# matters as insurance, but it is free and perf.sh has always set it.
......@@ -282,7 +294,7 @@ sweep() {
# Absent processes are NOT a failure here: "Ardour is not open yet" is a normal
# state ten minutes before doors, and the gate already checks liveness elsewhere.
check() {
local bad=0 found=0 name pid adj pids
local bad=0 found=0 name pid adj pids prio
scan
for t in "${TARGETS[@]}"; do
name="${t%%:*}"
......@@ -298,9 +310,18 @@ check() {
found=1
read -r adj 2>/dev/null < "/proc/$pid/oom_score_adj" || adj="?"
sched_of "$pid" || S_POL=""
if [ "$adj" != "$OOM_TARGET" ] || [ "$S_POL" != "SCHED_FIFO" ]; then
# A zero priority means OOM protection only; scheduling is left alone.
prio="${t#*:}"; prio="${prio%%:*}"
if [ "$adj" != "$OOM_TARGET" ] \
|| { [ "${prio:-0}" -gt 0 ] && [ "$S_POL" != "SCHED_FIFO" ]; }; then
if [ "${prio:-0}" -gt 0 ]; then
echo "UNPROTECTED $name[$pid] oom_score_adj=$adj sched=${S_POL:-none} (want $OOM_TARGET / SCHED_FIFO)"
else
echo "UNPROTECTED $name[$pid] oom_score_adj=$adj (want $OOM_TARGET; scheduling deliberately left alone)"
fi
bad=1
elif [ "${prio:-0}" -eq 0 ]; then
echo "ok $name[$pid] oom_score_adj=$adj sched=${S_POL:-OTHER} (oom shield only, by design)"
else
echo "ok $name[$pid] oom_score_adj=$adj sched=$S_POL/${S_PRIO}"
fi
......@@ -321,8 +342,18 @@ esac
_names=""
for t in "${TARGETS[@]}"; do _p="${t#*:}"; _names+=" ${t%%:*}/${_p%%:*}"; done
log "parvagues-protect: watching${_names} every ${INTERVAL}s (oom=$OOM_TARGET)"
log "parvagues-protect: parked; watching${_names} when music mode is on (oom=$OOM_TARGET)"
armed=0
while :; do
if [[ -f "$MODE_FILE" ]]; then
if [[ "$armed" -eq 0 ]]; then
log "music mode on: protecting${_names} every ${INTERVAL}s"
armed=1
fi
sweep
elif [[ "$armed" -eq 1 ]]; then
log "music mode off: protection parked"
armed=0
fi
sleep "$INTERVAL"
done
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment