1
0
Fork 0
mirror of https://github.com/sdr-enthusiasts/docker-adsb-ultrafeeder.git synced 2024-11-22 13:50:14 +00:00
docker-adsb-ultrafeeder/rootfs/etc/s6-overlay/scripts/mlat-client
2024-06-07 15:56:07 +02:00

312 lines
13 KiB
Text
Executable file

#!/command/with-contenv bash
# shellcheck shell=bash disable=SC1091,SC2015,SC2016,SC2001
#---------------------------------------------------------------------------------------------
# Copyright (C) 2023-2024, Ramon F. Kolb (kx1t) and contributors
#
# 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 <https://www.gnu.org/licenses/>.
#---------------------------------------------------------------------------------------------
trap 'pkill -P "$$" || true; s6wrap --quiet --timestamps --prepend=mlat-client --args echo "service stopping"; exit 0' SIGTERM SIGINT SIGHUP SIGQUIT
source /scripts/common
source /scripts/interpret_ultrafeeder_config
s6wrap=(s6wrap --quiet --timestamps --prepend="$SCRIPT_NAME")
"${s6wrap[@]}" --args echo "Started as an s6 service"
MLAT_CMD="/usr/bin/mlat-client"
LOGLEVEL="${LOGLEVEL,,}"
LOGLEVEL="${LOGLEVEL:-verbose}"
chk_disabled "$LOGLEVEL" && LOGLEVEL="none" || true
if ! [[ "$LOGLEVEL" =~ ^(verbose|error|none)$ ]]; then
[[ -n "$LOGLEVEL" ]] && "${s6wrap[@]}" --args echo "[WARNING] LOGLEVEL set to an unknown value. Defaulting to \"verbose\"" || true
LOGLEVEL="verbose"
fi
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" ]] && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_CONFIG" && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_NET_CONNECTOR"; then
"${s6wrap[@]}" --args echo "ERROR: READSB_LAT or LAT must be defined or GPSD must be enabled - MLAT will be disabled."
exec sleep infinity
fi
if [[ -z "$LONG$READSB_LON" ]] && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_CONFIG" && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_NET_CONNECTOR"; then
"${s6wrap[@]}" --args echo "ERROR: READSB_LON or LONG must be defined or GPSD must be enabled - MLAT will be disabled."
exec sleep infinity
fi
if [[ -z "$ALT$READSB_ALT" ]] && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_CONFIG" && ! grep -qi "gpsd_in" <<< "$ULTRAFEEDER_NET_CONNECTOR"; then
"${s6wrap[@]}" --args echo "ERROR: READSB_ALT or ALT must be defined or GPSD must be enabled - 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 if defined
if [[ -n "${return_port_arg}" ]] && ! chk_disabled "${return_port_arg}"; then
MLAT_PARAM+=("--results beast,listen,${return_port_arg}")
fi
# send data to MLATHUB unless disabled
if ! chk_disabled "${return_port_arg}" && ! chk_enabled "${MLATHUB_DISABLE}"; then
MLAT_PARAM+=("--results beast,connect,localhost:${MLATHUB_BEAST_IN_PORT:-31004}")
fi
# 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:
# see if we need to use GPS coords:
unset new_lat
unset new_lon
unset new_alt
if [[ -f /run/.gpsd_is_active && -f /run/readsb/receiver.json ]]; then
new_lat="$(jq -r .lat /run/readsb/receiver.json)"
new_lon="$(jq -r .lon /run/readsb/receiver.json)"
new_alt="$(jq -r .alt_m_amsl /run/readsb/receiver.json)"
if [[ "$new_lat" == "null" ]] || [[ "$new_lon" == "null" ]] || [[ "$new_alt" == "null" ]]; then
unset new_lat
unset new_lon
unset new_alt
fi
fi
if [[ -n "${new_lat:-$lat_arg}" ]]; then
MLAT_PARAM+=(--lat "${new_lat:-$lat_arg}")
elif [[ -n "${LAT}" ]]; then
MLAT_PARAM+=(--lat "${LAT}")
elif [[ -n "${READSB_LAT}" ]]; then
MLAT_PARAM+=(--lat "${READSB_LAT}")
fi
if [[ -n "${new_lon:-$lon_arg}" ]]; then
MLAT_PARAM+=(--lon "${new_lon:-$lon_arg}")
elif [[ -n "${LONG}" ]]; then
MLAT_PARAM+=(--lon "${LONG}")
elif [[ -n "${READSB_LON}" ]]; then
MLAT_PARAM+=(--lon "${READSB_LON}")
fi
if [[ -n "${new_alt:-$alt_arg}" ]]; then
MLAT_PARAM+=(--alt "${new_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 "${MLAT_STARTUP_STAGGER:-15}" & wait $!
# ------------------------------------------------
# run this Mlat_client instance in the background:
#shellcheck disable=SC2069,SC2086
if [[ "${LOGLEVEL}" == "verbose" ]]; then
"${s6wrap[@]}" --prepend="$(basename "$0")][${servername}" --args echo "starting: ${execstring}"
"${s6wrap[@]}" --prepend="$(basename "$0")][${servername}" --args ${execstring} &
elif [[ "${LOGLEVEL}" == "error" ]]; then
"${s6wrap[@]}" --ignore=stdout --prepend="$(basename "$0")][${servername}" --args ${execstring} &
elif [[ "${LOGLEVEL}" == "none" ]]; then
"${s6wrap[@]}" --ignore=stderr --ignore=stdout --prepend="$(basename "$0")][${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 $!
touch /run/.all_mlatclient_instances_have_started
# 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:
if [[ ! -f /run/readsb/gpsd.json ]] || [[ "$(jq -r .lat /run/readsb/gpsd.json)" == "null" ]]; then
# only sleep for a bit if the restarts aren't caused by GPS movement:
sleep "${RESTARTTIMER}" & wait $!
fi
servername="$(awk '{print $4}' <<< "${pid_array[$mlat_pid]}")"
servername="${servername%%:*}"
[[ "${LOGLEVEL,,}" != "none" ]] && "${s6wrap[@]}" --prepend="$(basename "$0")[${servername}" --args echo "MLAT_Client ${servername} exited. Attempting to restart." || true
# shellcheck disable=SC2086
execstring="$(echo ${MLAT_CMD} ${pid_array[$mlat_pid]} | xargs)"
# If GPSD is active, then replace the lat/lon/alt params with the ones from GPSD
if [[ -f /run/readsb/gpsd.json ]] && [[ "$(jq -r .lat /run/readsb/gpsd.json)" != "null" ]]; then
new_lat="$(jq -r .lat /run/readsb/gpsd.json)"
new_lon="$(jq -r .lon /run/readsb/gpsd.json)"
new_alt="$(jq -r .altMSL /run/readsb/gpsd.json)"
if [[ "$new_lat" != "null" ]] && [[ "$new_lon" != "null" ]] && [[ "$new_alt" != "null" ]]; then
execstring="$(sed "s/^\(.*\s\+--lat\s\+\)[0-9.-]\+\(.*\)$/\1${new_lat}\2/g" <<< "$execstring")"
execstring="$(sed "s/^\(.*\s\+--lon\s\+\)[0-9.-]\+\(.*\)$/\1${new_lon}\2/g" <<< "$execstring")"
execstring="$(sed "s/^\(.*\s\+--alt\s\+\)[mft0-9.-]\+\(.*\)$/\1${new_alt}m\2/g" <<< "$execstring")"
fi
fi
#shellcheck disable=SC2069,SC2086
if [[ "${LOGLEVEL}" == "verbose" ]]; then
"${s6wrap[@]}" --prepend="$(basename "$0")][${servername}" --args echo "Restarting: ${execstring}"
"${s6wrap[@]}" --prepend="$(basename "$0")][${servername}" --args ${execstring} &
elif [[ "${LOGLEVEL}" == "error" ]]; then
"${s6wrap[@]}" --ignore=stdout --prepend="$(basename "$0")][${servername}" --args ${execstring} &
elif [[ "${LOGLEVEL}" == "none" ]]; then
"${s6wrap[@]}" --ignore=stderr --ignore=stdout --prepend="$(basename "$0")][${servername}" --args ${execstring} &
fi
pid_array[$!]="${pid_array[${mlat_pid}]}"
unset "pid_array[${mlat_pid}]"
fi
done
# If GPSD is running, do more frequent checks than if it's not running
if [[ -f /run/readsb/gpsd.json ]] && [[ "$(jq -r .lat /run/readsb/gpsd.json)" != "null" ]]; then
sleep 1 & wait $!
else
sleep 10 & wait $!
fi
done