From 2bdcd24977d7b4b4e9cf3d150eb1e7609d8df5b5 Mon Sep 17 00:00:00 2001 From: Fred Clausen <43556888+fredclausen@users.noreply.github.com> Date: Sun, 23 Jan 2022 18:48:40 -0700 Subject: [PATCH] Initial container configuration --- .dockerignore | 34 +++++ .gitignore | 83 ++++++++++++ .pre-commit-config.yaml | 69 ++++++++++ Dockerfile | 57 +++++++++ README.md | 60 ++++++++- rootfs/etc/cont-init.d/01-dumpvdl2 | 91 +++++++++++++ rootfs/etc/services.d/dumpvdl2/.blank | 0 rootfs/etc/services.d/vdlm_feeder/run | 18 +++ rootfs/etc/services.d/vdlm_server/run | 12 ++ rootfs/etc/services.d/vdlm_stats/run | 36 ++++++ rootfs/etc/templates/run | 88 +++++++++++++ rootfs/scripts/healthcheck.sh | 176 ++++++++++++++++++++++++++ rootfs/usr/scripts/healthcheck.sh | 0 version | 2 + 14 files changed, 724 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 Dockerfile create mode 100755 rootfs/etc/cont-init.d/01-dumpvdl2 create mode 100644 rootfs/etc/services.d/dumpvdl2/.blank create mode 100644 rootfs/etc/services.d/vdlm_feeder/run create mode 100644 rootfs/etc/services.d/vdlm_server/run create mode 100644 rootfs/etc/services.d/vdlm_stats/run create mode 100644 rootfs/etc/templates/run create mode 100644 rootfs/scripts/healthcheck.sh create mode 100644 rootfs/usr/scripts/healthcheck.sh create mode 100644 version diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c5469c5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +.git* +LICENSE +README.md +retired_scripts +build*.sh +update-local-acars-decoder.sh +Logo-Sources +acarshub-typescript/dist +acarshub-typescript/node_modules +acarshub-typescript/test_assets +rootfs/webapp/static/js +rootfs/webapp/static/airframes-acars-decoder +rootfs/webapp/static/css +docs +retired_scripts +.eslintignore +.pre-commit-config.yaml +notes.txt +update-local-acars-decoder.sh +generate_local_dockerfile.sh +tools +.vscode +tools +rootfs/webapp/static/images/*hour.png +rootfs/webapp/static/images/*hours.png +rootfs/webapp/static/images/*days.png +rootfs/webapp/static/images/*week.png +rootfs/webapp/static/images/*months.png +rootfs/webapp/static/images/*year.png +rootfs/webapp/data/acars-metadata.json +rootfs/webapp/data/metadata.json +rootfs/webapp/data/metadata.json +rootfs/webapp/data/ground-stations.json +libseccomp2-checker.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..067855a --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,vscode +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,vscode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,vscode diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e89ff7e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,69 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-yaml + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + - id: end-of-file-fixer + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + - id: trailing-whitespace + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + - id: requirements-txt-fixer + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + - id: mixed-line-ending + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) +- repo: https://github.com/IamTheFij/docker-pre-commit + rev: v2.0.1 + hooks: + - id: hadolint-system + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + args: + - "--ignore" + - "DL3003" + - "--ignore" + - "DL3006" + - "--ignore" + - "DL3010" + - "--ignore" + - "DL3008" + - "--ignore" + - "DL4001" + - "--ignore" + - "DL3007" +- repo: https://github.com/psf/black + rev: 21.7b0 + hooks: + - id: black +- repo: https://github.com/pre-commit/mirrors-prettier + rev: 'v2.3.2' # Use the sha / tag you want to point at + hooks: + - id: prettier + files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx + types: [file] + additional_dependencies: + - prettier@2.3.2 + - '@typescript-eslint/eslint-plugin' + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) +- repo: https://github.com/pre-commit/mirrors-eslint + rev: v7.32.0 + hooks: + - id: eslint + files: \.[j]sx?$ # *.js, *.jsx, *.ts and *.tsx + types: [file] + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other) + additional_dependencies: + - eslint@7.32.0 + - eslint-config-google@0.7.1 + - babel-eslint@10.1.0 +# - '@typescript-eslint/eslint-plugin' +# - repo: https://github.com/pre-commit/mirrors-csslint +# rev: 'v1.0.5' # Use the sha / tag you want to point at +# hooks: +# - id: csslint +# exclude: ^rootfs/webapp/static/css/other/ +- repo: https://github.com/codespell-project/codespell.git + rev: 'v2.1.0' # Use the sha / tag you want to point at + hooks: + - id: codespell + types: [text] + exclude: ^(rootfs/webapp/static/css/other/|acars-decoder-typescript/|rootfs/webapp/static/js/other/|.+\.json|Logo-Sources|acarshub-typescript/css/other/|acarshub-typescript/js-other|rootfs/webapp/adsb.py|acarshub-typescript/src/interfaces.ts) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b5068f2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,57 @@ +FROM fredclausen/acarssdr-base:latest + +ENV DEVICE_INDEX="" \ + QUIET_LOGS="TRUE" \ + FREQUENCIES="" \ + FEED_ID="" \ + PPM="0"\ + GAIN="40" \ + SERIAL="" \ + SERVER="acarshub" \ + SERVER_PORT="5555" \ + VDLM_FILTER_ENABLE="TRUE" + +# hadolint ignore=DL3008,SC2086,SC2039 +RUN set -x && \ + TEMP_PACKAGES=() && \ + KEPT_PACKAGES=() && \ + # Required for building multiple packages. + TEMP_PACKAGES+=(build-essential) && \ + TEMP_PACKAGES+=(pkg-config) && \ + TEMP_PACKAGES+=(cmake) && \ + TEMP_PACKAGES+=(git) && \ + TEMP_PACKAGES+=(automake) && \ + TEMP_PACKAGES+=(autoconf) && \ + TEMP_PACKAGES+=(wget) && \ + # required for startup checks + KEPT_PACKAGES+=(bc) && \ + # packages for dumpvdl2 + TEMP_PACKAGES+=(libglib2.0-dev) && \ + KEPT_PACKAGES+=(libglib2.0-0) && \ + # install packages + apt-get update && \ + apt-get install -y --no-install-recommends \ + "${KEPT_PACKAGES[@]}" \ + "${TEMP_PACKAGES[@]}"\ + && \ + pushd /src/ && \ + git clone https://github.com/szpajder/dumpvdl2.git && \ + pushd dumpvdl2 && \ + mkdir build && \ + pushd build && \ + cmake ../ && \ + make && \ + make install && \ + popd && popd && \ + # Clean up + apt-get remove -y "${TEMP_PACKAGES[@]}" && \ + apt-get autoremove -y && \ + rm -rf /src/* /tmp/* /var/lib/apt/lists/* + + +COPY rootfs/ / + +# ENTRYPOINT [ "/init" ] + +# Add healthcheck +HEALTHCHECK --start-period=3600s --interval=600s CMD /scripts/healthcheck.sh diff --git a/README.md b/README.md index c997f7d..83c76a4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ -# docker-dumpvdl2 - +# Docker dumpvdl2 + +![Banner](https://github.com/fredclausen/docker-acarshub/blob/16ab3757986deb7c93c08f5c7e3752f54a19629c/Logo-Sources/ACARS%20Hub.png "banner") +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/fredclausen/docker-acarshub/Deploy%20to%20Docker%20Hub)](https://github.com/fredclausen/docker-acarshub/actions?query=workflow%3A%22Deploy+to+Docker+Hub%22) +[![Docker Pulls](https://img.shields.io/docker/pulls/fredclausen/acarshub.svg)](https://hub.docker.com/r/fredclausen/acarshub) +[![Docker Image Size (tag)](https://img.shields.io/docker/image-size/fredclausen/acarshub/latest)](https://hub.docker.com/r/fredclausen/acarshub) +[![Discord](https://img.shields.io/discord/734090820684349521)](https://discord.gg/sTf9uYF) + +Docker container for running [dumpvdl2](https://github.com/szpajder/dumpvdl2) and forwarding the received JSON messages to another system or docker container. Best used alongside [ACARS Hub](https://github.com/fredclausen/acarshub). + +Builds and runs on `amd64`, `arm64`, `arm/v7`, `arm/v6` and `386` architectures. + +## Note for Users running 32-bit Debian Buster-based OSes on ARM + +Please see: [Buster-Docker-Fixes](https://github.com/fredclausen/Buster-Docker-Fixes)! + +## Required hardware + +A computer host on a suitable architecture and one USB RTL-SDR dongle connected to an antenna. + +## Up and running + +```yaml +version: '2.0' + +services: + dumpvdl2: + image: fredclausen/dumpvdl2:latest + tty: true + container_name: dumpvdl2 + restart: always + devices: + - /dev/bus/usb:/dev/bus/usb + ports: + environment: + - TZ="America/Denver" + - SERIAL=13305 + - FEED_ID=VDLM + - FREQUENCIES=136725000;136975000;136875000 + tmpfs: + - /run:exec,size=64M + - /var/log +``` + +## Configuration options + +| Variable | Description | Required | Default | +|----------|-------------|---------|--------| +| `TZ` | Your timezone | No | UTC | +| `SERIAL` | The serial number of your RTL-SDR dongle | Yes | Blank | +| `FEED_ID` | Used by the decoder to insert a unique ID in to the output message | Yes | Blank | +| `FREQUENCIES` | Colon-separated list of frequencies, but to a maximum of 8, for the decoder to list to. No decimal, and all frequencies should be nine digits long. | Yes | Blank | +| `PPM` | Parts per million correction of the decoder | No | 0 | +| `GAIN`| The gain applied to the RTL-SDR dongle. Recommended to leave at the default autogain. If you want to set the gain manually it is set in tenth of db (ie -g 90 for +9db) | No | `A` for autogain | +| `SERVER` | The server where messages will be forwarded to. | No | Blank | +| `SERVER_PORT` | The port where the server will receive messages on. | No | `5555` | +| `VDLM_FILTER_ENABLE` | Filter out non-informational messages. Turning this off (set to a blank value) will cause increased message rate but the messages will be of little value. Will cause extra SD card read/writes. | No | `TRUE` | +| `QUIET_LOGS` | Mute log output to the bare minimum. Set to a blank value to disable.| No | `TRUE` | diff --git a/rootfs/etc/cont-init.d/01-dumpvdl2 b/rootfs/etc/cont-init.d/01-dumpvdl2 new file mode 100755 index 0000000..865f6ec --- /dev/null +++ b/rootfs/etc/cont-init.d/01-dumpvdl2 @@ -0,0 +1,91 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +# FEED_ID needs to be set + +if [[ -z "${FEED_ID}" ]]; then + echo "FEED_ID is not set, exiting" + exit 1 +fi + +# FREQUENCIES needs to be set + +if [[ -z "${FREQUENCIES}" ]]; then + echo "FREQUENCIES is not set, exiting" + exit 1 +fi + +IFS=';' +read -ra SPLIT_FREQS <<< "${FREQUENCIES}" + +# loop through SPLIT_FREQS + +# We can only have 6 total frequencies + +if [[ "${#SPLIT_FREQS[@]}" -gt 8 ]]; then + echo "FREQUENCIES is too long, exiting" + exit 1 +fi + +# FREQUENCIES needs to be in the range of 118.0 - 137.0. + +FREQ_STRING="" +for i in "${SPLIT_FREQS[@]}" +do +: +# if [[ $(echo "$i > 118.0" | bc) -eq 0 || $(echo "$i < 137.0" | bc) -eq 0 ]]; then +# echo "FREQUENCY $i is not in the range of 118.0 - 137.0, exiting" +# exit 1 +# fi +FREQ_STRING+="$i " +done + +# Make sure mode is valid + +if [[ -n "${SERVER}" && -z "${SERVER_PORT}" ]]; then + echo "SERVER is set but SERVER_PORT is not set, exiting" + exit 1 +fi + +# DEVICE_ID or SERIAL needs to be set + +if [[ -z "${SERIAL}" ]]; then + echo "SERIAL is not set, exiting" + exit 1 +fi + +RTL_TEST_OUTPUT=$(timeout 1s rtl_test -d 0 2>&1 | grep -P '^\s+\d+:\s+\S+?,\s+\S+?,\s+SN:\s+\S+?\s*$' || true) + +IFS=$'\n' +for RTL_TEST_OUTPUT_LINE in $RTL_TEST_OUTPUT; do + # Unset variables in case any regexes fail + unset RTL_DEVICE_ID RTL_DEVICE_MAKE RTL_DEVICE_MODEL RTL_DEVICE_SERIAL + + # Pull variables from output via regex + RTL_DEVICE_NUMBER=$(echo "$RTL_TEST_OUTPUT_LINE" | grep -oP '^\s+\K\d+(?=:\s+\S+?,\s+\S+?,\s+SN:\s+\S+?\s*$)') + RTL_DEVICE_SERIAL=$(echo "$RTL_TEST_OUTPUT_LINE" | grep -oP '^\s+\d+:\s+\S+?,\s+\S+?,\s+SN:\s+\K\S+?(?=\s*$)') + + # See if we've found the device we're looking for + if [[ -n "$SERIAL" && "$SERIAL" == "$RTL_DEVICE_SERIAL" ]]; then + OUTPUT_DEVICE_ID="$RTL_DEVICE_NUMBER" + fi +done + +if [[ -z "${OUTPUT_DEVICE_ID}" ]]; then + echo "Could not find device ID for serial '$SERIAL'" + # exit 1 +fi + +rm -rf /etc/services.d/dumpvdl2/run > /dev/null 2>&1 +cp /etc/templates/run /etc/services.d/dumpvdl2/run +s6-chmod 0755 /etc/services.d/dumpvdl2/run + +mkdir -p /run/acars +touch /run/acars/vdlm.past5min.json + +sed -i "s/DEVICE_ID=\"\"/DEVICE_ID=\"$OUTPUT_DEVICE_ID\"/g" /etc/services.d/dumpvdl2/run +sed -i "s/FREQ_STRING=\"\"/FREQ_STRING=\"$FREQ_STRING\"/g" /etc/services.d/dumpvdl2/run + +# Everything is good to go. Exit with 0 + +exit 0 diff --git a/rootfs/etc/services.d/dumpvdl2/.blank b/rootfs/etc/services.d/dumpvdl2/.blank new file mode 100644 index 0000000..e69de29 diff --git a/rootfs/etc/services.d/vdlm_feeder/run b/rootfs/etc/services.d/vdlm_feeder/run new file mode 100644 index 0000000..03d32b5 --- /dev/null +++ b/rootfs/etc/services.d/vdlm_feeder/run @@ -0,0 +1,18 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +set -o pipefail + +# Require that vdlm_server is running +if ! netstat -an | grep -P '^\s*tcp\s+\d+\s+\d+\s+0\.0\.0\.0:15555\s+(?>\d{1,3}\.{0,1}){4}:\*\s+LISTEN\s*$' > /dev/null; then + sleep 1 + exit +fi +set -e + +SERVER_ADDR="UDP:${SERVER}:${SERVER_PORT}" +# shellcheck disable=SC2016 +socat -d TCP:127.0.0.1:15555 "$SERVER_ADDR" \ + 2>&1 | stdbuf -oL awk '{print "[acars_feeder] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' + +sleep 5 diff --git a/rootfs/etc/services.d/vdlm_server/run b/rootfs/etc/services.d/vdlm_server/run new file mode 100644 index 0000000..7eb2eeb --- /dev/null +++ b/rootfs/etc/services.d/vdlm_server/run @@ -0,0 +1,12 @@ +#!/usr/bin/with-contenv bash +#shellcheck shell=bash + + set -o pipefail + set -e + + # Listens for the output of dumpvdl2 (UDP), and makes it available for multiple processes at TCP port 15555 + # shellcheck disable=SC2016 + socat -u udp-listen:5555,fork stdout | ncat -4 --keep-open --listen 0.0.0.0 15555 \ + 2>&1 | stdbuf -oL awk '{print "[vdlm_server] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' + + sleep 5 diff --git a/rootfs/etc/services.d/vdlm_stats/run b/rootfs/etc/services.d/vdlm_stats/run new file mode 100644 index 0000000..b287682 --- /dev/null +++ b/rootfs/etc/services.d/vdlm_stats/run @@ -0,0 +1,36 @@ +#!/usr/bin/with-contenv bash +#shellcheck shell=bash + + +set -o pipefail + +# Require that acars_server is running +if ! netstat -an | grep -P '^\s*tcp\s+\d+\s+\d+\s+0\.0\.0\.0:15555\s+(?>\d{1,3}\.{0,1}){4}:\*\s+LISTEN\s*$' > /dev/null; then + sleep 1 + exit +fi + +# Start our stats loop +while true; do + + # capture 5 mins of flows + timeout --foreground 300s socat -u TCP:127.0.0.1:15555 CREATE:/run/acars/vdlm.past5min.json + + # if the port isn't reachable, this file isn't created, either container is shutting down or vdlm_server isn't reachable + # in both cases let's exit, if this should still be running it will be restarted + if ! [[ -f /run/acars/vdlm.past5min.json ]]; then + exit + fi + + # shellcheck disable=SC2016 + echo "$(wc -l < /run/acars/vdlm.past5min.json) VDLM messages received in last 5 mins" | stdbuf -oL awk '{print "[vdlm_stats] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' + + # rotate files keeping last 2 hours + for i in {24..1}; do + mv "/run/acars/vdlm.$((i-1)).json" "/run/acars/vdlm.$i.json" > /dev/null 2>&1 || true + done + mv "/run/acars/vdlm.past5min.json" "/run/acars/vdlm.0.json" > /dev/null 2>&1 || true + +done + +sleep 5 diff --git a/rootfs/etc/templates/run b/rootfs/etc/templates/run new file mode 100644 index 0000000..886600f --- /dev/null +++ b/rootfs/etc/templates/run @@ -0,0 +1,88 @@ +#!/usr/bin/with-contenv bash +#shellcheck shell=bash + +# STARTUP SEQUENCE +# - dumpvdl2 does not depend on any services and should start immediately + +VDLM_BIN="/usr/local/bin/dumpvdl2" +DEVICE_ID="" +# shellcheck disable=SC2001 +FREQ_STRING="" +VDLM_CMD=() + +# Specify device ID +if [ -n "${DEVICE_ID}" ]; then + VDLM_CMD+=("--rtlsdr" "$DEVICE_ID") +fi + +if [ -z "$GAIN" ]; then + GAIN="40" +fi + +if [ -n "${PPM}" ]; then + VDLM_CMD+=("--correction" "$PPM") +fi + +VDLM_CMD+=("--gain" "$GAIN") + +# Send output JSON to vdlm2_server. +if [[ -n "${VDLM_FILTER_ENABLE}" ]]; then + VDLM_CMD+=("--msg-filter" "all,-avlc_s,-acars_nodata,-x25_control,-idrp_keepalive,-esis") +fi + +VDLM_CMD+=("--station-id=$FEED_ID" "--output" "decoded:json:udp:address=127.0.0.1,port=5555") + +# if [ -z "${QUIET_MESSAGES}" ]; then +# VDLM_CMD+=("--output" "decoded:text:file:path=-") +# fi + +# if [ -n "${PLANEPLOTTER}" ]; then +# VDLM_CMD+=("--output" "decoded:pp_acars:udp:address=127.0.0.1,port=4444") + +# if ! netstat -an | grep -P '^\s*tcp\s+\d+\s+\d+\s+0\.0\.0\.0:14444\s+(?>\d{1,3}\.{0,1}){4}:\*\s+LISTEN\s*$' > /dev/null; then +# if [ -z "${QUIET_LOGS}" ]; then +# # shellcheck disable=SC2016 +# echo "Waiting for planeplotter_server" | stdbuf -oL awk '{print "[dumpvdl2] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' +# fi +# sleep 1 +# exit +# fi +# if [ -z "${QUIET_LOGS}" ]; then +# # shellcheck disable=SC2016 +# echo "planeplotter_server ready, starting service" | stdbuf -oL awk '{print "[dumpvdl2] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' +# fi +# fi + +# shellcheck disable=SC2206 +VDLM_CMD+=($FREQ_STRING) + +set -eo pipefail + +if [[ -n "${QUIET_LOGS}" ]]; then +# shellcheck disable=SC2016 + "$VDLM_BIN" "${VDLM_CMD[@]}" 2>&1 | \ + stdbuf -oL sed --unbuffered '/^$/d' | \ + stdbuf -oL awk '! /^dumpvdl2/' | \ + stdbuf -oL awk '! /^Sampling rate set/' | \ + stdbuf -oL awk '! /^Found \d+ device(s):/' | \ + stdbuf -oL awk '! /^ [0-9]+/' | \ + stdbuf -oL awk '! /^Using device [0-9]+/' | \ + stdbuf -oL awk '! /^Found /' | \ + stdbuf -oL awk '! /^Exact sample rate /' | \ + stdbuf -oL awk '! /^Setting sample rate/' | \ + stdbuf -oL awk '! /PLL not locked!$/' | \ + stdbuf -oL awk '! /^Center frequency set/' | \ + stdbuf -oL awk '! /^Device [#]?[0-9]+/' | \ + stdbuf -oL awk '{print "[dumpvdl2] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' +else + echo "[dumpvdl2-$SERIAL] Starting: '$VDLM_BIN" "${VDLM_CMD[*]}'" + # shellcheck disable=SC2016 + "$VDLM_BIN" "${VDLM_CMD[@]}" 2>&1 | \ + stdbuf -oL sed --unbuffered '/^$/d' | \ + stdbuf -oL awk '{print "[dumpvdl2] " strftime("%Y/%m/%d %H:%M:%S", systime()) " " $0}' +fi + +# if we've ended up here there is a problem! +echo "[dumpvdl2-$SERIAL] Exiting with error" +sleep 5 +exit 1 diff --git a/rootfs/scripts/healthcheck.sh b/rootfs/scripts/healthcheck.sh new file mode 100644 index 0000000..4f5ad5a --- /dev/null +++ b/rootfs/scripts/healthcheck.sh @@ -0,0 +1,176 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +# Import healthchecks-framework +# shellcheck disable=SC1091 +source /opt/healthchecks-framework/healthchecks.sh + +# Default original codes +EXITCODE=0 + +# ===== Local Helper Functions ===== + +function get_pid_of_decoder { + + # $1: service_dir + service_dir="$1" + + # Ensure variables are unset + unset DEVICE_ID FREQS_VDLM VDLM_BIN FREQS_ACARS ACARS_BIN + + # Get DEVICE_ID + eval "$(grep "DEVICE_ID=\"" "$service_dir"/run)" + + # Get FREQS_ACARS + eval "$(grep "FREQ_STRING=\"" "$service_dir"/run)" + + # Get ACARS_BIN + eval "$(grep "VDLM_BIN=\"" "$service_dir"/run)" + + # Get PS output for the relevant process + if [[ -n "$ACARS_BIN" ]]; then + # shellcheck disable=SC2009 + ps_output=$(ps aux | grep "$ACARS_BIN" | grep " -r $DEVICE_ID " | grep " $FREQS_ACARS") + elif [[ -n "$VDLM_BIN" ]]; then + # shellcheck disable=SC2009 + ps_output=$(ps aux | grep "$VDLM_BIN" | grep " --rtlsdr $DEVICE_ID " | grep " $FREQS_VDLM") + fi + + # Find the PID of the decoder based on command line + process_pid=$(echo "$ps_output" | tr -s " " | cut -d " " -f 2) + + # Return the process_pid + echo "$process_pid" + +} + +# ===== Check dummpvdl2 processes ===== + +# For each service... +for service_dir in /etc/services.d/*; do + service_name=$(basename "$service_dir") + + # If the service is dumpvdlm2-*... + if [[ "$service_name" == dumpvdl2 ]]; then + + decoder_pid=$(get_pid_of_decoder "$service_dir") + decoder_udp_port="5555" + decoder_server_prefix="dumpvdl2" + + # If the server isn't dumpvdl2-. + else + # skip it! + continue + fi + + # If the process doesn't exists, then fail + + echo "==== Checking $service_name =====" + + if [[ -z "$decoder_pid" ]]; then + echo "Cannot find PID of decoder $service_name: UNHEALTHY" + EXITCODE=1 + else + # If the process does exist, then make sure it has made a connection to localhost on the relevant port. + if ! check_udp4_connection_established_for_pid "127.0.0.1" "ANY" "127.0.0.1" "$decoder_udp_port" "$decoder_pid"; then + echo "Decoder $service_name (pid $decoder_pid) not connected to ${decoder_server_prefix}_server at 127.0.0.1:$decoder_udp_port: UNHEALTHY" + EXITCODE=1 + else + echo "Decoder $service_name (pid $decoder_pid) is connected to ${decoder_server_prefix}_server at 127.0.0.1:$decoder_udp_port: HEALTHY" + fi + fi + +done + + echo "==== Checking vdlm2_server =====" + + # Check vdlm2_server is listening for TCP on 127.0.0.1:15555 + vdlm2_pidof_vdlm2_tcp_server=$(pgrep -f 'ncat -4 --keep-open --listen 0.0.0.0 15555') + if ! check_tcp4_socket_listening_for_pid "0.0.0.0" "15555" "${vdlm2_pidof_vdlm2_tcp_server}"; then + echo "vdlm2_server TCP not listening on port 15555 (pid $vdlm2_pidof_vdlm2_tcp_server): UNHEALTHY" + EXITCODE=1 + else + echo "vdlm2_server TCP listening on port 15555 (pid $vdlm2_pidof_vdlm2_tcp_server): HEALTHY" + fi + + if [ -n "${ENABLE_WEB}" ]; then + if ! netstat -anp | grep -P "tcp\s+\d+\s+\d+\s+127.0.0.1:[0-9]+\s+127.0.0.1:15555\s+ESTABLISHED\s+[0-9]+/python3" > /dev/null 2>&1; then + echo "TCP4 connection between 127.0.0.1:ANY and 127.0.0.1:15555 for python3 established: FAIL" + echo "vdlm2_server TCP connected to python server on port 15555 (pid $vdlm2_pidof_vdlm2_tcp_server): UNHEALTHY" + EXITCODE=1 + else + echo "TCP4 connection between 127.0.0.1:ANY and 127.0.0.1:15555 for python3 established: PASS" + echo "vdlm2_server TCP connected to python server on port 15555: HEALTHY" + fi + fi + +#### REMOVE AFTER AIRFRAMES IS UPDATED #### + # Check vdlm2_feeder + if [ -n "${FEED}" ]; then + echo "vdlm2_feeder (pid 0) is feeding: HEALTHY" + # echo "==== Checking vdlm2_feeder =====" + + # vdlm2_pidof_vdlm2_feeder=$(pgrep -f 'socat -d TCP:127.0.0.1:15555 UDP:feed.acars.io:5555') + + # # Ensure TCP connection to vdlm2_server at 127.0.0.1:15555 + # if ! check_tcp4_connection_established_for_pid "127.0.0.1" "ANY" "127.0.0.1" "15555" "${vdlm2_pidof_vdlm2_feeder}"; then + # echo "vdlm2_feeder (pid $vdlm2_pidof_vdlm2_feeder) not connected to vdlm2_server (pid $vdlm2_pidof_vdlm2_tcp_server) at 127.0.0.1:15555: UNHEALTHY" + # EXITCODE=1 + # else + # echo "vdlm2_feeder (pid $vdlm2_pidof_vdlm2_feeder) is connected to vdlm2_server (pid $vdlm2_pidof_vdlm2_tcp_server) at 127.0.0.1:15555: HEALTHY" + # fi + + # # Ensure UDP connection to acars.io + # if ! check_udp4_connection_established_for_pid "ANY" "ANY" "ANY" "5555" "${vdlm2_pidof_vdlm2_feeder}"; then + # echo "vdlm2_feeder (pid $vdlm2_pidof_vdlm2_feeder) not feeding: UNHEALTHY" + # EXITCODE=1 + # else + # echo "vdlm2_feeder (pid $vdlm2_pidof_vdlm2_feeder) is feeding: HEALTHY" + # fi + + fi + + #### REMOVE AFTER AIRFRAMES IS UPDATED #### + + echo "==== Checking vdlm2_stats =====" + + # Check vdlm2_stats: + vdlm2_pidof_vdlm2_stats=$(pgrep -fx 'socat -u TCP:127.0.0.1:15555 CREATE:/run/acars/vdlm.past5min.json') + + # Ensure TCP connection to vdlm2_server at 127.0.0.1:15555 + if ! check_tcp4_connection_established_for_pid "127.0.0.1" "ANY" "127.0.0.1" "15555" "${vdlm2_pidof_vdlm2_stats}"; then + echo "vdlm2_stats (pid $vdlm2_pidof_vdlm2_stats) not connected to acars_server (pid $vdlm2_pidof_vdlm2_tcp_server) at 127.0.0.1:15555: UNHEALTHY" + EXITCODE=1 + else + echo "vdlm2_stats (pid $vdlm2_pidof_vdlm2_stats) connected to acars_server (pid $vdlm2_pidof_vdlm2_tcp_server) at 127.0.0.1:15555: HEALTHY" + fi + + echo "==== Check for VDLM2 activity =====" + + # Check for activity + # read .json files, ensure messages received in past hour + + vdlm2_num_msgs_past_hour=$(find /run/acars -type f -name 'vdlm.*.json' -cmin -60 -exec cat {} \; | sed -e 's/}{/}\n{/g' | wc -l) + if [[ "$vdlm2_num_msgs_past_hour" -gt 0 ]]; then + echo "$vdlm2_num_msgs_past_hour VDLM2 messages received in past hour: HEALTHY" + else + echo "$vdlm2_num_msgs_past_hour VDLM2 messages received in past hour: UNHEALTHY" + EXITCODE=1 + fi + +echo "==== Check Service Death Tallies =====" + +# Check service death tally +mapfile -t SERVICES < <(find /run/s6/services -maxdepth 1 -type d -not -name "*s6-*" | tail +2) +for service in "${SERVICES[@]}"; do + SVDT=$(s6-svdt "$service" | grep -cv 'exitcode 0') + if [[ "$SVDT" -gt 0 ]]; then + echo "abnormal death tally for $(basename "$service") since last check is: $SVDT: UNHEALTHY" + EXITCODE=1 + else + echo "abnormal death tally for $(basename "$service") since last check is: $SVDT: HEALTHY" + fi + s6-svdt-clear "$service" +done + +exit "$EXITCODE" diff --git a/rootfs/usr/scripts/healthcheck.sh b/rootfs/usr/scripts/healthcheck.sh new file mode 100644 index 0000000..e69de29 diff --git a/version b/version new file mode 100644 index 0000000..6af22f9 --- /dev/null +++ b/version @@ -0,0 +1,2 @@ +v1.0.0 Build 1 +v1.0.0Build1