#!/bin/sh # event-forwarder - Forward OpenWrt events to central server # Copyright (c) 2026 Luis Rosales - MIT License # Size: ~20KB set -euo pipefail # Config CONFIG_FILE="/etc/config/event-forwarder" LOG_TAG="event-forwarder" # Load config load_config() { # Server URL SERVER_URL=$(uci get event-forwarder.server.url 2>/dev/null || echo "") SERVER_TOKEN=$(uci get event-forwarder.server.token 2>/dev/null || echo "") TIMEOUT=$(uci get event-forwarder.server.timeout 2>/dev/null || echo "10") RETRY=$(uci get event-forwarder.server.retry 2>/dev/null || echo "3") # Router ID (use hostname if not set) ROUTER_ID=$(uci get event-forwarder.router.id 2>/dev/null || hostname) # Event flags EVT_WIFI_CONN=$(uci get event-forwarder.events.wifi_connect 2>/dev/null || echo "1") EVT_WIFI_DISC=$(uci get event-forwarder.events.wifi_disconnect 2>/dev/null || echo "1") EVT_DHCP=$(uci get event-forwarder.events.dhcp_lease 2>/dev/null || echo "1") # Validate if [ -z "$SERVER_URL" ]; then log_err "Server URL not configured" exit 1 fi } # Logger log_debug() { [ "${DEBUG:-0}" = "1" ] && logger -t "$LOG_TAG" -p user.debug "$@"; } log_info() { logger -t "$LOG_TAG" -p user.info "$@"; } log_err() { logger -t "$LOG_TAG" -p user.err "$@"; } # Send event to server send_event() { local event_json="$1" local attempt=0 while [ $attempt -lt $RETRY ]; do attempt=$((attempt + 1)) response=$(curl -s -w "%{http_code}" \ --max-time "$TIMEOUT" \ -X POST "$SERVER_URL" \ -H "Authorization: Bearer $SERVER_TOKEN" \ -H "Content-Type: application/json" \ -d "$event_json" 2>/dev/null) || true http_code="${response: -3}" if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then log_debug "Event sent successfully" return 0 fi log_debug "Attempt $attempt failed (HTTP $http_code)" sleep 1 done log_err "Failed to send event after $RETRY attempts" return 1 } # Build JSON payload build_json() { local event_type="$1" local mac="$2" local ip="$3" local hostname="$4" local extra="$5" # Escape special chars mac="${mac:-null}" ip="${ip:-null}" hostname="${hostname:-null}" cat < $ip ($hostname)" send_event "$json" fi done old_leases="$current_leases" fi fi sleep 5 done } # 3. Interface events via ubus netifd listen_interfaces() { ubus -m listen network.interface 2>/dev/null | while read event; do event_type=$(echo "$event" | jsonfilter -e '@.handler') [ -z "$event_type" ] && continue device=$(echo "$event" | jsonfilter -e '@.interface') action=$(echo "$event" | jsonfilter -e '@.action') json=$(build_json "interface_${action}" "null" "null" "null" ", \"device\": \"$device\", \"action\": \"$action\"") log_info "Interface $action: $device" send_event "$json" & done } # 4. Generic ubus events listen_ubus() { ubus -m listen 2>/dev/null | while read event; do # Filter network-related events event_json=$(echo "$event" | jsonfilter -e '@') # Check for wireless events if echo "$event" | grep -q "wpa"; then log_debug "WiFi event: $event" fi done } # Main loop - runs all listeners concurrently main() { load_config log_info "Starting event forwarder..." log_info "Server: $SERVER_URL" # Trap signals for graceful shutdown trap 'log_info "Shutting down..."; kill 0 2>/dev/null; exit 0' TERM INT # Start listeners in background listen_dhcp & local dhcp_pid=$! listen_interfaces & local if_pid=$! log_info "Event listeners started (DHCP:$dhcp_pid, IFACE:$if_pid)" # Wait for any signal wait } # Run main main "$@"