#!/usr/bin/env bash set -euo pipefail BOOT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEV_SCRIPT_DIR="$(cd "${BOOT_SCRIPT_DIR}/../dev" && pwd)" source_with_nounset_off() { set +u # shellcheck disable=SC1090 source "$1" set -u } blitz_host_from_addr() { local value="${1:-}" if [[ -z "${value}" ]]; then return 1 fi if [[ "${value}" == \[*\]:* ]]; then value="${value#\[}" printf '%s\n' "${value%%]:*}" return 0 fi printf '%s\n' "${value%%:*}" } blitz_load_boot_env() { local env_file local default_time_server if [[ "${BLITZ_BOOT_ENV_LOADED:-0}" == "1" ]]; then return 0 fi # shellcheck disable=SC1091 source "${DEV_SCRIPT_DIR}/load-env.sh" for env_file in \ "${BOOT_SCRIPT_DIR}/robot-boot.env" \ "${BOOT_SCRIPT_DIR}/robot-boot.env.local" do if [[ -f "${env_file}" ]]; then set -a # shellcheck disable=SC1090 source "${env_file}" set +a fi done default_time_server="$(blitz_host_from_addr "${ROBOT_SIDE_OMNISOCKET_SERVER_ADDR:-}" || true)" export BLITZ_BOOT_DELAY_SEC="${BLITZ_BOOT_DELAY_SEC:-30}" export BLITZ_LOG_FILE="${BLITZ_LOG_FILE:-/var/log/blitz-robot/startup.log}" export BLITZ_5G_DIAL_DIR="${BLITZ_5G_DIAL_DIR:-${BOOT_SCRIPT_DIR}}" export BLITZ_5G_SERIAL_PORT="${BLITZ_5G_SERIAL_PORT:-/dev/ttyUSB7}" export BLITZ_5G_INTERFACE="${BLITZ_5G_INTERFACE:-}" export BLITZ_5G_MODEM_SUBNET="${BLITZ_5G_MODEM_SUBNET:-192.168.224.0/22}" export BLITZ_5G_GATEWAY="${BLITZ_5G_GATEWAY:-192.168.225.1}" export BLITZ_5G_SKIP_DHCP="${BLITZ_5G_SKIP_DHCP:-0}" export BLITZ_5G_REMOVE_DEFAULT_ROUTE="${BLITZ_5G_REMOVE_DEFAULT_ROUTE:-1}" export BLITZ_5G_ROUTE_TARGETS="${BLITZ_5G_ROUTE_TARGETS:-106.55.173.235}" export BLITZ_5G_INFO_JSON="${BLITZ_5G_INFO_JSON:-${BLITZ_5G_DIAL_DIR}/modem_network_info.json}" export BLITZ_5G_DISABLE_INTERFACES="${BLITZ_5G_DISABLE_INTERFACES:-}" export BLITZ_5G_SERIAL_WAIT_SEC="${BLITZ_5G_SERIAL_WAIT_SEC:-60}" export BLITZ_5G_ROUTE_WAIT_SEC="${BLITZ_5G_ROUTE_WAIT_SEC:-30}" export BLITZ_TIME_SERVER_IP="${BLITZ_TIME_SERVER_IP:-${default_time_server}}" export BLITZ_TIME_SERVER_PORT="${BLITZ_TIME_SERVER_PORT:-123}" export BLITZ_TIME_SYNC_WAIT_SEC="${BLITZ_TIME_SYNC_WAIT_SEC:-60}" export BLITZ_TIME_SYNC_MAX_OFFSET_SEC="${BLITZ_TIME_SYNC_MAX_OFFSET_SEC:-0.002}" export BLITZ_TIME_SYNC_INTERVAL_SEC="${BLITZ_TIME_SYNC_INTERVAL_SEC:-1}" export BLITZ_ROS_USER="${BLITZ_ROS_USER:-nvidia}" export BLITZ_ROS_SOCKET_WAIT_SEC="${BLITZ_ROS_SOCKET_WAIT_SEC:-20}" export BLITZ_BOOT_ENV_LOADED="1" } blitz_timestamp() { date '+%Y-%m-%d %H:%M:%S%z' } blitz_sanitize_detail() { local detail="${1:-}" detail="${detail//$'\n'/ ; }" detail="${detail//$'\r'/ }" printf '%s' "${detail}" } blitz_log() { local step="${1:-unknown-step}" local action="${2:-unknown-action}" local result="${3:-info}" local details="${4:-}" local exit_code="${5:-0}" printf '%s | %s | %s | %s | %s | %s\n' \ "$(blitz_timestamp)" \ "${step}" \ "${action}" \ "${result}" \ "$(blitz_sanitize_detail "${details}")" \ "${exit_code}" } blitz_join_cmd() { local cmd=() local arg for arg in "$@"; do cmd+=("$(printf '%q' "${arg}")") done printf '%s' "${cmd[*]}" } blitz_require_command() { local command_name="$1" local step="${2:-precheck}" if command -v "${command_name}" >/dev/null 2>&1; then blitz_log "${step}" "require-command" "success" "command=${command_name}" 0 return 0 fi blitz_log "${step}" "require-command" "failure" "missing command: ${command_name}" 127 return 127 } blitz_require_file() { local path="$1" local step="${2:-precheck}" if [[ -f "${path}" ]]; then blitz_log "${step}" "require-file" "success" "path=${path}" 0 return 0 fi blitz_log "${step}" "require-file" "failure" "missing file: ${path}" 1 return 1 } blitz_require_executable() { local path="$1" local step="${2:-precheck}" if [[ -x "${path}" ]]; then blitz_log "${step}" "require-executable" "success" "path=${path}" 0 return 0 fi blitz_log "${step}" "require-executable" "failure" "missing executable: ${path}" 1 return 1 } blitz_require_root() { local step="${1:-precheck}" if [[ "${EUID}" -eq 0 ]]; then blitz_log "${step}" "require-root" "success" "uid=${EUID}" 0 return 0 fi blitz_log "${step}" "require-root" "failure" "root privileges are required" 1 return 1 } blitz_run() { local step="$1" local action="$2" local rc shift 2 blitz_log "${step}" "${action}" "start" "$(blitz_join_cmd "$@")" 0 if "$@"; then blitz_log "${step}" "${action}" "success" "$(blitz_join_cmd "$@")" 0 return 0 else rc=$? fi blitz_log "${step}" "${action}" "failure" "$(blitz_join_cmd "$@")" "${rc}" return "${rc}" } blitz_route_ready() { local target_ip="$1" local expected_interface="${2:-}" local route_output route_output="$(ip route get "${target_ip}" 2>&1 || true)" if [[ -z "${route_output}" ]]; then return 1 fi if [[ "${route_output}" == *"unreachable"* || "${route_output}" == *"prohibit"* ]]; then return 1 fi if [[ -n "${expected_interface}" && "${route_output}" != *" dev ${expected_interface} "* && "${route_output}" != *" dev ${expected_interface}" ]]; then return 1 fi printf '%s\n' "${route_output}" return 0 }