#!/usr/bin/env bash set -euo pipefail env_file="${1:-}" if [[ -z "${env_file}" ]]; then echo "Usage: $0 " exit 1 fi if [[ ! -f "${env_file}" ]]; then echo "Missing env file: ${env_file}" exit 1 fi set_kv() { local key="$1" local value="$2" local esc esc="${value//\\/\\\\}" esc="${esc//&/\\&}" esc="${esc//|/\\|}" if grep -q "^${key}=" "${env_file}"; then sed -i -E "s|^${key}=.*|${key}=${esc}|" "${env_file}" else echo "${key}=${value}" >> "${env_file}" fi } get_kv() { local key="$1" local value value="$(grep -E "^${key}=" "${env_file}" | tail -n1 | cut -d= -f2- || true)" echo "${value}" } is_valid_port() { local port="$1" [[ "${port}" =~ ^[0-9]+$ ]] || return 1 (( port >= 1 && port <= 65535 )) } port_in_use() { local port="$1" if ! command -v ss >/dev/null 2>&1; then # If we cannot inspect sockets, do not block startup. Keep current/default value. return 1 fi if ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -E -q "(^|:|\\])${port}$"; then return 0 fi if ss -H -lun 2>/dev/null | awk '{print $5}' | grep -E -q "(^|:|\\])${port}$"; then return 0 fi return 1 } port_set_is_free() { local api_port="$1" local gateway_port="$((api_port - 1))" local p2p_port="$((api_port + 1))" local qdn_port="$((api_port + 3))" if ! is_valid_port "${api_port}" || ! is_valid_port "${gateway_port}" || ! is_valid_port "${p2p_port}" || ! is_valid_port "${qdn_port}"; then return 1 fi if port_in_use "${api_port}" || port_in_use "${gateway_port}" || port_in_use "${p2p_port}" || port_in_use "${qdn_port}"; then return 1 fi return 0 } pick_api_port() { local candidates=("$@") local candidate for candidate in "${candidates[@]}"; do if port_set_is_free "${candidate}"; then echo "${candidate}" return 0 fi done return 1 } # Preferred order requested for API: # 12391, then 22391/32391/42391/52391, then 12291/22291/32291/42291/52291. preferred_api=( 12391 22391 32391 42391 52391 12291 22291 32291 42291 52291 ) configured_api="$(get_kv "QORTAL_NODE_API_HOST_PORT")" selected_api="" # Respect explicit non-default API port overrides and keep P2P in sync as API+1. if is_valid_port "${configured_api}" && [[ "${configured_api}" != "12391" ]]; then selected_api="${configured_api}" else if selected_api="$(pick_api_port "${preferred_api[@]}")"; then : else echo "Unable to find free Qortal Gateway/API/P2P/QDN port set in requested range/order." >&2 exit 1 fi fi selected_gateway="$((selected_api - 1))" selected_p2p="$((selected_api + 1))" selected_qdn="$((selected_api + 3))" if ! is_valid_port "${selected_gateway}" || ! is_valid_port "${selected_p2p}" || ! is_valid_port "${selected_qdn}"; then echo "Computed invalid Qortal Gateway/P2P/QDN port from API port ${selected_api}: Gateway=${selected_gateway}, P2P=${selected_p2p}, QDN=${selected_qdn}" >&2 exit 1 fi set_kv "QORTAL_NODE_GATEWAY_HOST_PORT" "${selected_gateway}" set_kv "QORTAL_NODE_API_HOST_PORT" "${selected_api}" set_kv "QORTAL_NODE_P2P_HOST_PORT" "${selected_p2p}" set_kv "QORTAL_NODE_QDN_HOST_PORT" "${selected_qdn}" echo "Selected Qortal node host ports: Gateway=${selected_gateway}, API=${selected_api}, P2P=${selected_p2p}, QDN=${selected_qdn}" if port_in_use "${selected_gateway}" || port_in_use "${selected_api}" || port_in_use "${selected_p2p}" || port_in_use "${selected_qdn}"; then echo "Note: selected Gateway/API/P2P/QDN port appears in use; this may be expected if qortal_node is already running." fi exit 0