#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck disable=SC1091 source "${SCRIPT_DIR}/common.sh" STEP="5g-dial" append_route_targets() { local raw_list="$1" local target if [[ -z "${raw_list}" ]]; then return 0 fi for target in ${raw_list//,/ }; do if [[ -z "${target}" ]]; then continue fi dial_cmd+=(--route-target "${target}") done } read_detected_interface() { local info_json="$1" if [[ ! -f "${info_json}" ]]; then return 1 fi python3 -c 'import json, sys; print((json.load(open(sys.argv[1], encoding="utf-8")).get("interface") or "").strip())' "${info_json}" } disable_interfaces() { local raw_list="$1" local iface local nmcli_available=0 if [[ -z "${raw_list}" ]]; then return 0 fi if command -v nmcli >/dev/null 2>&1; then nmcli_available=1 fi for iface in ${raw_list//,/ }; do if [[ -z "${iface}" ]]; then continue fi blitz_log "${STEP}" "disable-interface" "start" "iface=${iface}" 0 if [[ "${nmcli_available}" -eq 1 ]]; then nmcli device disconnect "${iface}" >/dev/null 2>&1 || true fi if ip link show dev "${iface}" >/dev/null 2>&1; then if ip link set dev "${iface}" down; then blitz_log "${STEP}" "disable-interface" "success" "iface=${iface}" 0 else rc=$? blitz_log "${STEP}" "disable-interface" "failure" "iface=${iface}" "${rc}" return "${rc}" fi else blitz_log "${STEP}" "disable-interface" "success" "iface=${iface} not present, skipping" 0 fi done } wait_for_serial() { local serial_port="$1" local timeout_sec="$2" local waited=0 while (( waited < timeout_sec )); do if [[ -e "${serial_port}" ]]; then blitz_log "${STEP}" "wait-serial" "success" "serial_port=${serial_port} waited_sec=${waited}" 0 return 0 fi if (( waited == 0 || waited % 5 == 0 )); then blitz_log "${STEP}" "wait-serial" "waiting" "serial_port=${serial_port} waited_sec=${waited}" 0 fi sleep 1 waited=$(( waited + 1 )) done blitz_log "${STEP}" "wait-serial" "failure" "serial_port=${serial_port} timeout_sec=${timeout_sec}" 1 return 1 } wait_for_route() { local target_ip="$1" local timeout_sec="$2" local expected_interface="${3:-}" local waited=0 local route_output while (( waited < timeout_sec )); do route_output="$(blitz_route_ready "${target_ip}" "${expected_interface}" || true)" if [[ -n "${route_output}" ]]; then blitz_log "${STEP}" "route-check" "success" "target_ip=${target_ip} interface=${expected_interface:-auto} route=${route_output}" 0 return 0 fi if (( waited == 0 || waited % 5 == 0 )); then blitz_log "${STEP}" "route-check" "waiting" "target_ip=${target_ip} interface=${expected_interface:-auto} waited_sec=${waited}" 0 fi sleep 1 waited=$(( waited + 1 )) done blitz_log "${STEP}" "route-check" "failure" "target_ip=${target_ip} interface=${expected_interface:-auto} timeout_sec=${timeout_sec}" 1 return 1 } blitz_load_boot_env blitz_require_root "${STEP}" blitz_require_command ip "${STEP}" blitz_require_command python3 "${STEP}" blitz_require_file "${BLITZ_5G_DIAL_DIR}/rndis_dial.py" "${STEP}" if [[ -z "${BLITZ_TIME_SERVER_IP}" ]]; then blitz_log "${STEP}" "precheck" "failure" "BLITZ_TIME_SERVER_IP is empty and no fallback could be derived" 1 exit 1 fi disable_interfaces "${BLITZ_5G_DISABLE_INTERFACES:-}" if [[ -n "${BLITZ_5G_INTERFACE:-}" ]]; then route_output="$(blitz_route_ready "${BLITZ_TIME_SERVER_IP}" "${BLITZ_5G_INTERFACE}" || true)" if [[ -n "${route_output}" ]]; then blitz_log "${STEP}" "dial" "already_up" "target_ip=${BLITZ_TIME_SERVER_IP} interface=${BLITZ_5G_INTERFACE} route=${route_output}" 0 exit 0 fi else blitz_log "${STEP}" "route-check" "info" "BLITZ_5G_INTERFACE is empty, skipping pre-dial route shortcut and using auto-detect mode" 0 fi wait_for_serial "${BLITZ_5G_SERIAL_PORT}" "${BLITZ_5G_SERIAL_WAIT_SEC}" dial_cmd=( python3 rndis_dial.py --serial-port "${BLITZ_5G_SERIAL_PORT}" --modem-subnet "${BLITZ_5G_MODEM_SUBNET}" ) if [[ -n "${BLITZ_5G_INTERFACE:-}" ]]; then dial_cmd+=(--interface "${BLITZ_5G_INTERFACE}") fi case "${BLITZ_5G_SKIP_DHCP:-0}" in 1|true|TRUE|yes|YES) dial_cmd+=(--skip-dhcp) ;; esac case "${BLITZ_5G_REMOVE_DEFAULT_ROUTE:-1}" in 1|true|TRUE|yes|YES) dial_cmd+=(--remove-default-route --gateway "${BLITZ_5G_GATEWAY}" --route-target "${BLITZ_TIME_SERVER_IP}") append_route_targets "${BLITZ_5G_ROUTE_TARGETS:-}" ;; esac pushd "${BLITZ_5G_DIAL_DIR}" >/dev/null blitz_run "${STEP}" "dial" "${dial_cmd[@]}" popd >/dev/null resolved_interface="${BLITZ_5G_INTERFACE:-}" if [[ -z "${resolved_interface}" ]]; then resolved_interface="$(read_detected_interface "${BLITZ_5G_INFO_JSON}" || true)" if [[ -n "${resolved_interface}" ]]; then blitz_log "${STEP}" "resolve-interface" "success" "resolved interface from ${BLITZ_5G_INFO_JSON}: ${resolved_interface}" 0 else blitz_log "${STEP}" "resolve-interface" "failure" "failed to read detected interface from ${BLITZ_5G_INFO_JSON}" 1 fi fi if [[ -n "${resolved_interface}" ]]; then wait_for_route "${BLITZ_TIME_SERVER_IP}" "${BLITZ_5G_ROUTE_WAIT_SEC}" "${resolved_interface}" blitz_log "${STEP}" "complete" "success" "5G dial completed and route is ready on ${resolved_interface}" 0 else blitz_log "${STEP}" "complete" "success" "5G dial completed but route wait was skipped because no interface could be resolved; refer to rndis_dial.py logs" 0 fi