Document the PHP api layer and fix what documenting it exposed
Writing down what each endpoint actually guarantees made the places it didn't obvious — shell arguments reaching a crontab or a bash -c unescaped, master.conf written without tmp+rename, and conf edits that could be saved without ever being parsed.
This commit is contained in:
@@ -1,4 +1,90 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// Rsync tab data. Tier enablement, the currently running sync, last-run results, 30 days of
|
||||
// bandwidth history, the per-window script and share lists, and the tuning settings — plus
|
||||
// a separate action serving the live sync log.
|
||||
//
|
||||
// OPERATIONAL MODEL
|
||||
// Two responses on one URL. The default is the tab's full state. ?action=rsync_log is the
|
||||
// log viewer, split out because it polls far faster than the rest and returns a payload
|
||||
// nothing else needs.
|
||||
//
|
||||
// The log action has two sources and prefers the live one. An active sync is identified by
|
||||
// a lock file whose pid is still in /proc, and its in-progress log is read directly. With
|
||||
// no active sync, the most recently modified .last.log is served instead — so the panel
|
||||
// shows the run that is happening, or failing that the run that just happened, without the
|
||||
// caller having to know which.
|
||||
//
|
||||
// Window definitions are a table, not a set of branches. Each tier names the master.conf
|
||||
// script array and the host.conf shares array it draws from, so adding a tier is a row.
|
||||
// monthly is deliberately half-populated — monthly_maintenance.sh does ZFS scrub and SMART
|
||||
// tests and has no rsync section, so no shares variable exists for it. fallback has neither:
|
||||
// it is driven entirely by fallback.sh.
|
||||
//
|
||||
// DESIGN PRINCIPLES
|
||||
// Liveness is proven by the process table, not by the lock file's existence.
|
||||
// A lock whose pid is gone is a crashed run, and treating it as active would show a
|
||||
// stale log as a sync in progress forever.
|
||||
//
|
||||
// Toggles default to on when the key is absent.
|
||||
// !== 'false' rather than === 'true', so a conf that predates a flag behaves as it did
|
||||
// before the flag existed. That is the correct default for a tier that was previously
|
||||
// unconditional.
|
||||
//
|
||||
// History is filtered by date, not by line count.
|
||||
// The bandwidth log is append-only and unbounded; a 30-day cutoff keeps the response
|
||||
// proportional to the window the page renders rather than to the file's age.
|
||||
//
|
||||
// Elapsed time comes from the lock's mtime.
|
||||
// The lock is touched when the run starts, so its age is the run's age without the
|
||||
// script having to report progress.
|
||||
//
|
||||
// OPERATIONAL SAFEGUARDS
|
||||
// Read-only. Nothing here starts, stops, or reconfigures a sync — this endpoint reports on
|
||||
// rsync.sh, and the interlocks that make a sync safe live there. In particular, the
|
||||
// --merge-run / --delete ordering is rsync.sh's to enforce; nothing in this payload implies
|
||||
// it has been satisfied.
|
||||
//
|
||||
// The lock and log scan is bounded to a hardcoded directory.
|
||||
// glob over /tmp/unraid_locks — a literal, not a config value.
|
||||
//
|
||||
// Every read degrades to empty.
|
||||
// @file_get_contents, file() with ?: [] fallbacks, and file_exists() before each read.
|
||||
// A lock caught mid-write or a log removed between the glob and the read yields an empty
|
||||
// line list rather than a fatal.
|
||||
//
|
||||
// Malformed history lines are skipped, not repaired.
|
||||
// A count check before the fields are used, and an isset() on the optional bytes column,
|
||||
// so a truncated or older-format row is dropped instead of producing a row of nulls.
|
||||
//
|
||||
// Output is bounded and stripped.
|
||||
// Last 200 lines, with ANSI escapes removed. rsync logs are long and coloured, and the
|
||||
// escapes would otherwise reach the page as control codes.
|
||||
//
|
||||
// Every setting has a default.
|
||||
// ?? on all four tuning values and on the bandwidth warning threshold, so a conf missing
|
||||
// a key renders a usable panel rather than zeros that read as "no limit configured".
|
||||
//
|
||||
// The profile name is derived from the lock's own contents, with the filename as fallback,
|
||||
// so a lock written by an older format still identifies its run.
|
||||
//
|
||||
// REQUEST
|
||||
// GET full rsync tab state
|
||||
// GET ?action=rsync_log live sync log, or the most recent completed one
|
||||
//
|
||||
// RESPONSE
|
||||
// default {"enabled","windows","active","last_sync","bw_history":[…],"bw_warn_gb",
|
||||
// "win_arrays":{<window>:{"scripts":[…],"shares":[…]}},"settings":{…},"ts"}
|
||||
// log {"ok":true,"live":bool,"profile","elapsed","lines":[…]}
|
||||
//
|
||||
// DEPENDS ON
|
||||
// include/monitor.php vv_rsync_status()
|
||||
// include/config.php vv_conf_vars(), vv_read_conf_raw(), vv_detect_host(),
|
||||
// vv_parse_bash_array(), DATA_DIR
|
||||
// /tmp/unraid_locks rsync_*.lock, rsync_*.log, rsync_*.last.log — written by rsync.sh
|
||||
// DATA_DIR bandwidth_history.db
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
require_once dirname(__DIR__) . '/include/monitor.php';
|
||||
|
||||
Reference in New Issue
Block a user