mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-13 16:50:48 +01:00
Add overnight power-save to night-lockdown; harden dnsmasq with a watchdog
Caps CPU (amd-pstate-epp) and GPU (nvidia-smi power limit) during the existing night-lockdown window instead of migrating services to a Raspberry Pi (tried, abandoned after SSH auth failed). A more aggressive GPU PCI-unbind option is built but left disabled by default. Also fixes an unrelated live incident found while testing: dnsmasq (this PC's LAN DHCP/DNS server) had died with zero error logged, breaking WiFi for every device on the network. Added a watchdog timer mirroring the existing shutdown-timer-monitor-watchdog pattern, and whitelisted the Tapo smart plug app so the overnight power reading is checkable during the phone's curfew window. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
a7596d0442
commit
4f2c82fee7
@ -0,0 +1,57 @@
|
||||
{
|
||||
"intent": "Reduce overnight power draw during night-lockdown without moving self-hosted services off the desktop, and harden dnsmasq (this PC's LAN DHCP/DNS server) against silently dying again after it was found dead with zero error logged.",
|
||||
"scope": [
|
||||
"linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh",
|
||||
"linux_configuration/scripts/single_use/features/setup_dns_blocker.sh",
|
||||
"phone_focus_mode/config.sh",
|
||||
"Non-goal: migrating services to a Raspberry Pi (tried, abandoned mid-session per user decision after SSH auth failed)",
|
||||
"Non-goal: full GPU PCI unbind is built but left disabled by default (GPU_UNBIND_ENABLE=0), not enabled in this change"
|
||||
],
|
||||
"changes": [
|
||||
"night-lockdown: new POWER_SAVE_ENABLE (default 1) caps CPU via amd-pstate-epp energy_performance_preference and GPU via nvidia-smi -pl during lockdown, restoring the exact pre-lockdown values at unlock",
|
||||
"night-lockdown: prev-value capture guarded on the .prev file not already existing, and unlock deletes it after a successful restore -- closes a crash-safety hole where a kill between capture and LOCKED-state-write would have permanently corrupted the restore baseline on re-invocation",
|
||||
"night-lockdown: nvidia-smi -pl calls (both cap and restore) wrapped in timeout 10, matching the existing timeout 20 guard already used for modprobe -r in the same file",
|
||||
"night-lockdown: new GPU_UNBIND_ENABLE (default 0, left off) fully unbinds the GPU from the PCI bus as a more aggressive alternative; includes a re-blank_console call after unbind and a bounded driver-ready poll (replacing a fixed sleep) before lightdm restarts, in case it's ever enabled",
|
||||
"night-lockdown: extended the existing DRY_RUN NOTE convention to cover the new EPP/nvidia-smi/GPU-unbind steps",
|
||||
"setup_dns_blocker.sh: new dnsmasq-watchdog.timer/.service (5-min systemctl is-active check + restart + logger call), mirroring the existing shutdown-timer-monitor-watchdog pattern; enable call centralized in validate_and_enable() (after dnsmasq --test) rather than self-enabling in install_watchdog(), matching this file's own write-then-validate-then-enable convention",
|
||||
"phone_focus_mode/config.sh: com.tplink.iot (Tapo) added to both WHITELIST and NIGHT_WHITELIST so the overnight power-draw baseline reading is checkable during the curfew window"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "pre-commit run --files linux_configuration/scripts/periodic_background/digital_wellbeing/setup_night_lockdown.sh linux_configuration/scripts/single_use/features/setup_dns_blocker.sh phone_focus_mode/config.sh",
|
||||
"result": "pass",
|
||||
"evidence": "All hooks passed including shellcheck, codespell, secret scan; no lint suppressions added"
|
||||
},
|
||||
{
|
||||
"command": "bash test_setup_dns_blocker.sh (existing regression suite)",
|
||||
"result": "pass",
|
||||
"evidence": "PASS: test_setup_dns_blocker.sh -- unmodified, covers dispatch + firewall_is_loaded pipefail regression"
|
||||
},
|
||||
{
|
||||
"command": "sudo DRY_RUN=1 /usr/local/bin/night-lockdown-enter.sh / -unlock.sh (real deployed scripts, redeployed via setup_night_lockdown.sh setup)",
|
||||
"result": "pass",
|
||||
"evidence": "Correct log output for CPU EPP writes, timeout-wrapped nvidia-smi -pl, new DRY_RUN NOTE lines; state file and no real .prev files touched"
|
||||
},
|
||||
{
|
||||
"command": "Live dnsmasq incident (real, not simulated): found dead with zero error logged, mid-session",
|
||||
"result": "pass",
|
||||
"evidence": "Restarted (systemctl restart dnsmasq), confirmed listening on :53/:67, Tapo plug (P110) obtained a DHCP lease at 192.168.1.76 immediately after; watchdog timer deployed and confirmed active via systemctl list-timers"
|
||||
},
|
||||
{
|
||||
"command": "5-agent code review (CLAUDE.md compliance, bug scan, git-history context, code-comment compliance) against the working diff",
|
||||
"result": "pass",
|
||||
"evidence": "2 bug-scan findings independently verified as false positives on live hardware (nvidia-smi accepts decimal -pl args on this driver version; set -euo pipefail already present, refuting the pipeline-exit-status concern); 3 real findings fixed (install_watchdog enable-ordering, prev-value crash-safety, missing nvidia-smi timeout); 2 gated-off findings (GPU-unbind console-blank race, fixed sleep vs driver-ready poll) fixed proactively since GPU_UNBIND_ENABLE is inert by default"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"First real (non-DRY_RUN) lockdown cycle with POWER_SAVE_ENABLE=1 has not happened yet -- tonight's scheduled window is the first real-hardware validation",
|
||||
"GPU_UNBIND_ENABLE stays default-off; if ever manually enabled, the first real run of that path is also its first real test (self-acknowledged in the config comment, cannot be safely rehearsed since it tears down the same GPU/X session running any interactive test)",
|
||||
"Root cause of the dnsmasq death is still unknown (no crash logged) -- the watchdog mitigates recurrence but does not explain the original incident"
|
||||
],
|
||||
"rollback": [
|
||||
"Set POWER_SAVE_ENABLE=0 and/or GPU_UNBIND_ENABLE=0 in /etc/night-lockdown.conf, then re-run sudo ./setup_night_lockdown.sh setup to redeploy without the power-save steps",
|
||||
"To remove the dnsmasq watchdog: systemctl disable --now dnsmasq-watchdog.timer, rm the two unit files, systemctl daemon-reload",
|
||||
"git revert the commit; re-run both setup scripts' setup subcommand to redeploy the prior generated files",
|
||||
"After any rollback, verify SSH/WireGuard access before closing the session -- setup_dns_blocker.sh setup reloads the whole nftables ruleset as a side effect"
|
||||
]
|
||||
}
|
||||
@ -133,6 +133,34 @@ MIN_WAKE_AFTER_LOCKDOWN_HOURS="8"
|
||||
# way you want; the unlock script re-applies it so a DisplayPort link that
|
||||
# didn't re-enumerate cleanly doesn't require a manual xrandr/replug.
|
||||
AUTORANDR_PROFILE="default"
|
||||
|
||||
# Cap CPU/GPU power draw for the night as a safety net against anything left
|
||||
# running headless overnight (interactive GPU/CPU load dies with the GUI
|
||||
# teardown above, but a systemd service — e.g. ollama serving a request — does
|
||||
# not). Set to "0" to disable without touching the enter/unlock scripts.
|
||||
POWER_SAVE_ENABLE="1"
|
||||
|
||||
# AMD amd-pstate-epp hint applied to every core at lockdown (this machine is
|
||||
# Ryzen, not Intel, so intel_pstate/no_turbo does not apply). One of: default,
|
||||
# performance, balance_performance, balance_power, power, custom.
|
||||
CPU_EPP_NIGHT="power"
|
||||
|
||||
# nvidia-smi power limit (watts) applied at lockdown. Set to this card's
|
||||
# reported power.min_limit (100W on the RTX 3090 here) so a runaway overnight
|
||||
# CUDA job is bounded, not eliminated — persistence mode is deliberately left
|
||||
# alone so any legitimate headless GPU service keeps working, just slower.
|
||||
GPU_POWER_LIMIT_NIGHT_WATTS="100"
|
||||
|
||||
# Fully unbind the GPU from the PCI bus at lockdown (unload nvidia kernel
|
||||
# modules + PCI remove), rescanned back at unlock. Far more aggressive than
|
||||
# the power-limit cap above: recovers more idle wattage, but modprobe -r can
|
||||
# hang if anything still references the device, and a botched unbind might
|
||||
# need a hard power-cycle to recover -- which WOULD take every container down
|
||||
# with it (the one thing night-lockdown exists to prevent). Default OFF and
|
||||
# deliberately left off: the first real run of this is an uncorroborated test
|
||||
# on this exact kernel/driver, not a dress rehearsal -- nothing can safely
|
||||
# test it without also tearing down the GUI it runs under.
|
||||
GPU_UNBIND_ENABLE="0"
|
||||
EOF
|
||||
chmod 0644 "$CONF_FILE"
|
||||
|
||||
@ -181,6 +209,10 @@ MONITORED_PROCS="${MONITORED_PROCS:-}"
|
||||
MIN_WAKE_AFTER_LOCKDOWN_HOURS="${MIN_WAKE_AFTER_LOCKDOWN_HOURS:-8}"
|
||||
CONSOLE_TTY="${CONSOLE_TTY:-/dev/tty1}"
|
||||
CONSOLE_BLANK_MODE="${CONSOLE_BLANK_MODE:-1}"
|
||||
POWER_SAVE_ENABLE="${POWER_SAVE_ENABLE:-1}"
|
||||
CPU_EPP_NIGHT="${CPU_EPP_NIGHT:-power}"
|
||||
GPU_POWER_LIMIT_NIGHT_WATTS="${GPU_POWER_LIMIT_NIGHT_WATTS:-100}"
|
||||
GPU_UNBIND_ENABLE="${GPU_UNBIND_ENABLE:-0}"
|
||||
DRY_RUN="${DRY_RUN:-}"
|
||||
|
||||
logg() { logger -t night-lockdown "$*"; printf 'night-lockdown: %s\n' "$*"; }
|
||||
@ -191,6 +223,10 @@ if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN NOTE: exercise the blank-vs-VT-switch timing race (bug: visible"
|
||||
logg "DRY_RUN NOTE: console text). A clean DRY_RUN run is NOT proof that fix"
|
||||
logg "DRY_RUN NOTE: works — only a real run (DRY_RUN unset) validates it."
|
||||
logg "DRY_RUN NOTE: The CPU EPP write, nvidia-smi power-limit cap, and (if"
|
||||
logg "DRY_RUN NOTE: enabled) GPU PCI-unbind are also never actually applied"
|
||||
logg "DRY_RUN NOTE: under DRY_RUN — real-hardware timing/driver behavior for"
|
||||
logg "DRY_RUN NOTE: those is unverified until a real run."
|
||||
fi
|
||||
|
||||
# Run a command best-effort: log-only under DRY_RUN, never abort the lockdown.
|
||||
@ -269,6 +305,45 @@ if [[ "$RGB_ENABLE" == "1" ]] && command -v openrgb >/dev/null 2>&1; then
|
||||
run env HOME="$RGB_HOME" openrgb --mode "$RGB_OFF_MODE" --color "$RGB_OFF_COLOR"
|
||||
fi
|
||||
|
||||
# 2b. Cap CPU/GPU power draw for the night. Save the pre-lockdown values first
|
||||
# so unlock restores exactly what was there, not a hardcoded "default" —
|
||||
# the user may have changed either away from factory settings.
|
||||
# Capture is guarded on the .prev file NOT already existing: this step
|
||||
# runs before state is written LOCKED (step 5), so a kill between here and
|
||||
# there leaves state UNLOCKED and the 30-min curfew check re-invokes this
|
||||
# whole script. Without the guard, that re-run would recapture "prev" from
|
||||
# the already-applied NIGHT value, permanently stranding the machine in
|
||||
# night power-save mode. unlock deletes the .prev file after a successful
|
||||
# restore, so a normal (non-crashed) cycle still captures fresh each night.
|
||||
if [[ "$POWER_SAVE_ENABLE" == "1" ]]; then
|
||||
if [[ -f /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference ]]; then
|
||||
if [[ "$DRY_RUN" != "1" && ! -f "$STATE_DIR/cpu_epp.prev" ]]; then
|
||||
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference \
|
||||
>"$STATE_DIR/cpu_epp.prev" 2>/dev/null || true
|
||||
fi
|
||||
for epp_file in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
|
||||
[[ -e "$epp_file" ]] || continue
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would write $CPU_EPP_NIGHT > $epp_file"
|
||||
else
|
||||
echo "$CPU_EPP_NIGHT" >"$epp_file" 2>/dev/null || logg "WARN: could not set EPP on $epp_file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if command -v nvidia-smi >/dev/null 2>&1; then
|
||||
if [[ "$DRY_RUN" != "1" && ! -f "$STATE_DIR/gpu_power_limit_watts.prev" ]]; then
|
||||
nvidia-smi --query-gpu=power.limit --format=csv,noheader,nounits \
|
||||
>"$STATE_DIR/gpu_power_limit_watts.prev" 2>/dev/null || true
|
||||
fi
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would run: timeout 10 nvidia-smi -pl $GPU_POWER_LIMIT_NIGHT_WATTS"
|
||||
else
|
||||
run timeout 10 nvidia-smi -pl "$GPU_POWER_LIMIT_NIGHT_WATTS"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Mute audio (best-effort). The session teardown below silences apps anyway;
|
||||
# this covers any direct-ALSA sound. PipeWire is intentionally NOT touched, so
|
||||
# the morning unlock cannot be defeated by a persisted wireplumber mute.
|
||||
@ -360,6 +435,34 @@ fi
|
||||
# 8. Final guaranteed blank now that the GUI is fully down.
|
||||
blank_console
|
||||
|
||||
# 9. Optionally unbind the GPU entirely (default OFF — see GPU_UNBIND_ENABLE
|
||||
# comment in night-lockdown.conf for the risk tradeoff). Only ever runs
|
||||
# after the GUI is torn down, since nothing interactive needs the GPU at
|
||||
# this point. Timeout-guarded: modprobe -r can hang if anything still
|
||||
# references the device, and a hang here must not block the rest of
|
||||
# lockdown.
|
||||
if [[ "$GPU_UNBIND_ENABLE" == "1" ]] && command -v nvidia-smi >/dev/null 2>&1; then
|
||||
gpu_pci="$(lspci -d 10de: 2>/dev/null | grep -i 'VGA\|3D' | awk '{print $1}' | head -1)"
|
||||
if [[ -z "$gpu_pci" ]]; then
|
||||
logg "WARN: GPU_UNBIND_ENABLE=1 but no nvidia PCI device found"
|
||||
elif [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would unload nvidia modules and remove PCI device 0000:$gpu_pci"
|
||||
elif timeout 20 modprobe -r nvidia_uvm nvidia_drm nvidia_modeset nvidia 2>&1 | logger -t night-lockdown; then
|
||||
if echo 1 >"/sys/bus/pci/devices/0000:$gpu_pci/remove" 2>/dev/null; then
|
||||
logg "GPU unbound (0000:$gpu_pci removed from PCI bus)"
|
||||
# Unloading nvidia_drm / the PCI remove can surface a fallback
|
||||
# framebuffer that isn't blanked -- same failure class the
|
||||
# continuous re-blank during the lightdm stop (above) exists to
|
||||
# close, just triggered by module unload instead of a VT switch.
|
||||
blank_console
|
||||
else
|
||||
logg "WARN: modules unloaded but PCI remove failed for 0000:$gpu_pci"
|
||||
fi
|
||||
else
|
||||
logg "WARN: modprobe -r timed out or failed -- GPU left bound, skipping unbind"
|
||||
fi
|
||||
fi
|
||||
|
||||
logg "night lockdown active — servers up, GUI down, login surface masked, console dark"
|
||||
ENTER_EOF
|
||||
chmod 0755 "$ENTER_SCRIPT"
|
||||
@ -396,6 +499,8 @@ RGB_ENABLE="${RGB_ENABLE:-1}"
|
||||
RGB_OFF_MODE="${RGB_OFF_MODE:-static}"
|
||||
RGB_OFF_COLOR="${RGB_OFF_COLOR:-000000}"
|
||||
RGB_HOME="${RGB_HOME:-/root}"
|
||||
POWER_SAVE_ENABLE="${POWER_SAVE_ENABLE:-1}"
|
||||
GPU_UNBIND_ENABLE="${GPU_UNBIND_ENABLE:-0}"
|
||||
DRY_RUN="${DRY_RUN:-}"
|
||||
|
||||
logg() { logger -t night-lockdown "$*"; printf 'night-lockdown: %s\n' "$*"; }
|
||||
@ -408,6 +513,10 @@ if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN NOTE: run (DRY_RUN unset) validates them. The alarm floor/dedup"
|
||||
logg "DRY_RUN NOTE: logic (locked_at_epoch) IS exercised"
|
||||
logg "DRY_RUN NOTE: faithfully here, since that part is pure calculation."
|
||||
logg "DRY_RUN NOTE: The CPU EPP / GPU power-limit restore and (if enabled)"
|
||||
logg "DRY_RUN NOTE: the PCI rescan are also never actually applied under"
|
||||
logg "DRY_RUN NOTE: DRY_RUN — real-hardware timing/driver behavior for"
|
||||
logg "DRY_RUN NOTE: those is unverified until a real run."
|
||||
fi
|
||||
|
||||
run() {
|
||||
@ -467,6 +576,33 @@ restore_console
|
||||
# getty@.service is ever masked; autovt@.service is just an alias to it.
|
||||
run systemctl unmask getty@.service
|
||||
|
||||
# 1a. If the GPU was unbound at lockdown, rescan the PCI bus to bring it back
|
||||
# before starting the GUI. Harmless no-op if nothing was removed. Polls
|
||||
# for the driver actually being ready (nvidia-smi succeeding) rather than
|
||||
# a fixed sleep -- starting lightdm against a GPU that hasn't finished
|
||||
# reprobing risks a failed X start, which (unlike a mis-enumerated
|
||||
# display) the autorandr retry loop above cannot recover from.
|
||||
if [[ "$GPU_UNBIND_ENABLE" == "1" ]]; then
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would rescan PCI bus and wait for the GPU driver to be ready"
|
||||
else
|
||||
echo 1 >/sys/bus/pci/rescan 2>/dev/null || logg "WARN: PCI rescan failed"
|
||||
gpu_ready=false
|
||||
for ((i = 0; i < 15; i++)); do
|
||||
if nvidia-smi >/dev/null 2>&1; then
|
||||
gpu_ready=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [[ "$gpu_ready" == true ]]; then
|
||||
logg "GPU driver ready after PCI rescan"
|
||||
else
|
||||
logg "WARN: GPU driver not ready after rescan + 15s -- starting GUI anyway"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Start the GUI — lightdm autologins back into i3/dwm.
|
||||
run systemctl start lightdm.service
|
||||
|
||||
@ -501,6 +637,43 @@ for card in $ALSA_CARDS; do
|
||||
command -v amixer >/dev/null 2>&1 && run amixer -c "$card" -q set Master unmute
|
||||
done
|
||||
|
||||
# 3b. Restore CPU EPP and GPU power limit to their pre-lockdown values (saved
|
||||
# by night-lockdown-enter.sh). Missing state file = enter never ran the
|
||||
# power-save step (e.g. it was disabled then) — nothing to restore.
|
||||
# Delete each .prev file after a successful (non-DRY_RUN) restore: enter's
|
||||
# capture is guarded on the file NOT existing (crash-safety, see its own
|
||||
# comment), so leaving a stale file here would freeze future captures to
|
||||
# tonight's value forever instead of tracking whatever the user's daytime
|
||||
# setting actually is.
|
||||
if [[ "$POWER_SAVE_ENABLE" == "1" ]]; then
|
||||
if [[ -r "$STATE_DIR/cpu_epp.prev" ]]; then
|
||||
prev_epp="$(cat "$STATE_DIR/cpu_epp.prev")"
|
||||
if [[ -n "$prev_epp" ]]; then
|
||||
for epp_file in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
|
||||
[[ -e "$epp_file" ]] || continue
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would write $prev_epp > $epp_file"
|
||||
else
|
||||
echo "$prev_epp" >"$epp_file" 2>/dev/null || logg "WARN: could not restore EPP on $epp_file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
[[ "$DRY_RUN" == "1" ]] || rm -f "$STATE_DIR/cpu_epp.prev"
|
||||
fi
|
||||
|
||||
if command -v nvidia-smi >/dev/null 2>&1 && [[ -r "$STATE_DIR/gpu_power_limit_watts.prev" ]]; then
|
||||
prev_watts="$(cat "$STATE_DIR/gpu_power_limit_watts.prev")"
|
||||
if [[ -n "$prev_watts" ]]; then
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
logg "DRY_RUN: would run: timeout 10 nvidia-smi -pl $prev_watts"
|
||||
else
|
||||
run timeout 10 nvidia-smi -pl "$prev_watts"
|
||||
fi
|
||||
fi
|
||||
[[ "$DRY_RUN" == "1" ]] || rm -f "$STATE_DIR/gpu_power_limit_watts.prev"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. Re-assert lights-off rather than restoring anything: the preference is no
|
||||
# lighting at all, ever, so the morning must not turn the RGB back on.
|
||||
if [[ "$RGB_ENABLE" == "1" ]] && command -v openrgb >/dev/null 2>&1; then
|
||||
|
||||
@ -40,6 +40,8 @@ readonly DNSMASQ_DROPIN_DIR="/etc/systemd/system/dnsmasq.service.d"
|
||||
readonly DNSMASQ_DROPIN="${DNSMASQ_DROPIN_DIR}/blocker.conf"
|
||||
readonly REFRESH_SERVICE="/etc/systemd/system/dns-blocklist-refresh.service"
|
||||
readonly REFRESH_TIMER="/etc/systemd/system/dns-blocklist-refresh.timer"
|
||||
readonly WATCHDOG_SERVICE="/etc/systemd/system/dnsmasq-watchdog.service"
|
||||
readonly WATCHDOG_TIMER="/etc/systemd/system/dnsmasq-watchdog.timer"
|
||||
# Optional DHCP-server mode (for routers that cannot advertise a custom DNS).
|
||||
readonly DHCP_CONF="/etc/dnsmasq.d/lan-dhcp.conf"
|
||||
readonly DHCP_LEASE="12h"
|
||||
@ -184,6 +186,38 @@ WantedBy=timers.target
|
||||
EOF
|
||||
}
|
||||
|
||||
# The whole LAN's DHCP/DNS depends on this one process. Restart=always in the
|
||||
# drop-in only catches an actual crash; an incident on 2026-07-23 found
|
||||
# dnsmasq dead with ZERO error logged (no crash, no "Stopping"/"Stopped" line
|
||||
# either -- consistent with something issuing a plain stop, not a failure) and
|
||||
# it stayed down for hours, silently, until noticed by a phone unable to join
|
||||
# WiFi. Mirrors the existing shutdown-timer-monitor-watchdog pattern used
|
||||
# elsewhere in this repo: same 5-minute cadence, same is-active-or-start shape,
|
||||
# plus a logger call so a future incident leaves a trace instead of none.
|
||||
install_watchdog() {
|
||||
cat >"$WATCHDOG_SERVICE" <<'EOF'
|
||||
[Unit]
|
||||
Description=Watchdog for dnsmasq (LAN DHCP/DNS)
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash -c 'systemctl is-active --quiet dnsmasq || { logger -t dnsmasq-watchdog "dnsmasq was down -- restarting"; systemctl restart dnsmasq; }'
|
||||
EOF
|
||||
cat >"$WATCHDOG_TIMER" <<'EOF'
|
||||
[Unit]
|
||||
Description=Watchdog Timer for dnsmasq
|
||||
|
||||
[Timer]
|
||||
OnBootSec=60
|
||||
OnUnitActiveSec=300
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
}
|
||||
|
||||
validate_and_enable() {
|
||||
log_info "Validating dnsmasq configuration..."
|
||||
dnsmasq --test 2>&1 | tail -3
|
||||
@ -195,7 +229,8 @@ validate_and_enable() {
|
||||
# apply config changes on idempotent re-runs.
|
||||
systemctl restart dnsmasq
|
||||
systemctl enable --now dns-blocklist-refresh.timer
|
||||
log_ok "dnsmasq and the daily refresh timer are enabled."
|
||||
systemctl enable --now dnsmasq-watchdog.timer
|
||||
log_ok "dnsmasq, the daily refresh timer, and the watchdog are enabled."
|
||||
}
|
||||
|
||||
# Open port 53 for LAN clients -- but only touch the firewall if it is already
|
||||
@ -272,6 +307,7 @@ cmd_setup() {
|
||||
write_dnsmasq_conf
|
||||
install_restart_dropin
|
||||
install_refresh_timer
|
||||
install_watchdog
|
||||
validate_and_enable
|
||||
configure_firewall
|
||||
print_manual_steps
|
||||
|
||||
@ -466,6 +466,7 @@ com.Splitwise.SplitwiseMobile
|
||||
|
||||
# --- Smart home ---
|
||||
com.xiaomi.smarthome
|
||||
com.tplink.iot
|
||||
|
||||
# --- Remote SSH access via WireGuard (self-hosted, see
|
||||
# linux_configuration/scripts/single_use/features/setup_wireguard_ssh.sh) ---
|
||||
@ -526,8 +527,10 @@ com.beemdevelopment.aegis
|
||||
com.azure.authenticator
|
||||
oracle.idm.mobile.authenticator
|
||||
com.kunzisoft.keepass.libre
|
||||
# Smart home (control lights before sleep)
|
||||
# Smart home (control lights before sleep; Tapo plug used for overnight
|
||||
# power-draw measurement, must be checkable during the curfew window)
|
||||
com.xiaomi.smarthome
|
||||
com.tplink.iot
|
||||
|
||||
# --- Wake alarm sync (must work at night to set tomorrow's alarm) ---
|
||||
com.kuhy.wake_alarm_sync
|
||||
|
||||
Loading…
Reference in New Issue
Block a user