#!/bin/sh /etc/rc.common
# Copyright (c) 2026 Dab Co. Limited. All rights reserved.
#
# This software and its documentation are the confidential and
# proprietary information of Dab Co. Limited. Redistribution or use
# in source or binary forms, with or without modification, is not
# permitted without the express written consent of Dab Co. Limited.
# /etc/init.d/pps-vpn — Pairpoint Secure VPN service for Lantronix G526
# Init system: procd (OpenWrt-fork)

USE_PROCD=1
START=95
STOP=10

INSTALL_DIR="${PPS_VPN_HOME:-/usr/lib/pps-vpn}"
BIN="$INSTALL_DIR/bin/pps-vpn"        # PairPoint OpenVPN with TLS-PSK (bundled)
CONF="$INSTALL_DIR/client.conf"
PID_FILE="/var/run/pps-vpn.pid"
LOG_FILE="$INSTALL_DIR/logs/pps-vpn.log"
SCRIPTS_DIR="$INSTALL_DIR/scripts"

# Inherited by openvpn child — pp PSK script
PSK_SCRIPT="$INSTALL_DIR/pps-crypto/pps-vpn-psk.sh"

# Instance declaration is factored out because EVERY exit path from
# start_service() that leaves a healthy process running must still declare it.
# procd treats the instance set built during a `start` as the complete desired
# state: return without calling procd_open_instance and it concludes the
# service should have no instances and kills the running one. Verified on
# device with a scratch service — a start whose preflight failed took down a
# live instance (SIGTERM, then SIGKILL).
_declare_instance() {
    procd_open_instance
    procd_set_param command "$BIN" \
        --config "$CONF" \
        --writepid "$PID_FILE" \
        --log-append "$LOG_FILE"
    procd_set_param env \
        PPS_VPN_ENABLE_TLS_PSK=1 \
        PPS_VPN_PSK_CLIENT_SCRIPT="$PSK_SCRIPT" \
        LD_LIBRARY_PATH="$INSTALL_DIR/lib"
    procd_set_param respawn 3600 5 3
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_set_param pidfile "$PID_FILE"
    procd_close_instance
}

_vpn_running() {
    local _p
    [ -f "$PID_FILE" ] || return 1
    _p=$(cat "$PID_FILE" 2>/dev/null)
    [ -n "$_p" ] || return 1
    kill -0 "$_p" 2>/dev/null
}

start_service() {
    # Clear the deliberate-stop marker; we are being started on purpose.
    rm -f /var/run/pps-vpn.stopped 2>/dev/null || true

    # A second `start` on an already-running service must be a true
    # no-op. It used to re-run config regeneration and the full SIM preflight
    # (~33 s of modem access, contending with eventsms and key generation)
    # only to hand procd an identical instance; and if that redundant
    # preflight failed, we returned before declaring the instance and procd
    # tore down a perfectly healthy tunnel. Re-declare and leave.
    if _vpn_running; then
        _declare_instance
        return 0
    fi

    mkdir -p "$INSTALL_DIR/logs"

    # Rotate large logs (>1MiB)
    if [ -f "$LOG_FILE" ]; then
        sz=$(wc -c < "$LOG_FILE" 2>/dev/null || echo 0)
        [ "$sz" -gt 1048576 ] && mv "$LOG_FILE" "${LOG_FILE}.old"
    fi

    # Regenerate openvpn client.conf from UCI on every start
    "$SCRIPTS_DIR/config_manager.sh" generate_openvpn || {
        echo "[pps-vpn] config_manager.sh generate_openvpn failed" >&2
        # Same rule as the preflight branch: a bare `return 1` here would let
        # procd retire a live instance. Guard every early exit, not just the
        # ones we happened to test.
        if _vpn_running; then
            _declare_instance
            return 0
        fi
        return 1
    }
    # G3 — regenerate SDK config on EVERY start so UCI changes to
    # simtrust_host / simtrust_port / naf_id / serial_port / baudrate
    # take effect on `restart` without rebuild. The earlier "skip if
    # config.toml exists" guard silently dropped all UCI-driven SDK
    # changes (pps-router-define G3). PLATFORM_CONFIG_FRAGMENT (G16) is
    # re-injected idempotently inside [simtrust.common] each time.
    "$SCRIPTS_DIR/config_manager.sh" generate_sdk || {
        echo "[pps-vpn] config_manager.sh generate_sdk failed (continuing)" >&2
    }

    # G27 pre-flight — cellular/DNS/SIM/BSF gates. If it fails we don't
    # open the procd instance (procd start_service returning non-zero
    # means the service simply isn't started; boot / user will re-attempt).
    sh "$SCRIPTS_DIR/init.sh" preflight
    _pf_rc=$?
    if [ "$_pf_rc" != "0" ]; then
        echo "[pps-vpn] pre-flight failed (rc=$_pf_rc) — not starting" >&2
        # Covers the race where the service came up while we were in
        # preflight: never let a failed start tear down a live instance.
        if _vpn_running; then
            _declare_instance
            return 0
        fi
        return 1
    fi

    _declare_instance
}

stop_service() {
    # Mark this as a DELIBERATE stop. netmon auto-recovers the VPN
    # when procd has given up respawning it (see pps-vpn-netmon.sh); without
    # this marker it could not tell "procd gave up" from "the operator
    # stopped it" and would fight an intentional stop. /var/run is tmpfs, so
    # the marker clears on reboot and the normal boot start wins.
    touch /var/run/pps-vpn.stopped 2>/dev/null || true

    if [ -f "$PID_FILE" ]; then
        kill -TERM "$(cat "$PID_FILE")" 2>/dev/null
        sleep 1
        kill -0 "$(cat "$PID_FILE")" 2>/dev/null && \
            kill -KILL "$(cat "$PID_FILE")" 2>/dev/null
        rm -f "$PID_FILE"
    fi
    "$SCRIPTS_DIR/network_utils.sh" firewall down 2>/dev/null || true
}

reload_service() {
    stop
    start
}

service_triggers() {
    procd_add_reload_trigger "ppsvpn"
}
