#!/bin/bash # Receiver-side helpers: invoke peer-shell over SSH and consume its output. # These run on the local instance (the one initiating the pull) and connect # OUT to a direct-ssh-direct peer using the keypair from peerKeyEnsure. # Inner: SSH options. Strict key check is on; the peer's host key gets # learned/stored in ~/.ssh/known_hosts on first connection. Future hardening # could bind to the fingerprint we stored at pairing time. _peerSshOpts() { local priv; priv="${HOME}/.ssh/libreportal-peer" echo "-i" "$priv" "-o" "BatchMode=yes" "-o" "StrictHostKeyChecking=accept-new" \ "-o" "PasswordAuthentication=no" "-o" "ConnectTimeout=10" } # Read a peer's connection config from its peers row. # Echos: "@ " or empty on miss. _peerSshTarget() { local peer_name="$1" local row; row=$(peerGet "$peer_name") [[ -z "$row" || "$row" == "null" ]] && return 1 local host port user host=$(printf '%s' "$row" | grep -o '"host":"[^"]*"' | head -1 | cut -d'"' -f4) port=$(printf '%s' "$row" | grep -o '"port":[0-9]*' | head -1 | cut -d':' -f2) user=$(printf '%s' "$row" | grep -o '"user":"[^"]*"' | head -1 | cut -d'"' -f4) [[ -z "$host" || -z "$user" ]] && return 1 [[ -z "$port" ]] && port=22 printf '%s@%s %s\n' "$user" "$host" "$port" } # peerExec [args...] # Runs `peer-shell ` on the peer over SSH and prints its output. # Returns the SSH exit status. peerExec() { local peer_name="$1"; shift local target; target=$(_peerSshTarget "$peer_name") || { isError "Peer '$peer_name' has no SSH connection config" return 1 } local user_host port read -r user_host port <<< "$target" # The remote side has the forced-command in authorized_keys, so what we # type after `ssh user@host` is ignored as a command and passed as # $SSH_ORIGINAL_COMMAND. Stringify the verb + args. local remote_cmd="$*" ssh $(_peerSshOpts) -p "$port" "$user_host" "$remote_cmd" } # Probe a peer by calling its peer-shell ping verb. Updates the peers row # status + last_seen. Echos the new status. peerPing() { local peer_name="$1" local out out=$(peerExec "$peer_name" ping 2>&1) local rc=$? local status now now=$(date -Iseconds) if (( rc == 0 )) && [[ "$out" == *'"ok":true'* ]]; then status="ok" elif (( rc != 0 )); then status="unreachable" else status="protocol-error" fi sqlite3 "$(_peerDb)" \ "UPDATE peers SET status='$(peerSqlEscape "$status")', last_seen='$now' WHERE name='$(peerSqlEscape "$peer_name")';" \ 2>/dev/null echo "$status" [[ "$status" == "ok" ]] } # Fetch the peer's app list. Just relays the JSON peer-shell emits. peerListAppsRemote() { local peer_name="$1" peerExec "$peer_name" list-apps }