docker_update.sh runs bare in daily, --weekly in weekly and --remainder in monthly, so switching it off in monthly disabled the daily run and reported success.
100 lines
5.4 KiB
PHP
100 lines
5.4 KiB
PHP
<?php
|
|
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
// PURPOSE
|
|
// Orchestrator membership toggle. Comments or uncomments one script's entry inside the
|
|
// *_SCRIPTS arrays in master.conf, so the scheduler page can take a script out of an
|
|
// orchestrator's run list without deleting it.
|
|
//
|
|
// OPERATIONAL MODEL
|
|
// This is not the same switch as the scheduler's enable/disable. Scheduler state controls
|
|
// whether a *cron entry* fires; this controls whether an orchestrator *calls a child
|
|
// script* during its own run. A script can be disabled here and still run, if another
|
|
// orchestrator lists it — unraid_api_key_renew.sh is deliberately in two arrays.
|
|
//
|
|
// The edit is a comment marker, not a deletion. The line stays in master.conf with its
|
|
// arguments and its position intact, so re-enabling restores exactly what was there before
|
|
// and a diff shows an intent change rather than a removal.
|
|
//
|
|
// DESIGN PRINCIPLES
|
|
// Only the first match is toggled.
|
|
// vv_conf_toggle_script() breaks after the first array entry it matches. A script
|
|
// listed in two orchestrators is not silently changed in both by one click.
|
|
//
|
|
// No match is success, not failure.
|
|
// A script that is not in any array has nothing to toggle and the conf is already in
|
|
// the requested state. Returning an error there would make the UI report a problem
|
|
// where none exists.
|
|
//
|
|
// OPERATIONAL SAFEGUARDS
|
|
// POST only, checked before any parameter is read.
|
|
//
|
|
// The id is pattern-matched and traversal-checked separately.
|
|
// ^[a-zA-Z0-9_./\-]+\.sh$ permits the Category/script.sh form the arrays actually use,
|
|
// so the slash cannot simply be banned — str_contains($id, '..') is therefore a second,
|
|
// explicit check rather than something folded into the pattern.
|
|
//
|
|
// The id is never used as a path.
|
|
// It reaches vv_conf_toggle_script() only as a preg_quote()d needle matched against
|
|
// existing lines in master.conf. Nothing is opened, executed, or created from it, so a
|
|
// value that slipped past validation still has no file to reach.
|
|
//
|
|
// The scope of the edit is bounded to array bodies.
|
|
// The library tracks whether it is inside a `*_SCRIPTS=(` block and skips every line
|
|
// outside one, so a matching string in a comment or an unrelated variable cannot be
|
|
// rewritten.
|
|
//
|
|
// The write is atomic, backed up, verified and logged.
|
|
// vv_conf_toggle_script() goes through vv_conf_edit(), the one guarded conf write path:
|
|
// an exclusive lock, a timestamped copy into CONF_BACKUP_DIR, bash -n on the candidate,
|
|
// tmp + rename to install it, then the installed file is sourced to prove it still loads.
|
|
// There is no single key to read back for a commented array member, so a clean source is
|
|
// the whole assertion. Every script sources master.conf; a truncated or unparseable write
|
|
// here would be a system-wide outage rather than a lost toggle.
|
|
//
|
|
// A script in no array writes nothing at all.
|
|
// The rewrite returns the contents unchanged, which reports success without taking a
|
|
// backup or touching the file. "Already in the requested state" is not a write.
|
|
//
|
|
// REQUEST
|
|
// POST id=<Category/script.sh> enabled=0|1
|
|
//
|
|
// RESPONSE
|
|
// {"ok":true,"error":null}
|
|
// {"ok":false,"error":"POST only"|"Invalid id"|"Failed to write master.conf"}
|
|
//
|
|
// DEPENDS ON
|
|
// include/scheduler.php vv_conf_toggle_script() → vv_conf_edit() → vv_write_conf_raw()
|
|
// Configurations/master.conf the *_SCRIPTS arrays
|
|
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
header('Content-Type: application/json');
|
|
require_once dirname(__DIR__) . '/include/scheduler.php';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
|
exit;
|
|
}
|
|
|
|
$id = trim($_POST['id'] ?? '');
|
|
$enabled = ($_POST['enabled'] ?? '0') === '1';
|
|
// Which orchestrator's list this click came from. Optional for callers that have only one, but
|
|
// the scheduler always sends it: without it the toggle acts on whichever array declares the
|
|
// script first, which for a script listed in three is right by luck at best.
|
|
$array = trim($_POST['array'] ?? '');
|
|
|
|
if (!$id || !preg_match('/^[a-zA-Z0-9_.\/\-]+\.sh$/', $id) || str_contains($id, '..')) {
|
|
echo json_encode(['ok' => false, 'error' => 'Invalid id']);
|
|
exit;
|
|
}
|
|
|
|
// Shaped like the arrays it may name and nothing else. It is compared against array names read
|
|
// out of master.conf rather than used to build a pattern, but a value that cannot be an array
|
|
// name has no legitimate target and is refused rather than quietly ignored — silently falling
|
|
// back to first-match is how this went wrong in the first place.
|
|
if ($array !== '' && !preg_match('/^[A-Z][A-Z0-9_]*_SCRIPTS$/', $array)) {
|
|
echo json_encode(['ok' => false, 'error' => 'Invalid array']);
|
|
exit;
|
|
}
|
|
|
|
$ok = vv_conf_toggle_script($id, $enabled, $array !== '' ? $array : null);
|
|
echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write master.conf']);
|