From 216ae1ebf9a08b381824532b4a319658677d8ec4 Mon Sep 17 00:00:00 2001 From: Weston Blieden Date: Wed, 8 Jul 2026 09:40:14 +0200 Subject: [PATCH] Add super-linter (markdown/yaml/json/dockerfile/shell); fix lint findings --- .github/super-linter.env | 10 ++ .github/workflows/super-linter.yml | 22 +++++ .hadolint.yaml | 8 ++ .markdownlint.yaml | 7 +- .yamllint.yaml | 2 + CONTRIBUTING.md | 28 +++--- README.md | 9 +- aarch64/README.md | 4 +- aarch64_ROOT/README.md | 4 +- arm/README.md | 4 +- arm_ROOT/README.md | 4 +- arm_acap3/app/Tailscale_VPN | 46 ++++----- common/app/Tailscale_VPN_run | 144 ++++++++++++++--------------- 13 files changed, 170 insertions(+), 122 deletions(-) create mode 100644 .github/super-linter.env create mode 100644 .github/workflows/super-linter.yml create mode 100644 .hadolint.yaml create mode 100644 .yamllint.yaml diff --git a/.github/super-linter.env b/.github/super-linter.env new file mode 100644 index 0000000..27aec66 --- /dev/null +++ b/.github/super-linter.env @@ -0,0 +1,10 @@ +DEFAULT_BRANCH=origin/main +LINTER_RULES_PATH=/ +VALIDATE_ALL_CODEBASE=true +IGNORE_GITIGNORED_FILES=true +YAML_CONFIG_FILE=.yamllint.yaml +VALIDATE_DOCKERFILE_HADOLINT=true +VALIDATE_JSON=true +VALIDATE_MARKDOWN=true +VALIDATE_SHELL_SHFMT=true +VALIDATE_YAML=true diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml new file mode 100644 index 0000000..1fc86fb --- /dev/null +++ b/.github/workflows/super-linter.yml @@ -0,0 +1,22 @@ +--- +name: Lint + +on: push + +jobs: + Build: + name: Lint code base + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup Environment + run: cat .github/super-linter.env >> "$GITHUB_ENV" + + - name: Lint code base + uses: super-linter/super-linter/slim@v8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..db51dab --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,8 @@ +--- +# Enforce error-level Dockerfile correctness. Warnings/info are advisory: the +# ACAP cross-compile Dockerfiles use accepted patterns (cd in RUN, ARG-templated +# FROM tags hadolint cannot resolve, optional pipefail). +failure-threshold: error +ignored: + # Pin versions in 'apt-get install' - the SDK base image is already pinned. + - DL3008 diff --git a/.markdownlint.yaml b/.markdownlint.yaml index b2a77cd..62ae187 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -1,7 +1,8 @@ --- -# Line length -MD013: - line_length: 132 +# Line length (disabled: long lines in tables, URLs and prose are acceptable) +MD013: false +# Allow inline HTML (e.g. logos and badges in READMEs) +MD033: false # Allow blank lines inside blockquotes MD028: false # First line in a file should be a top-level heading diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..288670b --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,2 @@ +rules: + line-length: disable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 242decc..6f1a864 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,13 +13,13 @@ All types of contributions are encouraged and valued. See the [Table of contents - [I have a question](#i-have-a-question) - [I want to contribute](#i-want-to-contribute) - - [Reporting bugs](#reporting-bugs) - - [Before submitting a bug report](#before-submitting-a-bug-report) - - [How do I submit a good bug report?](#how-do-i-submit-a-good-bug-report) - - [Suggesting enhancements](#suggesting-enhancements) - - [Before Submitting an Enhancement](#before-submitting-an-enhancement) - - [How do I submit a good enhancement suggestion?](#how-do-i-submit-a-good-enhancement-suggestion) - - [Your first code contribution](#your-first-code-contribution) + - [Reporting bugs](#reporting-bugs) + - [Before submitting a bug report](#before-submitting-a-bug-report) + - [How do I submit a good bug report?](#how-do-i-submit-a-good-bug-report) + - [Suggesting enhancements](#suggesting-enhancements) + - [Before Submitting an Enhancement](#before-submitting-an-enhancement) + - [How do I submit a good enhancement suggestion?](#how-do-i-submit-a-good-enhancement-suggestion) + - [Your first code contribution](#your-first-code-contribution) ## I have a question @@ -46,13 +46,13 @@ A good bug report shouldn't leave others needing to chase you up for more inform - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker][issues_bugs]. - Also make sure to search the internet to see if users outside of the GitHub community have discussed the issue. - Collect information about the bug: - - Axis device model - - Axis device firmware version - - Stack trace - - OS and version (Windows, Linux, macOS, x86, ARM) - - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant - - Possibly your input and the output - - Can you reliably reproduce the issue? And can you also reproduce it with older versions? + - Axis device model + - Axis device firmware version + - Stack trace + - OS and version (Windows, Linux, macOS, x86, ARM) + - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant + - Possibly your input and the output + - Can you reliably reproduce the issue? And can you also reproduce it with older versions? #### How do I submit a good bug report? diff --git a/README.md b/README.md index 817b791..7ca47e4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This repository provides an **ACAP package** that installs the [Tailscale VPN cl [![Sponsor](https://img.shields.io/badge/sponsor-%E2%9D%A4-lightgrey?logo=github)](https://github.com/sponsors/Mo3he) [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-orange?style=flat&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/mo3he) -> **Disclaimer:** This is an independent, community-developed ACAP package and is not an official Axis Communications product. It is not affiliated with, endorsed by, or supported by Axis Communications AB. Use it at your own risk. For official Axis software, visit axis.com +> **Disclaimer:** This is an independent, community-developed ACAP package and is not an official Axis Communications product. It is not affiliated with, endorsed by, or supported by Axis Communications AB. Use it at your own risk. For official Axis software, visit axis.com > **Tailscale Notice:** Tailscale is a product of Tailscale Inc. This package independently redistributes the Tailscale binaries under the [BSD 3-Clause License](LICENSE) and is not affiliated with, endorsed by, or supported by Tailscale Inc. For the official Tailscale client, visit [tailscale.com](https://tailscale.com). --- @@ -47,7 +47,8 @@ Get the **prebuilt `.eap` file** from the [Releases page](https://github.com/Mo3 3. Upload the `.eap` file. Once installed: -- Start the app. + +- Start the app. - Click **Open** to view logs and get your Tailscale authentication URL. - On uninstall, all changes/files are removed. @@ -80,6 +81,7 @@ All parameters are configurable via the web UI (**Open → Settings** card) and --- +### Proxy Support All non-ROOT variants expose two local proxy endpoints that route outbound traffic through the Tailscale tunnel. The ports are configurable via **Settings → HTTP Proxy Port / SOCKS5 Proxy Port** in the web UI. @@ -118,6 +120,7 @@ There is an important asymmetry to understand. Making the camera **reachable fro In short: on non-root builds the proxies cover apps that know how to use a proxy, but a system feature like "add network share" opens a raw socket that never touches the tunnel. The ROOT build is the clean way to let the camera *consume* tailnet services. ### Plan B: reverse-SSH tunnel +> > **Requires root on the camera.** Port 445 is privileged, so binding it needs a root-capable build (e.g. developer certificates installed). If you cannot use the ROOT build but still need the camera to mount a share on a machine that is on your tailnet, you can make the remote share appear **local** to the camera with a reverse SSH tunnel. Because the destination becomes `127.0.0.1`, the proxy-unaware SMB client never has to route over the tailnet. @@ -141,6 +144,7 @@ Then, in the camera's **System → Storage → Add network share** dialog, use ` ### Manual update (advanced) Replace the binaries in `common/app/lib/` (shared by `aarch64`, `armv7hf`, and their ROOT variants) or `arm_acap3/app/lib/` (legacy variant, kept separate): + - `tailscale` - `tailscaled` @@ -178,6 +182,7 @@ An **ACAP 3** variant (`armv7hf_acap3`) is available for older cameras that do n ## Purpose Adding a VPN client directly to the camera enables: + - Secure remote access without additional hardware or complex network configuration. - Easy setup through Tailscale’s lightweight WireGuard-based tunnel. diff --git a/aarch64/README.md b/aarch64/README.md index 6e0fc02..03faa92 100644 --- a/aarch64/README.md +++ b/aarch64/README.md @@ -1,5 +1,5 @@ To build, from main directory -docker build --tag aarch64 . +docker build --tag aarch64 . -docker cp $(docker create aarch64):/opt/app ./build \ No newline at end of file +docker cp $(docker create aarch64):/opt/app ./build diff --git a/aarch64_ROOT/README.md b/aarch64_ROOT/README.md index 6e0fc02..03faa92 100644 --- a/aarch64_ROOT/README.md +++ b/aarch64_ROOT/README.md @@ -1,5 +1,5 @@ To build, from main directory -docker build --tag aarch64 . +docker build --tag aarch64 . -docker cp $(docker create aarch64):/opt/app ./build \ No newline at end of file +docker cp $(docker create aarch64):/opt/app ./build diff --git a/arm/README.md b/arm/README.md index adf52d5..34e6641 100644 --- a/arm/README.md +++ b/arm/README.md @@ -1,5 +1,5 @@ To build from main directory -docker build --tag arm . +docker build --tag arm . -docker cp $(docker create arm):/opt/app ./build \ No newline at end of file +docker cp $(docker create arm):/opt/app ./build diff --git a/arm_ROOT/README.md b/arm_ROOT/README.md index adf52d5..34e6641 100644 --- a/arm_ROOT/README.md +++ b/arm_ROOT/README.md @@ -1,5 +1,5 @@ To build from main directory -docker build --tag arm . +docker build --tag arm . -docker cp $(docker create arm):/opt/app ./build \ No newline at end of file +docker cp $(docker create arm):/opt/app ./build diff --git a/arm_acap3/app/Tailscale_VPN b/arm_acap3/app/Tailscale_VPN index 062391a..db1dd1c 100755 --- a/arm_acap3/app/Tailscale_VPN +++ b/arm_acap3/app/Tailscale_VPN @@ -16,19 +16,19 @@ logger -t "Tailscale_VPN" "Starting tailscaled daemon" # Log to file (not piped through logger) -- avoids extra logger process holding # tailscaled stdout open, which prevents our wait loop from detecting exit "$APP_DIR/lib/tailscaled" \ - --state="$STATE_DIR/tailscaled.state" \ - --socket="$STATE_DIR/tailscaled.sock" \ - --socks5-server=localhost:1055 \ - --outbound-http-proxy-listen=localhost:8080 \ - --tun=userspace-networking \ - >> "$STATE_DIR/tailscaled.log" 2>&1 & + --state="$STATE_DIR/tailscaled.state" \ + --socket="$STATE_DIR/tailscaled.sock" \ + --socks5-server=localhost:1055 \ + --outbound-http-proxy-listen=localhost:8080 \ + --tun=userspace-networking \ + >>"$STATE_DIR/tailscaled.log" 2>&1 & TAILSCALED_PID=$! # Wait for socket to appear (up to 15 seconds) i=0 while [ $i -lt 15 ] && [ ! -S "$STATE_DIR/tailscaled.sock" ]; do - sleep 1 - i=$((i + 1)) + sleep 1 + i=$((i + 1)) done logger -t "Tailscale_VPN" "Connecting to Tailscale network" @@ -37,9 +37,9 @@ logger -t "Tailscale_VPN" "Connecting to Tailscale network" # cameras with limited RAM (e.g. 222 MB). # Capture output so we can extract auth URL and log it to syslog for the web UI. UP_OUT=$("$APP_DIR/lib/tailscale" \ - --socket="$STATE_DIR/tailscaled.sock" \ - up --hostname="$(hostname)" --timeout=10s 2>&1) || true -echo "$UP_OUT" >> "$STATE_DIR/tailscaled.log" + --socket="$STATE_DIR/tailscaled.sock" \ + up --hostname="$(hostname)" --timeout=10s 2>&1) || true +echo "$UP_OUT" >>"$STATE_DIR/tailscaled.log" # If an auth URL was returned, log it so the web UI can show it AUTH_URL=$(echo "$UP_OUT" | grep -o 'https://login\.tailscale\.com/[^ ]*' | head -1) @@ -48,7 +48,7 @@ AUTH_URL=$(echo "$UP_OUT" | grep -o 'https://login\.tailscale\.com/[^ ]*' | head # Log IP and version into syslog so the web UI details panel can populate TS_IP=$("$APP_DIR/lib/tailscale" --socket="$STATE_DIR/tailscaled.sock" ip -4 2>/dev/null | head -1) TS_VER=$("$APP_DIR/lib/tailscale" --socket="$STATE_DIR/tailscaled.sock" version 2>/dev/null | head -1) -[ -n "$TS_IP" ] && logger -t "Tailscale_VPN" "Tailscale IP: $TS_IP" +[ -n "$TS_IP" ] && logger -t "Tailscale_VPN" "Tailscale IP: $TS_IP" [ -n "$TS_VER" ] && logger -t "Tailscale_VPN" "Tailscale version: $TS_VER" logger -t "Tailscale_VPN" "Tailscale VPN is running" @@ -63,19 +63,19 @@ logger -t "Tailscale_VPN" "SOCKS5 proxy: 127.0.0.1:1055" STATUS_FILE="$STATE_DIR/status.json" publish_status() { - if "$APP_DIR/lib/tailscale" --socket="$STATE_DIR/tailscaled.sock" status --json > "$STATUS_FILE.tmp" 2>/dev/null; then - mv "$STATUS_FILE.tmp" "$STATUS_FILE" 2>/dev/null - chmod 644 "$STATUS_FILE" 2>/dev/null - else - rm -f "$STATUS_FILE.tmp" 2>/dev/null - fi + if "$APP_DIR/lib/tailscale" --socket="$STATE_DIR/tailscaled.sock" status --json >"$STATUS_FILE.tmp" 2>/dev/null; then + mv "$STATUS_FILE.tmp" "$STATUS_FILE" 2>/dev/null + chmod 644 "$STATUS_FILE" 2>/dev/null + else + rm -f "$STATUS_FILE.tmp" 2>/dev/null + fi } # Remove stale status on stop so the UI does not show a connected node after exit. cleanup() { - rm -f "$STATUS_FILE" 2>/dev/null - [ -n "$TAILSCALED_PID" ] && kill "$TAILSCALED_PID" 2>/dev/null - exit 0 + rm -f "$STATUS_FILE" 2>/dev/null + [ -n "$TAILSCALED_PID" ] && kill "$TAILSCALED_PID" 2>/dev/null + exit 0 } trap cleanup TERM INT @@ -83,8 +83,8 @@ trap cleanup TERM INT # status fresh. This keeps the parent Tailscale_VPN (C launcher) in the process # table so pidof finds it and the camera web UI shows "Running" instead of "Stopped". while kill -0 "$TAILSCALED_PID" 2>/dev/null; do - publish_status - sleep 5 + publish_status + sleep 5 done rm -f "$STATUS_FILE" 2>/dev/null diff --git a/common/app/Tailscale_VPN_run b/common/app/Tailscale_VPN_run index f148c06..ff7172e 100644 --- a/common/app/Tailscale_VPN_run +++ b/common/app/Tailscale_VPN_run @@ -27,48 +27,48 @@ ACCEPT_ROUTES="false" ADVERTISE_ROUTES="" if [ -f "$STATE_DIR/params.conf" ]; then - . "$STATE_DIR/params.conf" + . "$STATE_DIR/params.conf" fi if [ "$VARIANT" = "root" ]; then - logger -t "Tailscale_VPN" "Starting (root mode): custom_server=${CUSTOM_SERVER:-(default)}" + logger -t "Tailscale_VPN" "Starting (root mode): custom_server=${CUSTOM_SERVER:-(default)}" else - logger -t "Tailscale_VPN" "Starting: http_port=$CONF_HTTP socks_port=$CONF_SOCKS custom_server=${CUSTOM_SERVER:-(default)}" + logger -t "Tailscale_VPN" "Starting: http_port=$CONF_HTTP socks_port=$CONF_SOCKS custom_server=${CUSTOM_SERVER:-(default)}" - # Check whether a TCP port is already bound - is_port_in_use() { - local port=$1 - local hex_port - hex_port=$(printf '%04X' "$port") - grep -q ":${hex_port} " /proc/net/tcp 2>/dev/null && return 0 - grep -q ":${hex_port} " /proc/net/tcp6 2>/dev/null && return 0 - return 1 - } + # Check whether a TCP port is already bound + is_port_in_use() { + local port=$1 + local hex_port + hex_port=$(printf '%04X' "$port") + grep -q ":${hex_port} " /proc/net/tcp 2>/dev/null && return 0 + grep -q ":${hex_port} " /proc/net/tcp6 2>/dev/null && return 0 + return 1 + } - if is_port_in_use "$CONF_HTTP"; then - logger -t "Tailscale_VPN" "ERROR: HTTP proxy port $CONF_HTTP is already in use. Change it in Settings." - exit 1 - fi - if is_port_in_use "$CONF_SOCKS"; then - logger -t "Tailscale_VPN" "ERROR: SOCKS5 port $CONF_SOCKS is already in use. Change it in Settings." - exit 1 - fi + if is_port_in_use "$CONF_HTTP"; then + logger -t "Tailscale_VPN" "ERROR: HTTP proxy port $CONF_HTTP is already in use. Change it in Settings." + exit 1 + fi + if is_port_in_use "$CONF_SOCKS"; then + logger -t "Tailscale_VPN" "ERROR: SOCKS5 port $CONF_SOCKS is already in use. Change it in Settings." + exit 1 + fi fi logger -t "Tailscale_VPN" "Starting tailscaled daemon" if [ "$VARIANT" = "root" ]; then - $TAILSCALED_PATH \ - --state="$STATE_DIR/tailscaled.state" \ - --socket=$SOCKET_PATH \ - >/dev/null 2>&1 & + $TAILSCALED_PATH \ + --state="$STATE_DIR/tailscaled.state" \ + --socket=$SOCKET_PATH \ + >/dev/null 2>&1 & else - $TAILSCALED_PATH \ - --state="$STATE_DIR/tailscaled.state" \ - --socket=$SOCKET_PATH \ - --socks5-server=localhost:$CONF_SOCKS \ - --outbound-http-proxy-listen=localhost:$CONF_HTTP \ - --tun=userspace-networking \ - >/dev/null 2>&1 & + $TAILSCALED_PATH \ + --state="$STATE_DIR/tailscaled.state" \ + --socket=$SOCKET_PATH \ + --socks5-server=localhost:$CONF_SOCKS \ + --outbound-http-proxy-listen=localhost:$CONF_HTTP \ + --tun=userspace-networking \ + >/dev/null 2>&1 & fi TAILSCALED_PID=$! @@ -77,19 +77,19 @@ sleep 2 TAILSCALE_CMD="$TAILSCALE_PATH --socket=$SOCKET_PATH up --reset --hostname=$(hostname)" if [ -n "$CUSTOM_SERVER" ]; then - TAILSCALE_CMD="$TAILSCALE_CMD --login-server $CUSTOM_SERVER" + TAILSCALE_CMD="$TAILSCALE_CMD --login-server $CUSTOM_SERVER" fi if [ -n "$AUTH_KEY" ]; then - TAILSCALE_CMD="$TAILSCALE_CMD --authkey $AUTH_KEY" + TAILSCALE_CMD="$TAILSCALE_CMD --authkey $AUTH_KEY" fi if [ "$ACCEPT_DNS" = "true" ]; then - TAILSCALE_CMD="$TAILSCALE_CMD --accept-dns=true" + TAILSCALE_CMD="$TAILSCALE_CMD --accept-dns=true" fi if [ "$ACCEPT_ROUTES" = "true" ]; then - TAILSCALE_CMD="$TAILSCALE_CMD --accept-routes=true" + TAILSCALE_CMD="$TAILSCALE_CMD --accept-routes=true" fi # Advertise LAN subnets so this camera acts as a subnet router. Comma-separated @@ -99,11 +99,11 @@ fi # forward packets between the tailnet and the LAN, so enable IP forwarding. # Routes must still be approved in the Tailscale admin console either way. if [ -n "$ADVERTISE_ROUTES" ]; then - if [ "$VARIANT" = "root" ]; then - echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null || true - echo 1 > /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || true - fi - TAILSCALE_CMD="$TAILSCALE_CMD --advertise-routes=$ADVERTISE_ROUTES" + if [ "$VARIANT" = "root" ]; then + echo 1 >/proc/sys/net/ipv4/ip_forward 2>/dev/null || true + echo 1 >/proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || true + fi + TAILSCALE_CMD="$TAILSCALE_CMD --advertise-routes=$ADVERTISE_ROUTES" fi # Run `tailscale up` in the background and act on its outcome. If the node needs @@ -115,27 +115,27 @@ fi # for it from here, because in POSIX sh `wait` only works on children of the # current shell — a subshell waiting on the parent's child returns 127. { - eval "$TAILSCALE_CMD" - up_exit=$? - if [ "$up_exit" -eq 0 ]; then - if [ "$VARIANT" = "root" ]; then - logger -t "Tailscale_VPN" "Tailscale VPN is running (root mode)" - else - logger -t "Tailscale_VPN" "Tailscale VPN is running" - fi - # Auth succeeded with a one-time auth key — signal param_bridge to clear it - if [ -n "$AUTH_KEY" ]; then - : > "$STATE_DIR/authkey_clear" - fi - else - logger -t "Tailscale_VPN" "ERROR: tailscale up failed (exit $up_exit)" - fi + eval "$TAILSCALE_CMD" + up_exit=$? + if [ "$up_exit" -eq 0 ]; then + if [ "$VARIANT" = "root" ]; then + logger -t "Tailscale_VPN" "Tailscale VPN is running (root mode)" + else + logger -t "Tailscale_VPN" "Tailscale VPN is running" + fi + # Auth succeeded with a one-time auth key — signal param_bridge to clear it + if [ -n "$AUTH_KEY" ]; then + : >"$STATE_DIR/authkey_clear" + fi + else + logger -t "Tailscale_VPN" "ERROR: tailscale up failed (exit $up_exit)" + fi } & TAILSCALE_UP_PID=$! if [ "$VARIANT" != "root" ]; then - logger -t "Tailscale_VPN" "HTTP/HTTPS proxy: http://127.0.0.1:$CONF_HTTP" - logger -t "Tailscale_VPN" "SOCKS5 proxy: 127.0.0.1:$CONF_SOCKS" + logger -t "Tailscale_VPN" "HTTP/HTTPS proxy: http://127.0.0.1:$CONF_HTTP" + logger -t "Tailscale_VPN" "SOCKS5 proxy: 127.0.0.1:$CONF_SOCKS" fi # Publish tailscale's real backend state as JSON for the web UI to consume. @@ -145,19 +145,19 @@ fi STATUS_FILE="$APP_DIR/html/status.json" publish_status() { - if "$TAILSCALE_PATH" --socket="$SOCKET_PATH" status --json > "$STATUS_FILE.tmp" 2>/dev/null; then - mv "$STATUS_FILE.tmp" "$STATUS_FILE" 2>/dev/null - chmod 644 "$STATUS_FILE" 2>/dev/null - else - rm -f "$STATUS_FILE.tmp" 2>/dev/null - fi + if "$TAILSCALE_PATH" --socket="$SOCKET_PATH" status --json >"$STATUS_FILE.tmp" 2>/dev/null; then + mv "$STATUS_FILE.tmp" "$STATUS_FILE" 2>/dev/null + chmod 644 "$STATUS_FILE" 2>/dev/null + else + rm -f "$STATUS_FILE.tmp" 2>/dev/null + fi } status_loop() { - while true; do - publish_status - sleep 5 - done + while true; do + publish_status + sleep 5 + done } status_loop & STATUS_LOOP_PID=$! @@ -166,11 +166,11 @@ STATUS_LOOP_PID=$! # stop/restart so param_bridge (which signals this script) leaves no orphans or # stale state. cleanup() { - [ -n "$STATUS_LOOP_PID" ] && kill "$STATUS_LOOP_PID" 2>/dev/null - [ -n "$TAILSCALE_UP_PID" ] && kill "$TAILSCALE_UP_PID" 2>/dev/null - [ -n "$TAILSCALED_PID" ] && kill "$TAILSCALED_PID" 2>/dev/null - rm -f "$STATUS_FILE" 2>/dev/null - exit 0 + [ -n "$STATUS_LOOP_PID" ] && kill "$STATUS_LOOP_PID" 2>/dev/null + [ -n "$TAILSCALE_UP_PID" ] && kill "$TAILSCALE_UP_PID" 2>/dev/null + [ -n "$TAILSCALED_PID" ] && kill "$TAILSCALED_PID" 2>/dev/null + rm -f "$STATUS_FILE" 2>/dev/null + exit 0 } trap cleanup TERM INT