_lib.sh 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/bin/sh
  2. # Shared helpers for client2server hotplug scripts.
  3. # Source this file from 01-wifi / 02-dhcp / etc.
  4. #
  5. # Behavior:
  6. # - Loads UCI config (SERVER_URL / TOKEN / ROUTER_ID).
  7. # - Provides send_event() that:
  8. # 1. Appends the JSON event to a shared on-disk buffer.
  9. # 2. Wakes the Lua agent via SIGHUP so it flushes ASAP.
  10. # 3. Falls back to a direct curl POST if the buffer is not writable
  11. # (e.g. /var/run not yet mounted in early boot).
  12. #
  13. # Shared buffer location: /var/run/client2server/buffer
  14. # - Same format as client2server-unified.lua's buffer file
  15. # (one JSON event per line, NDJSON).
  16. # - The Lua agent's buffer.init() reads it on startup, and
  17. # buffer.flush() drains it on every reconnect / SIGHUP.
  18. CLIENT2SERVER_DIR="${CLIENT2SERVER_DIR:-/var/run/client2server}"
  19. BUFFER_FILE="${BUFFER_FILE:-${CLIENT2SERVER_DIR}/buffer}"
  20. PID_FILE="${PID_FILE:-${CLIENT2SERVER_DIR}/pid}"
  21. ENV_FILE="${ENV_FILE:-${CLIENT2SERVER_DIR}/env}"
  22. # Ensure runtime dir exists
  23. mkdir -p "$CLIENT2SERVER_DIR" 2>/dev/null
  24. # Load config: prefer env file (set by init.d), fall back to UCI
  25. if [ -f "$ENV_FILE" ]; then
  26. # shellcheck disable=SC1090
  27. . "$ENV_FILE"
  28. fi
  29. : "${SERVER_URL:=$(uci get client2server.server.url 2>/dev/null)}"
  30. : "${ROUTER_ID:=$(uci get client2server.router.id 2>/dev/null)}"
  31. : "${TOKEN:=$(uci get client2server.server.token 2>/dev/null)}"
  32. # Final fallbacks
  33. : "${SERVER_URL:=http://127.0.0.1:3843}"
  34. : "${ROUTER_ID:=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo unknown)}"
  35. log_msg() {
  36. logger -t client2server-hotplug -p user.info "$1"
  37. }
  38. log_err() {
  39. logger -t client2server-hotplug -p user.err "$1"
  40. }
  41. # Build a JSON event in the same shape the Lua agent uses.
  42. # Args: event_type, data_json
  43. build_event_json() {
  44. local event_type="$1"
  45. local data="$2"
  46. printf '{"router_id":"%s","event":"%s","data":%s}' \
  47. "$ROUTER_ID" "$event_type" "$data"
  48. }
  49. # Append to disk buffer (atomic-ish via mv from a temp file).
  50. # Returns 0 on success, 1 on failure.
  51. spool_event() {
  52. local json="$1"
  53. local tmp
  54. tmp=$(mktemp "${BUFFER_FILE}.XXXXXX") || return 1
  55. if ! printf '%s\n' "$json" >> "$tmp"; then
  56. rm -f "$tmp" 2>/dev/null
  57. return 1
  58. fi
  59. if ! cat "$tmp" >> "$BUFFER_FILE" 2>/dev/null; then
  60. rm -f "$tmp" 2>/dev/null
  61. return 1
  62. fi
  63. rm -f "$tmp" 2>/dev/null
  64. return 0
  65. }
  66. # Wake the Lua agent (best-effort).
  67. # We touch `<state_dir>/wake`; the agent's main loop polls for it
  68. # and triggers an immediate buffer flush. This is more portable than
  69. # SIGHUP across Lua 5.1/5.2/LuaJIT builds.
  70. wake_agent() {
  71. touch "$CLIENT2SERVER_DIR/wake" 2>/dev/null || true
  72. # Also try SIGHUP as a fast path (harmless if PID is stale or
  73. # the agent doesn't install a handler).
  74. if [ -f "$PID_FILE" ]; then
  75. local pid
  76. pid=$(cat "$PID_FILE" 2>/dev/null)
  77. if [ -n "$pid" ] && [ "$pid" -gt 0 ] 2>/dev/null; then
  78. kill -HUP "$pid" 2>/dev/null || true
  79. fi
  80. fi
  81. }
  82. # Direct HTTP POST fallback. Args: json_event
  83. direct_post() {
  84. local json="$1"
  85. if [ -n "$TOKEN" ]; then
  86. curl -s -m 3 -X POST "$SERVER_URL/api/events" \
  87. -H "Content-Type: application/json" \
  88. -H "Authorization: Bearer $TOKEN" \
  89. -d "$json" >/dev/null 2>&1
  90. else
  91. curl -s -m 3 -X POST "$SERVER_URL/api/events" \
  92. -H "Content-Type: application/json" \
  93. -d "$json" >/dev/null 2>&1
  94. fi
  95. }
  96. # Main entry point. Args: event_type, data_json
  97. # Behavior:
  98. # 1. Try to spool to disk + wake agent (preferred: survives outages).
  99. # 2. If spooling fails, do a one-shot direct POST (best effort, may drop).
  100. send_event() {
  101. local event_type="$1"
  102. local data="$2"
  103. local json
  104. json=$(build_event_json "$event_type" "$data")
  105. if spool_event "$json"; then
  106. log_msg "Spooled: $event_type"
  107. wake_agent
  108. return 0
  109. fi
  110. # Fallback: try direct POST (may be lost if offline)
  111. log_err "Spool failed, falling back to direct POST: $event_type"
  112. direct_post "$json"
  113. log_msg "Sent (fallback): $event_type"
  114. }