#!/command/with-contenv bash # shellcheck shell=bash disable=SC1091,SC2015,SC2016 #--------------------------------------------------------------------------------------------- # Copyright (C) 2023, Ramon F. Kolb (kx1t) # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with this program. # If not, see . #--------------------------------------------------------------------------------------------- trap 'pkill -P "$$" || true; s6wrap --quiet --timestamps --prepend=mlat-client --args echo "service stopping"; exit 0' SIGTERM SIGINT SIGHUP SIGQUIT APPNAME="mlat-client" s6wrap=(s6wrap --quiet --timestamps --prepend="${APPNAME}") "${s6wrap[@]}" --args echo "Started as an s6 service" source /scripts/common source /scripts/interpret_ultrafeeder_config MLAT_CMD="/usr/bin/mlat-client" RESTARTTIMER=15 declare -A pid_array if [[ -z "${MLAT_CONFIG}" ]] then "${s6wrap[@]}" --args echo "Warning: MLAT_CONFIG not defined - MLAT will be disabled." exec sleep infinity fi if [[ -z "${MLAT_USER}" ]] && [[ -z "${UUID}" ]] then "${s6wrap[@]}" --args echo "ERROR: either UUID or MLAT_USER must be defined - MLAT will be disabled." exec sleep infinity fi if [[ -z "$LAT$READSB_LAT" ]]; then "${s6wrap[@]}" --args echo "ERROR: READSB_LAT or LAT must be defined - MLAT will be disabled." exec sleep infinity fi if [[ -z "$LONG$READSB_LON" ]]; then "${s6wrap[@]}" --args echo "ERROR: READSB_LON or LONG must be defined - MLAT will be disabled." exec sleep infinity fi if [[ -z "$ALT$READSB_ALT" ]]; then "${s6wrap[@]}" --args echo "ERROR: READSB_ALT or ALT must be defined - MLAT will be disabled." exec sleep infinity fi # MLAT_CONFIG has the following format: # MLAT_CONFIG=mlatserver_1,mlatserver_port_1,x1,y1,z1;mlatserver_2,mlatserver_port_2,x2,y2,z2 etc # where x1,y1,z1; x22,y2,z2; etc are optional and are interpreted as follows: # if it's a number, we'll assume it's a port number for return messages # if it starts with 'uuid=', it's a UUID number for that instance # anything else will be appended as extra parameter(s) to the mlat-client command line # parse MLAT_CONFIG string into mlat_configs array # Strip any extraneous spaces: MLAT_CONFIG="${MLAT_CONFIG#"${MLAT_CONFIG%%[![:space:]]*}"}" # strip leading space MLAT_CONFIG="${MLAT_CONFIG//; /;}" # remove any newlines: MLAT_CONFIG="${MLAT_CONFIG//$'\n'/}" readarray -td ";" mlat_configs < <(printf '%s' "${MLAT_CONFIG}") # wait until readsb is established... if ! pgrep readsb >/dev/null; then if [[ -z "${LOGLEVEL}" ]] || [[ "${LOGLEVEL,,}" == "verbose" ]]; then "${s6wrap[@]}" --args echo "Delaying start of MLAT client(s) until container is established..." fi while ! pgrep readsb >/dev/null do sleep 2 & wait $! done # wait 2 seconds after readsb process exists sleep 2 & wait $! fi # Now loop through the MLAT_CONFIG items and start up an Mlat_client for each of them: for instance in "${mlat_configs[@]}" do [[ -z "${instance}" ]] && continue || true # put individual params into the $params array: readarray -td "," params < <(printf '%s' "${instance}") # Check if the params array has values for the mandatory elements: if [[ -z "${params[0]}" ]] || [[ -z "${params[1]}" ]] then "${s6wrap[@]}" --args echo "ERROR -- MLAT_CONFIG is malformed: \"${instance}\". Stopping MLAT execution." # shellcheck disable=SC2046 kill $(ps -s $$ -o pid=) exec sleep infinity fi # Now process the the arguments # The order doesn't matter, we'll do pattern matching: # If the argument is a number, then it must be the beast_results port # If the argument starts with "uuid=", then it must be a UUID, etc. # If the argument isn't any of the above, then it's an "extra argument" unset uuid_arg lat_arg lon_arg alt_arg input_connect_arg return_port_arg extra_args for ((i=2; i<${#params[*]}; i++)) do if [[ -n "${params[i]}" ]] && [[ "${params[i]}" =~ ^[0-9]+$ ]]; then # It's a number so it must be the return port return_port_arg="${params[i]}" elif header="${params[i]:0:5}" && [[ "${header,,}" == "uuid=" ]]; then # It's a UUID uuid_arg="${params[i]#*=}" elif header="${params[i]:0:5}" && [[ "${header,,}" == "name=" ]]; then # It's a MLAT_NAME name_arg="${params[i]#*=}" elif header="${params[i]:0:4}" && [[ "${header,,}" == "lat=" ]]; then # It's a latitude lat_arg="${params[i]#*=}" elif header="${params[i]:0:4}" && [[ "${header,,}" == "lon=" ]]; then # It's a longitude lon_arg="${params[i]#*=}" elif header="${params[i]:0:4}" && [[ "${header,,}" == "alt=" ]]; then # It's a latitude alt_arg="${params[i]#*=}" elif header="${params[i]:0:14}" && [[ "${header,,}" == "input_connect=" ]]; then #It's the input_connect parameter value input_connect_arg="${params[i]#*=}" else # It's an Extra Args string extra_args="$extra_args ${params[i]}" fi done # ------------------------------------------------ # Build the MLAT parameter string: MLAT_PARAM=(--input-type "${MLAT_INPUT_TYPE:-auto}") MLAT_PARAM+=(--server "${params[0]}:${params[1]}") servername=${params[0]} # add return port: [[ -n "${return_port_arg}" ]] && MLAT_PARAM+=("--results beast,listen,${return_port_arg}") || true # add input-connect to the param array: MLAT_PARAM+=(--input-connect "${input_connect_arg:-localhost:30005}") if [[ -n "${name_arg}" ]] || [[ -n "${MLAT_USER}" ]]; then MLAT_PARAM+=(--user "${name_arg:-${MLAT_USER}}") else rnd="${RANDOM}" "${s6wrap[@]}" --args echo "WARNING: MLAT_USER is not set - using random number \"${rnd}\" as MLAT_USER" MLAT_PARAM+=(--user "${rnd}") fi # add LAT/LON/ALT to instance: if [[ -n "${lat_arg}" ]]; then MLAT_PARAM+=(--lat "${lat_arg}") elif [[ -n "${LAT}" ]]; then MLAT_PARAM+=(--lat "${LAT}") elif [[ -n "${READSB_LAT}" ]]; then MLAT_PARAM+=(--lat "${READSB_LAT}") fi if [[ -n "${lon_arg}" ]]; then MLAT_PARAM+=(--lon "${lon_arg}") elif [[ -n "${LONG}" ]]; then MLAT_PARAM+=(--lon "${LONG}") elif [[ -n "${READSB_LON}" ]]; then MLAT_PARAM+=(--lon "${READSB_LON}") fi if [[ -n "${alt_arg}" ]]; then MLAT_PARAM+=(--alt "${alt_arg}") elif [[ -n "${ALT}" ]]; then MLAT_PARAM+=(--alt "${ALT}") elif [[ -n "${READSB_ALT}" ]]; then MLAT_PARAM+=(--alt "${READSB_ALT}") fi # Add UUID to instance if [[ -n "$uuid_arg" ]]; then MLAT_PARAM+=("--uuid ${uuid_arg}") elif [[ -n "${UUID}" ]]; then MLAT_PARAM+=("--uuid ${UUID}") else "${s6wrap[@]}" --args echo "WARNING: UUID is not defined, proceeding without UUID" fi # Now add the extra_args, if any: [[ -n "${extra_args}" ]] && MLAT_PARAM+=("${extra_args}") || true # ------------------------------------------------ # Create the command exec string: # shellcheck disable=SC2048,SC2086 execstring="$(echo ${MLAT_CMD} ${MLAT_PARAM[*]} | xargs)" # stagger by 15 second so they don't all start at the same time sleep 15 & wait $! # ------------------------------------------------ # run this Mlat_client instance in the background: #shellcheck disable=SC2069,SC2086 if [[ -z "${LOGLEVEL}" ]] || [[ "${LOGLEVEL,,}" == "verbose" ]]; then "${s6wrap[@]}" --prepend="${APPNAME}][${servername}" --args echo "starting: ${MLAT_CMD} ${MLAT_PARAM[*]}" "${s6wrap[@]}" --prepend="${APPNAME}][${servername}" --args ${execstring} & elif [[ "${LOGLEVEL,,}" == "error" ]]; then "${s6wrap[@]}" --ignore-stdout --prepend="${APPNAME}][${servername}" --args ${execstring} & elif [[ "${LOGLEVEL,,}" == "none" ]]; then "${s6wrap[@]}" --ignore-stderr --ignore-stdout --prepend="${APPNAME}][${servername}" --args ${execstring} & fi # pid_array is indexed by the PID of each mlat_client and contains the MLAT_PARAMs for that instance # This is done so we can monitor them and restart them if needed pid_array[$!]="${MLAT_PARAM[*]}" done sleep 5 & wait $! # All MLAT Clients are up, so set the flag. This flag is read by MLATHUB as a sign that it's # safe to start up touch /run/mlathub_up # Now iterate over all MLAT-client instances and check if they are still running: while true do for mlat_pid in "${!pid_array[@]}" do if ! kill -0 "${mlat_pid}" >/dev/null 2>&1 then # it exited - let's restart: sleep "${RESTARTTIMER}" & wait $! servername="$(awk '{print $4}' <<< "${pid_array[$mlat_pid]}")" servername="${servername%%:*}" [[ "${LOGLEVEL,,}" != "none" ]] && "${s6wrap[@]}" --prepend="${APPNAME}[${servername}" --args echo "MLAT_Client ${servername} exited. Attempting to restart." || true # shellcheck disable=SC2086 execstring="$(echo ${MLAT_CMD} ${pid_array[$mlat_pid]} | xargs)" #shellcheck disable=SC2069,SC2086 if [[ -z "${LOGLEVEL}" ]] || [[ "${LOGLEVEL,,}" == "verbose" ]]; then "${s6wrap[@]}" --prepend="${APPNAME}][${servername}" --args ${execstring} & elif [[ "${LOGLEVEL,,}" == "error" ]]; then "${s6wrap[@]}" --ignore-stdout --prepend="${APPNAME}][${servername}" --args ${execstring} & elif [[ "${LOGLEVEL,,}" == "none" ]]; then "${s6wrap[@]}" --ignore-stderr --ignore-stdout --prepend="${APPNAME}][${servername}" --args ${execstring} & fi pid_array[$!]="${pid_array[${mlat_pid}]}" unset "pid_array[${mlat_pid}]" fi done sleep 10 & wait $! done