#!/bin/sh # Shared helpers for client2server hotplug scripts. # Source this file from 01-wifi / 02-dhcp / etc. # # Behavior: # - Loads UCI config (SERVER_URL / TOKEN / ROUTER_ID). # - Provides send_event() that: # 1. Appends the JSON event to a shared on-disk buffer. # 2. Wakes the Lua agent via SIGHUP so it flushes ASAP. # 3. Falls back to a direct curl POST if the buffer is not writable # (e.g. /var/run not yet mounted in early boot). # # Shared buffer location: /var/run/client2server/buffer # - Same format as client2server-unified.lua's buffer file # (one JSON event per line, NDJSON). # - The Lua agent's buffer.init() reads it on startup, and # buffer.flush() drains it on every reconnect / SIGHUP. CLIENT2SERVER_DIR="${CLIENT2SERVER_DIR:-/var/run/client2server}" BUFFER_FILE="${BUFFER_FILE:-${CLIENT2SERVER_DIR}/buffer}" PID_FILE="${PID_FILE:-${CLIENT2SERVER_DIR}/pid}" ENV_FILE="${ENV_FILE:-${CLIENT2SERVER_DIR}/env}" # Ensure runtime dir exists mkdir -p "$CLIENT2SERVER_DIR" 2>/dev/null # Load config: prefer env file (set by init.d), fall back to UCI if [ -f "$ENV_FILE" ]; then # shellcheck disable=SC1090 . "$ENV_FILE" fi : "${SERVER_URL:=$(uci get client2server.server.url 2>/dev/null)}" : "${ROUTER_ID:=$(uci get client2server.router.id 2>/dev/null)}" : "${TOKEN:=$(uci get client2server.server.token 2>/dev/null)}" # Final fallbacks : "${SERVER_URL:=http://127.0.0.1:3843}" : "${ROUTER_ID:=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo unknown)}" log_msg() { logger -t client2server-hotplug -p user.info "$1" } log_err() { logger -t client2server-hotplug -p user.err "$1" } # Build a JSON event in the same shape the Lua agent uses. # Args: event_type, data_json build_event_json() { local event_type="$1" local data="$2" printf '{"router_id":"%s","event":"%s","data":%s}' \ "$ROUTER_ID" "$event_type" "$data" } # Append to disk buffer (atomic-ish via mv from a temp file). # Returns 0 on success, 1 on failure. spool_event() { local json="$1" local tmp tmp=$(mktemp "${BUFFER_FILE}.XXXXXX") || return 1 if ! printf '%s\n' "$json" >> "$tmp"; then rm -f "$tmp" 2>/dev/null return 1 fi if ! cat "$tmp" >> "$BUFFER_FILE" 2>/dev/null; then rm -f "$tmp" 2>/dev/null return 1 fi rm -f "$tmp" 2>/dev/null return 0 } # Wake the Lua agent (best-effort). # We touch `/wake`; the agent's main loop polls for it # and triggers an immediate buffer flush. This is more portable than # SIGHUP across Lua 5.1/5.2/LuaJIT builds. wake_agent() { touch "$CLIENT2SERVER_DIR/wake" 2>/dev/null || true # Also try SIGHUP as a fast path (harmless if PID is stale or # the agent doesn't install a handler). if [ -f "$PID_FILE" ]; then local pid pid=$(cat "$PID_FILE" 2>/dev/null) if [ -n "$pid" ] && [ "$pid" -gt 0 ] 2>/dev/null; then kill -HUP "$pid" 2>/dev/null || true fi fi } # Direct HTTP POST fallback. Args: json_event direct_post() { local json="$1" if [ -n "$TOKEN" ]; then curl -s -m 3 -X POST "$SERVER_URL/api/events" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d "$json" >/dev/null 2>&1 else curl -s -m 3 -X POST "$SERVER_URL/api/events" \ -H "Content-Type: application/json" \ -d "$json" >/dev/null 2>&1 fi } # Main entry point. Args: event_type, data_json # Behavior: # 1. Try to spool to disk + wake agent (preferred: survives outages). # 2. If spooling fails, do a one-shot direct POST (best effort, may drop). send_event() { local event_type="$1" local data="$2" local json json=$(build_event_json "$event_type" "$data") if spool_event "$json"; then log_msg "Spooled: $event_type" wake_agent return 0 fi # Fallback: try direct POST (may be lost if offline) log_err "Spool failed, falling back to direct POST: $event_type" direct_post "$json" log_msg "Sent (fallback): $event_type" }