Watchdog and Monitor looked the partner up by exact key, and the remote probe passed a quote to cut as a filename
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/common.php';
|
||||
require_once __DIR__ . '/partnership.php'; // vv_pt_peer_match() — tailnet name vs conf hostname
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
@@ -94,7 +95,11 @@ function vv_partner_state(): array {
|
||||
if (!$hostname) continue;
|
||||
$isMe = ($id === $myHostId);
|
||||
$isOwner = strcasecmp($id, $vars['PARTNERSHIP_OWNER_HOST'] ?? '') === 0;
|
||||
$online = $isMe ? true : ($tsPeers[strtolower($hostname)] ?? null);
|
||||
// Exact-key lookup, until now. The tailnet name and the conf hostname differ by one
|
||||
// character on this mesh, so the partner card reported a host that was up as unknown.
|
||||
// Same unambiguous-prefix rule as everywhere else — one candidate or none.
|
||||
$peerKey = $isMe ? null : vv_pt_peer_match(array_keys($tsPeers), $hostname);
|
||||
$online = $isMe ? true : ($peerKey !== null ? $tsPeers[$peerKey] : null);
|
||||
$onboardPhase = $isMe ? null
|
||||
: (($setupDb[$id . '_PHASE2_DONE'] ?? '') === 'true' ? 2
|
||||
: (($setupDb[$id . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
|
||||
|
||||
@@ -199,15 +199,24 @@ function vv_pt_ts_peers(): array {
|
||||
// none — server1 must never resolve to server10 because it happens to share a prefix, and this
|
||||
// is never similarity scoring. Same rule as vv_resolve_tailscale_ip(), which applies it to
|
||||
// `tailscale status` text rather than to the parsed peer array.
|
||||
function vv_pt_peer_lookup(array $tsPeers, string $hostname): array {
|
||||
// The matching rule itself, separated from the value it looks up, because the peer map is not
|
||||
// always the same shape: include/monitor.php keys hostname => bool while everything else keys
|
||||
// hostname => array. Four call sites had each written their own exact-key lookup and all four
|
||||
// rendered this mesh's partner as dark. Returns the matching KEY, or null.
|
||||
function vv_pt_peer_match(array $peerNames, string $hostname): ?string {
|
||||
$label = strtolower($hostname);
|
||||
if (isset($tsPeers[$label])) return $tsPeers[$label];
|
||||
if (in_array($label, $peerNames, true)) return $label;
|
||||
|
||||
$cand = [];
|
||||
foreach ($tsPeers as $peerName => $peer) {
|
||||
if (str_starts_with($peerName, $label) || str_starts_with($label, $peerName)) $cand[] = $peer;
|
||||
foreach ($peerNames as $peerName) {
|
||||
if (str_starts_with($peerName, $label) || str_starts_with($label, $peerName)) $cand[] = $peerName;
|
||||
}
|
||||
return count($cand) === 1 ? $cand[0] : ['online' => null, 'active' => false, 'ip' => null];
|
||||
return count($cand) === 1 ? $cand[0] : null;
|
||||
}
|
||||
|
||||
function vv_pt_peer_lookup(array $tsPeers, string $hostname): array {
|
||||
$key = vv_pt_peer_match(array_keys($tsPeers), $hostname);
|
||||
return $key !== null ? $tsPeers[$key] : ['online' => null, 'active' => false, 'ip' => null];
|
||||
}
|
||||
|
||||
// ── SSH helper — run a single command on a remote host ────────────────────────
|
||||
|
||||
@@ -366,8 +366,12 @@ function vv_wd_remote_data(string $ip, string $sshKey, string $restartLogPath):
|
||||
. 'sf="$sd/data/state"; [ -d "$sf" ] || sf="$sd/State_Files"; '
|
||||
. 'db="$sd/data/db"; [ -d "$db" ] || db="$sd/data"; '
|
||||
. "printf 'UPTIME:%s\nLOAD:%s\nCORES:%s\nDAEMON:%s\nOOM:%s\nBASELINECOUNT:%s\nBASELINEAGE:%s\n---MEMINFO---\n%s\n---RW---\n%s\n---DOCK---\n%s\n---SKIP---\n%s\n---SYS---\n%s\n---REBOOT---\n%s\n---RESTART---\n%s\n---STORAGE---\n%s\n---NETWORK---\n%s\n' "
|
||||
. '"$(cat /proc/uptime|cut -d\" \" -f1)" '
|
||||
. '"$(cat /proc/loadavg|cut -d\" \" -f1)" '
|
||||
// awk, not `cut -d" "`. This is a PHP SINGLE-quoted string, which does not process \" —
|
||||
// so the shell received a literal backslash-quote, cut read the quote as a FILENAME
|
||||
// ("cut: '\"': No such file or directory"), and UPTIME and LOAD came back empty while every
|
||||
// quote-free field beside them parsed fine. awk needs no delimiter argument at all.
|
||||
. '"$(awk \'{print $1}\' /proc/uptime)" '
|
||||
. '"$(awk \'{print $1}\' /proc/loadavg)" '
|
||||
. '"$(nproc)" '
|
||||
. '"$(docker info >/dev/null 2>&1 && echo ok || echo err)" '
|
||||
. '"$(cat "$sf/system_watchdog_oom.db" 2>/dev/null||echo 0)" '
|
||||
@@ -550,8 +554,11 @@ function vv_wd_all(): array {
|
||||
$nodes = [];
|
||||
foreach ($hosts as $slot => $hostname) {
|
||||
$isMe = ($slot === $currentHost || $currentHost === 'unknown');
|
||||
$tsLabel = strtolower($hostname);
|
||||
$ts = $tsPeers[$tsLabel] ?? ['online' => null, 'active' => false, 'ip' => null];
|
||||
// Exact-key only, until now — the third copy of that lookup in this codebase and the
|
||||
// third to render a live partner as dark. This mesh's conf name and tailnet name differ
|
||||
// by one character, so HOST2 missed every time and its whole card read UNREACHABLE /
|
||||
// "No data" while HOST2's own page showed the same watchdogs reporting OK.
|
||||
$ts = vv_pt_peer_lookup($tsPeers, $hostname);
|
||||
$ip = $ts['ip'] ?? null;
|
||||
$raw = vv_read_conf_raw($slot . '.conf');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user