refactor: pos system event-trigger → pos system schedule — per-job timers, notify policies, legacy migrate

The single-timer threshold monitor generalizes into a scheduler: each job is a
chmod-600 file in schedule.d/<name>.env (INTERVAL 5m..59m/1h..23h/hourly/daily/
weekly/OnCalendar=..., NOTIFY policy, optional MSG, RULE for threshold,
COMMAND = literal rest of line) with its own systemd user timer pair
(pos-schedule-<name>.timer + oneshot .service, Persistent, reconciled on
enable/disable — orphan units + the legacy pos-event-trigger timer
auto-removed). Policies: always (full output every run), onchange (diff vs
last run, first run sends), onerror (non-zero exit or empty output),
threshold (old event-trigger behavior: first numeric vs RULE, alert on
false→true + recovery, per-job firing state), never (silent side-effect jobs).
run [name|all], list, config (interactive add/edit/remove/enable/disable with
validation), enable/disable [name|all], status, migrate (converts legacy
event.env rules → rule-N.env threshold jobs, verbatim LHS as COMMAND, adopts
the legacy timer's OnCalendar or 5m, removes the old timer). Per-run logs +
state in ~/.local/share/linux_post_install/schedule/{logs,state}/.
config/event.env + event-rules.template → config/schedule.d/ starter jobs
(nvme-health, cpu-temp, disk-root, silent log-cleanup); postinstall installs
them no-clobber into an empty schedule.d/ (legacy event.env users get a
migrate hint instead). bin/pos EXAMPLES + INTERACTIVE_CMDS
(system-schedule config) updated; install.sh ships the renamed tool+lib.
This commit is contained in:
Your Name
2026-08-13 02:14:25 -04:00
parent 8b3435a8d5
commit 414a990801
18 changed files with 1134 additions and 727 deletions
-54
View File
@@ -1,54 +0,0 @@
# eventer rules template — copy/paste into ~/.config/linux_post_install/event.env
# (or add each line via: pos system event-trigger config).
#
# One independent rule per line. Grammar:
#
# "<message>" if <check-command> <op> <threshold>
# <check-command> <op> <threshold> (no message → auto-composed)
#
# <message> optional quoted alert text (quote-stripped)
# <check-command> any shell command; first numeric output is the value
# <op> > < >= <= == !=
# <threshold> number with optional unit suffix: 60c, 80%, 10g
#
# Rules alert once on false→true (plus one recovery message on true→false)
# via lib/notify.sh — Telegram by default. State is tracked per rule in
# ~/.local/share/linux_post_install/eventer/state/ (keyed by rule-line hash).
#
# Verify each check on your machine first (adjust device names):
# sensors -u coretemp-isa-0000 | awk '/Package id 0:/{f=1} f && /temp1_input:/{print $2; exit}'
# smartctl -A /dev/sda | awk '/Temperature_Celsius/{print $10}'
# nvme smart-log /dev/nvme0
# vnstat -d 1
#
# ── CPU (lm-sensors) ──────────────────────────────────────────────
"CPU too hot" if sensors -u coretemp-isa-0000 | awk '/Package id 0:/{f=1} f && /temp1_input:/{print $2; exit}' > 60c
"CPU hot warning" if sensors -u coretemp-isa-0000 | awk '/Package id 0:/{f=1} f && /temp1_input:/{print $2; exit}' > 55c
"Load high" if uptime | sed 's/.*load average: //' | awk -F'[, ]+' '{print $1}' > 4
# ── Memory (procps, always present) ───────────────────────────────
"RAM high" if free -m | awk '/^Mem:/{print int($3/$2*100)}' > 90%
"Swap in use" if free -m | awk '/^Swap:/{print $3+0}' > 1000
# ── Disk space ───────────────────────────────────────────────────
"Disk / nearly full" if df -P / | awk 'NR==2{print $5+0}' > 80%
"Disk /srv full" if df -P /srv | awk 'NR==2{print $5+0}' > 85%
# ── Disk health (smartmontools) ──────────────────────────────────
"Disk sda SMART fail" if smartctl -H /dev/sda | grep -q PASSED && echo 0 || echo 1 == 1
"SDA hot" if smartctl -A /dev/sda | awk '/Temperature_Celsius/{print $10}' > 50c
# ── NVMe (nvme-cli) ──────────────────────────────────────────────
"NVMe hot" if nvme smart-log /dev/nvme0 | awk '/^temperature/{print $3}' > 65c
"NVMe wear-out" if nvme smart-log /dev/nvme0 | awk '/percentage used/{print $NF+0}' > 80%
# ── Network (vnstat needs ~1 day of collected data) ──────────────
"Big traffic day" if vnstat -d 1 | awk '/today/{print $10+0}' > 20000
"Link eth0 down" if ethtool eth0 | grep -q 'Link detected: yes' && echo 0 || echo 1 == 1
# ── Processes ────────────────────────────────────────────────────
"Top CPU 80%" if top -bn1 | awk 'NR==8{print $9+0}' > 80%
"Top MEM 90%" if top -bn1 | awk 'NR==8{print $10+0}' > 90%
# Manage rules interactively: pos system event-trigger config
# Schedule the timer: pos system event-trigger enable 5m
-21
View File
@@ -1,21 +0,0 @@
# eventer rules — evaluated by 'pos system event-trigger run' (systemd timer).
# Copied to ~/.config/linux_post_install/event.env on install (no clobber).
# One independent rule per line. Grammar:
#
# "<message>" if <check-command> <op> <threshold>
# <check-command> <op> <threshold> (no message → auto-composed)
#
# The check command is run; its first numeric output is compared against the
# threshold (float-safe, unit suffix on the threshold is fine: 60c, 80%, 10g).
# Operators: > < >= <= == !=
#
# Rules alert once on false→true (plus one recovery message when the
# condition clears) via lib/notify.sh — Telegram by default.
#
# Examples:
# "CPU too hot" if sensors -u coretemp-isa-0000 | awk '/Package id 0:/{f=1} f && /temp1_input:/{print $2; exit}' > 60c
# "Disk nearly full" if df -P / | awk 'NR==2{print $5+0}' > 80%
# "Load high" if uptime | sed 's/.*load average: //; s/,.*//' >= 4
#
# Manage rules interactively: pos system event-trigger config
# Schedule the timer: pos system event-trigger enable 5m
+9
View File
@@ -0,0 +1,9 @@
# CPU temperature threshold — alerts once while over 60c, one recovery message.
# lm-sensors (in preinstall PACKAGES). Chip names differ per machine — check
# `sensors -u` for the real key and pin the chip (a bare grep can match a
# different chip's temp1_input first).
INTERVAL=5m
NOTIFY=threshold
MSG=CPU too hot
RULE="> 60c"
COMMAND=sensors -u coretemp-isa-0000 | awk '/Package id 0:/{f=1} f && /temp1_input:/{print $2; exit}'
+6
View File
@@ -0,0 +1,6 @@
# Root filesystem usage threshold — alerts once while over 80%, one recovery.
INTERVAL=30m
NOTIFY=threshold
MSG=Disk / nearly full
RULE="> 80%"
COMMAND=df -P / | awk 'NR==2{print $5+0}'
+5
View File
@@ -0,0 +1,5 @@
# Side-effect job: silently prune old pos invocation logs every week.
# NOTIFY=never — runs and does its thing, no notification.
INTERVAL=weekly
NOTIFY=never
COMMAND=find ~/.local/share/linux_post_install/logs -type f -name '*_pos_*.log' -mtime +30 -delete
+11
View File
@@ -0,0 +1,11 @@
# NVMe SMART health — notify when any monitored field changes.
# Needs smartmontools (in preinstall PACKAGES) + passwordless sudo for
# smartctl. Set it up once:
# echo '%sudo ALL=(ALL) NOPASSWD: /usr/sbin/smartctl' | sudo tee /etc/sudoers.d/smartctl
# Adjust /dev/nvme0n1 if your disk has a different device name.
# Note: 'temperature' drifts, so onchange will fire most hours — drop the
# temperature alternative from the grep if you only want fault-field alerts.
INTERVAL=hourly
NOTIFY=onchange
MSG=NVMe health
COMMAND=sudo -n smartctl -a /dev/nvme0n1 | grep -Ei 'critical_warning|temperature|available_spare|percentage_used|media_errors|error_information'