#!/bin/bash # Reachability check for a peer. The meaning of "reachable" depends on kind: # backup-channel At least one snapshot from this peer's hostname is # visible in the configured location within the last # 30 days (or ever, if it's just been added). # direct-ssh-direct SSH connect + 'peer-shell ping' (Phase 3). # direct-ssh-via-relay Open relay session + 'peer-shell ping' (Phase 3b). # # Updates the peer's status + last_seen columns on success/failure so the UI # can render a colored dot without re-running the check on every page load. peerCheckReachable() { local name="$1" if [[ -z "$name" ]]; then isError "peerCheckReachable: name required"; return 1; fi local row row=$(sqlite3 "$(_peerDb)" "SELECT id, kind, config_json FROM peers WHERE name='$(peerSqlEscape "$name")';" 2>/dev/null) if [[ -z "$row" ]]; then isError "No peer named '$name'" return 1 fi local id kind cfg IFS='|' read -r id kind cfg <<< "$row" local new_status="unknown" local now now=$(date -Iseconds) case "$kind" in backup-channel) local hostname loc_idx hostname=$(printf '%s' "$cfg" | grep -o '"hostname":"[^"]*"' | head -1 | cut -d'"' -f4) loc_idx=$(printf '%s' "$cfg" | grep -o '"loc_idx":[0-9]*' | head -1 | cut -d':' -f2) if [[ -z "$hostname" ]]; then new_status="config-error" elif [[ -z "$loc_idx" ]]; then # No preferred location — try any enabled location. local found="" while IFS= read -r idx; do [[ -z "$idx" ]] && continue if engineSnapshotsJson "$idx" "" "$hostname" 2>/dev/null | grep -q '"short_id":'; then found="$idx"; break fi done < <(resticEnabledLocations) [[ -n "$found" ]] && new_status="ok" || new_status="no-snapshots" else if engineSnapshotsJson "$loc_idx" "" "$hostname" 2>/dev/null | grep -q '"short_id":'; then new_status="ok" else new_status="no-snapshots" fi fi ;; direct-ssh-direct) # peerPing already updates the row + returns the status name on # stdout. We re-read from the DB at the bottom of the function so # callers see the same value. new_status=$(peerPing "$name" 2>/dev/null) [[ -z "$new_status" ]] && new_status="unreachable" # peerPing wrote status + last_seen already; short-circuit the # second UPDATE below. echo "$new_status" [[ "$new_status" == "ok" ]] return $? ;; direct-ssh-via-relay) new_status="needs-connect" ;; *) new_status="unknown-kind" ;; esac sqlite3 "$(_peerDb)" \ "UPDATE peers SET status='$(peerSqlEscape "$new_status")', last_seen='$now' WHERE id=$id;" 2>/dev/null echo "$new_status" [[ "$new_status" == "ok" ]] } # Check every peer; useful for the WebUI's "Refresh" button. peerCheckAll() { local name while IFS= read -r name; do [[ -z "$name" ]] && continue local status status=$(peerCheckReachable "$name") isNotice " $name → $status" done < <(sqlite3 "$(_peerDb)" "SELECT name FROM peers ORDER BY name;" 2>/dev/null) }