// // RESPONSE // {"ok":true,"output":string} action ran; output is the script's own report // {"ok":false,"error":string} bad method, unknown action, or unresolvable host // // DEPENDS ON // include/fallback.php vv_pt_peer_lookup(), vv_pt_ts_peers(), vv_pt_ssh() // Fallback/fallback.sh --stop // Fallback/fallback_test.sh --stop // ═══════════════════════════════════════════════════════════════════════════════════════════════ require_once dirname(__DIR__) . '/include/fallback.php'; header('Content-Type: application/json'); if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['ok' => false, 'error' => 'POST only']); exit; } $action = (string)($_POST['action'] ?? ''); $slot = strtolower((string)($_POST['host'] ?? '')); $allowed = ['stop', 'stop_test', 'start_dry', 'clear_lock']; if (!in_array($action, $allowed, true)) { echo json_encode(['ok' => false, 'error' => 'Unknown action']); exit; } $hosts = vv_fb_known_hosts(); if (!isset($hosts[$slot])) { echo json_encode(['ok' => false, 'error' => 'Unknown host']); exit; } $isMe = ($slot === vv_detect_host()); $scripts = rtrim(SCRIPTS_DIR, '/'); // The command, as the script that owns the operation would be invoked by hand. $cmds = [ 'stop' => 'bash ' . escapeshellarg("$scripts/Fallback/fallback.sh") . ' --stop 2>&1', 'stop_test' => 'bash ' . escapeshellarg("$scripts/Fallback/fallback_test.sh") . ' --stop 2>&1', // setsid so it outlives this request. No redirect: fallback.sh writes its own persistent // log when stdout is not a terminal, so an ad-hoc preview and the array-start daemon leave // their record in the same file rather than one going to tmpfs and vanishing on reboot. 'start_dry' => 'setsid bash ' . escapeshellarg("$scripts/Fallback/fallback.sh") . ' --dry-run --log > /dev/null 2>&1 < /dev/null & echo started', 'clear_lock' => 'rm -f /tmp/unraid_locks/fallback.lock /tmp/unraid_locks/fallback_test.lock && echo cleared', ]; if ($isMe) { @mkdir('/tmp/varaverk', 0755, true); $out = (string)shell_exec($cmds[$action]); echo json_encode(['ok' => true, 'output' => trim($out)]); exit; } // Remote: same command, same script, over the SSH this file's neighbours already use. $tsPeers = vv_pt_ts_peers(); $ts = vv_pt_peer_lookup($tsPeers, $hosts[$slot]); $ip = $ts['ip'] ?? null; $myId = strtoupper(vv_detect_host()); $sshKey = vv_fb_scalar(vv_read_conf_raw(vv_detect_host() . '.conf'), $myId . '_SSH_KEY'); if (!$ip || !$sshKey) { echo json_encode(['ok' => false, 'error' => 'Partner not resolvable — no Tailscale IP or no SSH key']); exit; } // The remote's SCRIPTS_DIR is not this host's: appdata mode on one side and flash on the other // is the normal case on this mesh, so ask the partner where it keeps them. $remoteDir = trim((string)vv_pt_ssh($ip, $sshKey, 'sed -n \'s/^SCRIPTS_DIR="\(.*\)"$/\1/p\' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null')); if ($remoteDir === '') $remoteDir = '/boot/config/plugins/varaverk'; $remoteCmds = [ 'stop' => "bash '$remoteDir/Fallback/fallback.sh' --stop 2>&1", 'stop_test' => "bash '$remoteDir/Fallback/fallback_test.sh' --stop 2>&1", 'start_dry' => "setsid bash '$remoteDir/Fallback/fallback.sh'" . " --dry-run --log > /dev/null 2>&1 < /dev/null & echo started", 'clear_lock' => 'rm -f /tmp/unraid_locks/fallback.lock /tmp/unraid_locks/fallback_test.lock && echo cleared', ]; $out = vv_pt_ssh($ip, $sshKey, $remoteCmds[$action]); echo json_encode(['ok' => true, 'output' => trim((string)$out)]);