- jshn cannot embed raw JSON in objects, use printf instead
- Return proper {"success":true,"result":{...}} format
- Handle error cases with escaped error messages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
338 lines
9.2 KiB
Bash
338 lines
9.2 KiB
Bash
#!/bin/sh
|
|
|
|
# RPCD handler for luci-app-rtty-remote
|
|
# Provides RPC interface for RTTY Remote Control
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Load libraries
|
|
[ -f /usr/lib/secubox/rtty-proxy.sh ] && . /usr/lib/secubox/rtty-proxy.sh
|
|
[ -f /usr/lib/secubox/rtty-session.sh ] && . /usr/lib/secubox/rtty-session.sh
|
|
[ -f /usr/lib/secubox/rtty-auth.sh ] && . /usr/lib/secubox/rtty-auth.sh
|
|
|
|
RTTYCTL="/usr/sbin/rttyctl"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Helper functions
|
|
#------------------------------------------------------------------------------
|
|
|
|
get_config() {
|
|
local section="$1"
|
|
local option="$2"
|
|
local default="$3"
|
|
|
|
config_load rtty-remote
|
|
config_get value "$section" "$option" "$default"
|
|
echo "$value"
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# RPC Methods
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Get server status
|
|
method_status() {
|
|
json_init
|
|
|
|
config_load rtty-remote
|
|
local enabled
|
|
config_get enabled main enabled '0'
|
|
json_add_boolean "enabled" "$enabled"
|
|
|
|
local port
|
|
config_get port main server_port '7681'
|
|
json_add_int "port" "$port"
|
|
|
|
# Check if running
|
|
if pgrep -f "rttyctl server-daemon" >/dev/null 2>&1; then
|
|
json_add_boolean "running" 1
|
|
else
|
|
json_add_boolean "running" 0
|
|
fi
|
|
|
|
# Stats
|
|
if [ -f /srv/rtty-remote/sessions.db ]; then
|
|
local stats=$(session_stats 2>/dev/null)
|
|
json_add_int "total_sessions" "$(echo "$stats" | jsonfilter -e '@.total_sessions' 2>/dev/null || echo 0)"
|
|
json_add_int "active_sessions" "$(echo "$stats" | jsonfilter -e '@.active_sessions' 2>/dev/null || echo 0)"
|
|
json_add_int "total_rpc_calls" "$(echo "$stats" | jsonfilter -e '@.total_rpc_calls' 2>/dev/null || echo 0)"
|
|
json_add_int "unique_nodes" "$(echo "$stats" | jsonfilter -e '@.unique_nodes' 2>/dev/null || echo 0)"
|
|
else
|
|
json_add_int "total_sessions" 0
|
|
json_add_int "active_sessions" 0
|
|
json_add_int "total_rpc_calls" 0
|
|
json_add_int "unique_nodes" 0
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get mesh nodes
|
|
method_get_nodes() {
|
|
local nodes=$($RTTYCTL json-nodes 2>/dev/null)
|
|
[ -z "$nodes" ] && nodes='{"nodes":[]}'
|
|
echo "$nodes"
|
|
}
|
|
|
|
# Get single node details
|
|
method_get_node() {
|
|
local node_id
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var node_id node_id
|
|
|
|
[ -z "$node_id" ] && {
|
|
echo '{"error":"Missing node_id"}'
|
|
return
|
|
}
|
|
|
|
# Get node info from master-link or P2P
|
|
json_init
|
|
json_add_string "node_id" "$node_id"
|
|
|
|
# Try to get address
|
|
local addr=$($RTTYCTL node "$node_id" 2>&1 | grep "Address:" | awk '{print $2}')
|
|
json_add_string "address" "$addr"
|
|
|
|
# Check connectivity
|
|
if [ -n "$addr" ] && ping -c 1 -W 2 "$addr" >/dev/null 2>&1; then
|
|
json_add_string "status" "online"
|
|
else
|
|
json_add_string "status" "offline"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Execute remote RPC call
|
|
method_rpc_call() {
|
|
local node_id object method params
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var node_id node_id
|
|
json_get_var object object
|
|
json_get_var method method
|
|
json_get_var params params
|
|
|
|
[ -z "$node_id" ] || [ -z "$object" ] || [ -z "$method" ] && {
|
|
echo '{"error":"Missing required parameters (node_id, object, method)"}'
|
|
return
|
|
}
|
|
|
|
# Execute via rttyctl
|
|
local result=$($RTTYCTL rpc "$node_id" "$object" "$method" "$params" 2>&1)
|
|
local rc=$?
|
|
|
|
if [ $rc -eq 0 ] && [ -n "$result" ]; then
|
|
# Check if result is valid JSON
|
|
if echo "$result" | jsonfilter -e '@' >/dev/null 2>&1; then
|
|
# Output success with embedded JSON result
|
|
printf '{"success":true,"result":%s}' "$result"
|
|
else
|
|
# Result is not JSON, wrap as string
|
|
printf '{"success":true,"result":"%s"}' "$(echo "$result" | sed 's/"/\\"/g')"
|
|
fi
|
|
else
|
|
# Error case
|
|
local err_msg=$(echo "$result" | sed 's/"/\\"/g' | tr '\n' ' ')
|
|
printf '{"success":false,"error":"%s"}' "$err_msg"
|
|
fi
|
|
}
|
|
|
|
# List remote RPCD objects
|
|
method_rpc_list() {
|
|
local node_id
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var node_id node_id
|
|
|
|
[ -z "$node_id" ] && {
|
|
echo '{"error":"Missing node_id"}'
|
|
return
|
|
}
|
|
|
|
local result=$($RTTYCTL rpc-list "$node_id" 2>&1)
|
|
echo "$result"
|
|
}
|
|
|
|
# Get sessions
|
|
method_get_sessions() {
|
|
local node_id limit
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var node_id node_id
|
|
json_get_var limit limit
|
|
|
|
[ -z "$limit" ] && limit=50
|
|
|
|
session_list "$node_id" "$limit"
|
|
}
|
|
|
|
# Start server
|
|
method_server_start() {
|
|
/etc/init.d/rtty-remote start 2>&1
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
}
|
|
|
|
# Stop server
|
|
method_server_stop() {
|
|
/etc/init.d/rtty-remote stop 2>&1
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
}
|
|
|
|
# Get settings
|
|
method_get_settings() {
|
|
json_init
|
|
|
|
config_load rtty-remote
|
|
|
|
json_add_object "main"
|
|
local val
|
|
config_get val main enabled '0'
|
|
json_add_boolean "enabled" "$val"
|
|
config_get val main server_port '7681'
|
|
json_add_int "server_port" "$val"
|
|
config_get val main auth_method 'master-link'
|
|
json_add_string "auth_method" "$val"
|
|
config_get val main session_ttl '3600'
|
|
json_add_int "session_ttl" "$val"
|
|
config_get val main max_sessions '10'
|
|
json_add_int "max_sessions" "$val"
|
|
json_close_object
|
|
|
|
json_add_object "security"
|
|
config_get val security require_wg '1'
|
|
json_add_boolean "require_wg" "$val"
|
|
config_get val security allowed_networks ''
|
|
json_add_string "allowed_networks" "$val"
|
|
json_close_object
|
|
|
|
json_add_object "proxy"
|
|
config_get val proxy rpc_timeout '30'
|
|
json_add_int "rpc_timeout" "$val"
|
|
config_get val proxy batch_limit '50'
|
|
json_add_int "batch_limit" "$val"
|
|
config_get val proxy cache_ttl '60'
|
|
json_add_int "cache_ttl" "$val"
|
|
json_close_object
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Set settings
|
|
method_set_settings() {
|
|
read -r input
|
|
|
|
# Parse and apply settings
|
|
local enabled=$(echo "$input" | jsonfilter -e '@.main.enabled' 2>/dev/null)
|
|
[ -n "$enabled" ] && uci set rtty-remote.main.enabled="$enabled"
|
|
|
|
local port=$(echo "$input" | jsonfilter -e '@.main.server_port' 2>/dev/null)
|
|
[ -n "$port" ] && uci set rtty-remote.main.server_port="$port"
|
|
|
|
local auth=$(echo "$input" | jsonfilter -e '@.main.auth_method' 2>/dev/null)
|
|
[ -n "$auth" ] && uci set rtty-remote.main.auth_method="$auth"
|
|
|
|
local ttl=$(echo "$input" | jsonfilter -e '@.main.session_ttl' 2>/dev/null)
|
|
[ -n "$ttl" ] && uci set rtty-remote.main.session_ttl="$ttl"
|
|
|
|
uci commit rtty-remote
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
}
|
|
|
|
# Replay session
|
|
method_replay_session() {
|
|
local session_id target_node
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var session_id session_id
|
|
json_get_var target_node target_node
|
|
|
|
[ -z "$session_id" ] || [ -z "$target_node" ] && {
|
|
echo '{"error":"Missing session_id or target_node"}'
|
|
return
|
|
}
|
|
|
|
local result=$($RTTYCTL replay "$session_id" "$target_node" 2>&1)
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "$result"
|
|
json_dump
|
|
}
|
|
|
|
# Connect to node (start terminal)
|
|
method_connect() {
|
|
local node_id
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var node_id node_id
|
|
|
|
[ -z "$node_id" ] && {
|
|
echo '{"error":"Missing node_id"}'
|
|
return
|
|
}
|
|
|
|
# Get node address
|
|
local addr=$($RTTYCTL node "$node_id" 2>&1 | grep "Address:" | awk '{print $2}')
|
|
|
|
json_init
|
|
json_add_string "node_id" "$node_id"
|
|
json_add_string "address" "$addr"
|
|
json_add_string "terminal_url" "ws://localhost:7681/ws"
|
|
json_add_string "ssh_command" "ssh root@$addr"
|
|
json_dump
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Main dispatcher
|
|
#------------------------------------------------------------------------------
|
|
|
|
case "$1" in
|
|
list)
|
|
cat << 'EOF'
|
|
{
|
|
"status": {},
|
|
"get_nodes": {},
|
|
"get_node": {"node_id": "string"},
|
|
"rpc_call": {"node_id": "string", "object": "string", "method": "string", "params": "string"},
|
|
"rpc_list": {"node_id": "string"},
|
|
"get_sessions": {"node_id": "string", "limit": 50},
|
|
"server_start": {},
|
|
"server_stop": {},
|
|
"get_settings": {},
|
|
"set_settings": {"config": "object"},
|
|
"replay_session": {"session_id": "integer", "target_node": "string"},
|
|
"connect": {"node_id": "string"}
|
|
}
|
|
EOF
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status) method_status ;;
|
|
get_nodes) method_get_nodes ;;
|
|
get_node) method_get_node ;;
|
|
rpc_call) method_rpc_call ;;
|
|
rpc_list) method_rpc_list ;;
|
|
get_sessions) method_get_sessions ;;
|
|
server_start) method_server_start ;;
|
|
server_stop) method_server_stop ;;
|
|
get_settings) method_get_settings ;;
|
|
set_settings) method_set_settings ;;
|
|
replay_session) method_replay_session ;;
|
|
connect) method_connect ;;
|
|
*)
|
|
echo '{"error":"Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|