#!/bin/sh # System Hub RPCD Backend # Central system control and monitoring # Version: 0.1.1 . /lib/functions.sh . /usr/share/libubox/jshn.sh get_pkg_version() { local ctrl="/usr/lib/opkg/status" local pkg="luci-app-system-hub" if [ -f "$ctrl" ]; then awk -F': ' '/Package: '"$pkg"'/,/^Package/ { if ($1 == "Version") { print $2; exit } }' "$ctrl" else echo "unknown" fi } PKG_VERSION="$(get_pkg_version)" DIAG_DIR="/tmp/system-hub/diagnostics" mkdir -p "$DIAG_DIR" safe_filename() { local name="$1" name="${name##*/}" name="${name//../}" echo "$name" } # Get comprehensive system status status() { json_init # Basic info local hostname=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "unknown") local uptime=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo 0) local model=$(cat /tmp/sysinfo/model 2>/dev/null || cat /proc/device-tree/model 2>/dev/null | tr -d '\0' || echo "Unknown") json_add_string "version" "$PKG_VERSION" json_add_string "hostname" "$hostname" json_add_string "model" "$model" json_add_int "uptime" "$uptime" # Health metrics local cpu_load=$(awk '{print $1}' /proc/loadavg 2>/dev/null || echo "0") # Memory local mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_free=$(awk '/MemFree/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_used=$((mem_total - mem_available)) local mem_percent=0 if [ "$mem_total" -gt 0 ]; then mem_percent=$(( (mem_used * 100) / mem_total )) fi json_add_object "health" json_add_string "cpu_load" "$cpu_load" json_add_int "mem_total_kb" "$mem_total" json_add_int "mem_used_kb" "$mem_used" json_add_int "mem_percent" "$mem_percent" json_close_object # Storage local disk_root=$(df / | awk 'NR==2 {gsub("%","",$5); print $5}' 2>/dev/null || echo 0) json_add_int "disk_percent" "$disk_root" # Service count local service_count=$(ls /etc/init.d/ 2>/dev/null | wc -l) json_add_int "service_count" "$service_count" json_dump } # Get detailed system information get_system_info() { json_init # Hostname local hostname=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "unknown") json_add_string "version" "$PKG_VERSION" json_add_string "hostname" "$hostname" # Model local model=$(cat /tmp/sysinfo/model 2>/dev/null || cat /proc/device-tree/model 2>/dev/null | tr -d '\0' || echo "Unknown") json_add_string "model" "$model" # Board name local board=$(cat /tmp/sysinfo/board_name 2>/dev/null || echo "unknown") json_add_string "board" "$board" # OpenWrt version local openwrt_version=$(cat /etc/openwrt_release 2>/dev/null | grep DISTRIB_DESCRIPTION | cut -d"'" -f2 || echo "Unknown") json_add_string "openwrt_version" "$openwrt_version" # Kernel version local kernel=$(uname -r 2>/dev/null || echo "unknown") json_add_string "kernel" "$kernel" # Architecture local arch=$(uname -m 2>/dev/null || echo "unknown") json_add_string "architecture" "$arch" # Uptime local uptime_sec=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo 0) json_add_int "uptime_seconds" "$uptime_sec" # Uptime formatted local days=$((uptime_sec / 86400)) local hours=$(((uptime_sec % 86400) / 3600)) local mins=$(((uptime_sec % 3600) / 60)) json_add_string "uptime_formatted" "${days}d ${hours}h ${mins}m" # Local time local localtime=$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown") json_add_string "local_time" "$localtime" json_dump } # Get system health metrics get_health() { json_init # CPU usage calculation local cpu_cores=$(grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo 1) local load=$(cat /proc/loadavg 2>/dev/null || echo "0 0 0") local load1=$(echo $load | awk '{print $1}') local load5=$(echo $load | awk '{print $2}') local load15=$(echo $load | awk '{print $3}') # Process count (v0.3.2) local processes_running=$(echo $load | awk '{split($4,a,"/"); print a[1]}') local processes_total=$(echo $load | awk '{split($4,a,"/"); print a[2]}') # Calculate CPU usage percentage (load / cores * 100) local cpu_usage=$(awk -v load="$load1" -v cores="$cpu_cores" 'BEGIN { printf "%.0f", (load / cores) * 100 }') [ "$cpu_usage" -gt 100 ] && cpu_usage=100 # CPU status local cpu_status="ok" [ "$cpu_usage" -ge 80 ] && cpu_status="warning" [ "$cpu_usage" -ge 95 ] && cpu_status="critical" json_add_object "cpu" json_add_int "usage" "$cpu_usage" json_add_string "status" "$cpu_status" json_add_string "load_1m" "$load1" json_add_string "load_5m" "$load5" json_add_string "load_15m" "$load15" json_add_int "cores" "$cpu_cores" json_add_int "processes_running" "${processes_running:-0}" json_add_int "processes_total" "${processes_total:-0}" json_close_object # Memory (v0.3.2: added swap support) local mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_free=$(awk '/MemFree/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo 2>/dev/null || echo $mem_free) local mem_buffers=$(awk '/Buffers/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_cached=$(awk '/^Cached/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local mem_used=$((mem_total - mem_available)) # Swap info (v0.3.2) local swap_total=$(awk '/SwapTotal/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local swap_free=$(awk '/SwapFree/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) local swap_used=$((swap_total - swap_free)) local swap_usage=0 if [ "$swap_total" -gt 0 ]; then swap_usage=$(( (swap_used * 100) / swap_total )) fi local mem_usage=0 if [ "$mem_total" -gt 0 ]; then mem_usage=$(( (mem_used * 100) / mem_total )) fi # Memory status local mem_status="ok" [ "$mem_usage" -ge 80 ] && mem_status="warning" [ "$mem_usage" -ge 95 ] && mem_status="critical" json_add_object "memory" json_add_int "total_kb" "$mem_total" json_add_int "free_kb" "$mem_free" json_add_int "available_kb" "$mem_available" json_add_int "used_kb" "$mem_used" json_add_int "buffers_kb" "$mem_buffers" json_add_int "cached_kb" "$mem_cached" json_add_int "usage" "$mem_usage" json_add_string "status" "$mem_status" json_add_int "swap_total_kb" "$swap_total" json_add_int "swap_used_kb" "$swap_used" json_add_int "swap_usage" "$swap_usage" json_close_object # Disk (root filesystem) local disk_total=$(df / | awk 'NR==2 {print $2}') local disk_used=$(df / | awk 'NR==2 {print $3}') local disk_usage=$(df / | awk 'NR==2 {gsub("%","",$5); print $5}' 2>/dev/null || echo 0) # Disk status local disk_status="ok" [ "$disk_usage" -ge 80 ] && disk_status="warning" [ "$disk_usage" -ge 95 ] && disk_status="critical" json_add_object "disk" json_add_int "total_kb" "$disk_total" json_add_int "used_kb" "$disk_used" json_add_int "usage" "$disk_usage" json_add_string "status" "$disk_status" json_close_object # Temperature local temp_value=0 local temp_status="ok" for zone in /sys/class/thermal/thermal_zone*/temp; do if [ -f "$zone" ]; then local temp=$(cat "$zone" 2>/dev/null || echo 0) local temp_c=$((temp / 1000)) # Use the highest temperature [ "$temp_c" -gt "$temp_value" ] && temp_value="$temp_c" fi done [ "$temp_value" -ge 70 ] && temp_status="warning" [ "$temp_value" -ge 85 ] && temp_status="critical" json_add_object "temperature" json_add_int "value" "$temp_value" json_add_string "status" "$temp_status" json_close_object # Network (WAN connectivity + throughput v0.3.2) local wan_up=0 local wan_status="error" if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then wan_up=1 wan_status="ok" fi # Network throughput (v0.3.2) - get total RX/TX from all interfaces local rx_bytes=0 local tx_bytes=0 for iface in /sys/class/net/*; do [ -d "$iface" ] || continue local ifname=$(basename "$iface") # Skip loopback and virtual interfaces case "$ifname" in lo|br-*|wlan*-*) continue ;; esac local rx=$(cat "$iface/statistics/rx_bytes" 2>/dev/null || echo 0) local tx=$(cat "$iface/statistics/tx_bytes" 2>/dev/null || echo 0) rx_bytes=$((rx_bytes + rx)) tx_bytes=$((tx_bytes + tx)) done json_add_object "network" json_add_boolean "wan_up" "$wan_up" json_add_string "status" "$wan_status" json_add_int "rx_bytes" "$rx_bytes" json_add_int "tx_bytes" "$tx_bytes" json_close_object # Services local running_count=0 local failed_count=0 for service in /etc/init.d/*; do [ -x "$service" ] || continue local name=$(basename "$service") case "$name" in boot|done|functions|rc.*|sysctl|umount) continue ;; esac if [ -f "/etc/rc.d/S"*"$name" ]; then if "$service" running >/dev/null 2>&1; then running_count=$((running_count + 1)) else failed_count=$((failed_count + 1)) fi fi done json_add_object "services" json_add_int "running" "$running_count" json_add_int "failed" "$failed_count" json_close_object # Calculate overall health score local score=100 # CPU impact (max -30) if [ "$cpu_usage" -ge 95 ]; then score=$((score - 30)) elif [ "$cpu_usage" -ge 80 ]; then score=$((score - 15)) elif [ "$cpu_usage" -ge 60 ]; then score=$((score - 5)) fi # Memory impact (max -25) if [ "$mem_usage" -ge 95 ]; then score=$((score - 25)) elif [ "$mem_usage" -ge 80 ]; then score=$((score - 12)) elif [ "$mem_usage" -ge 60 ]; then score=$((score - 5)) fi # Disk impact (max -20) if [ "$disk_usage" -ge 95 ]; then score=$((score - 20)) elif [ "$disk_usage" -ge 80 ]; then score=$((score - 10)) fi # Temperature impact (max -15) if [ "$temp_value" -ge 85 ]; then score=$((score - 15)) elif [ "$temp_value" -ge 70 ]; then score=$((score - 7)) fi # Network impact (max -10) [ "$wan_up" -eq 0 ] && score=$((score - 10)) # Services impact (max -10) [ "$failed_count" -gt 0 ] && score=$((score - 10)) json_add_int "score" "$score" json_add_string "timestamp" "$(date '+%Y-%m-%d %H:%M:%S')" # Recommendations json_add_array "recommendations" [ "$cpu_usage" -ge 80 ] && json_add_string "" "CPU usage is high ($cpu_usage%). Consider closing unnecessary services." [ "$mem_usage" -ge 80 ] && json_add_string "" "Memory usage is high ($mem_usage%). Check for memory leaks." [ "$disk_usage" -ge 80 ] && json_add_string "" "Disk usage is high ($disk_usage%). Clean up old files or logs." [ "$temp_value" -ge 70 ] && json_add_string "" "Temperature is elevated (${temp_value}°C). Ensure proper ventilation." [ "$wan_up" -eq 0 ] && json_add_string "" "WAN connection is down. Check network connectivity." [ "$failed_count" -gt 0 ] && json_add_string "" "$failed_count service(s) enabled but not running. Check service status." json_close_array json_dump } # List all services with status list_services() { json_init json_add_array "services" for service in /etc/init.d/*; do [ -x "$service" ] || continue local name=$(basename "$service") # Skip special scripts case "$name" in boot|done|functions|rc.*|sysctl|umount) continue ;; esac # Check if enabled local enabled=0 [ -f "/etc/rc.d/S"*"$name" ] && enabled=1 # Check if running local running=0 "$service" running >/dev/null 2>&1 && running=1 json_add_object "" json_add_string "name" "$name" json_add_boolean "enabled" "$enabled" json_add_boolean "running" "$running" json_close_object done json_close_array json_dump } # Perform service action service_action() { read -r input json_load "$input" local service action json_get_var service service json_get_var action action json_cleanup if [ -z "$service" ] || [ -z "$action" ]; then json_init json_add_boolean "success" 0 json_add_string "message" "Service and action are required" json_dump return 1 fi local service_script="/etc/init.d/$service" if [ ! -x "$service_script" ]; then json_init json_add_boolean "success" 0 json_add_string "message" "Service not found: $service" json_dump return 1 fi local result=0 case "$action" in start|stop|restart) "$service_script" "$action" >/dev/null 2>&1 result=$? ;; enable) "$service_script" enable >/dev/null 2>&1 result=$? ;; disable) "$service_script" disable >/dev/null 2>&1 result=$? ;; *) json_init json_add_boolean "success" 0 json_add_string "message" "Invalid action: $action" json_dump return 1 ;; esac json_init if [ "$result" -eq 0 ]; then json_add_boolean "success" 1 json_add_string "message" "Service $service $action successful" else json_add_boolean "success" 0 json_add_string "message" "Service $service $action failed" fi json_dump } # Get system logs get_logs() { read -r input json_load "$input" local lines filter json_get_var lines lines "100" json_get_var filter filter "" json_cleanup # Get logs into a temporary file to avoid subshell issues local tmpfile="/tmp/syslog-$$" if [ -n "$filter" ]; then logread | tail -n "$lines" | grep -i "$filter" > "$tmpfile" else logread | tail -n "$lines" > "$tmpfile" fi json_init json_add_array "logs" # Read from file line by line while IFS= read -r line; do json_add_string "" "$line" done < "$tmpfile" json_close_array # Cleanup rm -f "$tmpfile" json_dump } # Create backup backup_config() { local backup_file="/tmp/backup-$(date +%Y%m%d-%H%M%S).tar.gz" # Create backup sysupgrade -b "$backup_file" >/dev/null 2>&1 if [ ! -f "$backup_file" ]; then json_init json_add_boolean "success" 0 json_add_string "message" "Backup creation failed" json_dump return 1 fi # Encode to base64 local backup_data=$(base64 < "$backup_file" 2>/dev/null) local backup_size=$(stat -c%s "$backup_file" 2>/dev/null || echo 0) # Cleanup rm -f "$backup_file" json_init json_add_boolean "success" 1 json_add_string "data" "$backup_data" json_add_int "size" "$backup_size" json_add_string "filename" "backup-$(date +%Y%m%d-%H%M%S).tar.gz" json_dump } # Restore configuration restore_config() { read -r input json_load "$input" local backup_data file_name json_get_var backup_data data json_get_var file_name file_name json_cleanup if [ -z "$backup_data" ]; then json_init json_add_boolean "success" 0 json_add_string "message" "No backup data provided" json_dump return 1 fi local clean_name="$(basename "${file_name:-backup.tar.gz}")" clean_name="$(echo "$clean_name" | tr -c 'A-Za-z0-9._-' '_')" [ -z "$clean_name" ] && clean_name="backup.tar.gz" local timestamp="$(date +%s)" local backup_file="/tmp/${timestamp}-${clean_name}" # Decode base64 if ! echo "$backup_data" | base64 -d > "$backup_file" 2>/dev/null; then rm -f "$backup_file" json_init json_add_boolean "success" 0 json_add_string "message" "Failed to decode backup data" json_dump return 1 fi if [ ! -s "$backup_file" ]; then rm -f "$backup_file" json_init json_add_boolean "success" 0 json_add_string "message" "Decoded backup archive is empty" json_dump return 1 fi # Restore sysupgrade -r "$backup_file" >/dev/null 2>&1 local result=$? # Cleanup rm -f "$backup_file" json_init if [ "$result" -eq 0 ]; then json_add_boolean "success" 1 json_add_string "message" "Configuration restored successfully. Reboot required." else json_add_boolean "success" 0 json_add_string "message" "Configuration restore failed" fi json_dump } # Reboot system reboot_system() { json_init json_add_boolean "success" 1 json_add_string "message" "System reboot initiated" json_dump # Reboot after 3 seconds ( sleep 3 && reboot ) & } # Get storage details get_storage() { json_init json_add_array "storage" df -h | tail -n +2 | while read filesystem size used avail percent mountpoint; do local percent_num=$(echo $percent | tr -d '%') json_add_object "" json_add_string "filesystem" "$filesystem" json_add_string "size" "$size" json_add_string "used" "$used" json_add_string "available" "$avail" json_add_int "percent" "$percent_num" json_add_string "mountpoint" "$mountpoint" json_close_object done json_close_array json_dump } # Collect diagnostic data into archive collect_diagnostics() { read input [ -n "$input" ] && json_load "$input" local include_logs include_config include_network anonymize json_get_var include_logs include_logs 2>/dev/null json_get_var include_config include_config 2>/dev/null json_get_var include_network include_network 2>/dev/null json_get_var anonymize anonymize 2>/dev/null [ -z "$include_logs" ] && include_logs=1 [ -z "$include_config" ] && include_config=1 [ -z "$include_network" ] && include_network=1 [ -z "$anonymize" ] && anonymize=0 local timestamp="$(date +%Y%m%d-%H%M%S)" local workdir="$DIAG_DIR/work-$timestamp" mkdir -p "$workdir" # System info { echo "=== System Information ===" uname -a echo echo "--- CPU ---" cat /proc/cpuinfo echo echo "--- Memory ---" cat /proc/meminfo echo echo "--- Disk ---" df -h echo echo "--- Uptime ---" uptime } >"$workdir/sysinfo.txt" # Logs if [ "$include_logs" = "1" ]; then logread 2>/dev/null >"$workdir/system.log" || true dmesg 2>/dev/null >"$workdir/kernel.log" || true fi # Configs if [ "$include_config" = "1" ]; then mkdir -p "$workdir/config" sysupgrade -b "$workdir/config/backup.tar.gz" >/dev/null 2>&1 || true for conf in /etc/config/*; do [ -f "$conf" ] || continue local dest="$workdir/config/$(basename "$conf")" if [ "$anonymize" = "1" ]; then grep -viE "(password|passwd|secret|key|token|psk|ipaddr|macaddr)" "$conf" >"$dest" 2>/dev/null || cp "$conf" "$dest" else cp "$conf" "$dest" fi done fi # Network info if [ "$include_network" = "1" ]; then { echo "=== Interfaces ===" ip addr echo echo "=== Routes ===" ip route echo echo "=== Firewall ===" iptables -L -n -v 2>/dev/null || true echo echo "=== DNS ===" cat /etc/resolv.conf 2>/dev/null echo echo "=== Connectivity (8.8.8.8) ===" ping -c 3 -W 2 8.8.8.8 2>&1 echo echo "=== Connectivity (1.1.1.1) ===" ping -c 3 -W 2 1.1.1.1 2>&1 } >"$workdir/network.txt" fi local archive_name="diagnostics-$(hostname)-$timestamp.tar.gz" local archive_path="$DIAG_DIR/$archive_name" tar -czf "$archive_path" -C "$workdir" . >/dev/null 2>&1 || { rm -rf "$workdir" json_init json_add_boolean "success" 0 json_add_string "error" "archive_failed" json_dump return } rm -rf "$workdir" local size=$(stat -c%s "$archive_path" 2>/dev/null || stat -f%z "$archive_path") local created=$(date -r "$archive_path" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S') json_init json_add_boolean "success" 1 json_add_string "file" "$archive_name" json_add_int "size" "$size" json_add_string "created_at" "$created" json_dump } list_diagnostics() { json_init json_add_array "archives" for file in $(ls -t "$DIAG_DIR"/diagnostics-*.tar.gz 2>/dev/null | head -n 50); do [ -f "$file" ] || continue local size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file") local created=$(date -r "$file" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "") json_add_object json_add_string "name" "$(basename "$file")" json_add_int "size" "$size" json_add_string "created_at" "$created" json_close_object done json_close_array json_add_boolean "success" 1 json_dump } download_diagnostic() { read input json_load "$input" json_get_var name name local safe="$(safe_filename "$name")" local file="$DIAG_DIR/$safe" json_init if [ -z "$safe" ] || [ ! -f "$file" ]; then json_add_boolean "success" 0 json_add_string "error" "not_found" json_dump return fi local data=$(base64 "$file" 2>/dev/null) json_add_boolean "success" 1 json_add_string "name" "$safe" json_add_string "data" "$data" json_dump } delete_diagnostic() { read input json_load "$input" json_get_var name name local safe="$(safe_filename "$name")" local file="$DIAG_DIR/$safe" json_init if [ -n "$safe" ] && [ -f "$file" ]; then rm -f "$file" json_add_boolean "success" 1 else json_add_boolean "success" 0 json_add_string "error" "not_found" fi json_dump } run_diagnostic_test() { read input json_load "$input" json_get_var test test local output="" local success=1 case "$test" in connectivity) output="$(ping -c 3 -W 2 8.8.8.8 2>&1; echo; ping -c 3 -W 2 1.1.1.1 2>&1)" ;; dns) if command -v nslookup >/dev/null 2>&1; then output="$(nslookup openwrt.org 2>&1)" else output="nslookup unavailable" success=0 fi ;; latency) output="$(ping -c 5 -W 2 google.com 2>&1)" ;; disk) local testfile="/tmp/system-hub-disk-test" if dd if=/dev/zero of="$testfile" bs=1M count=4 conv=fsync 2>&1; then sync rm -f "$testfile" output="Disk write test completed (4MB)." else output="Disk test failed." success=0 fi ;; firewall) output="$(iptables -L -n -v 2>&1)" ;; wifi) if command -v iwinfo >/dev/null 2>&1; then output="$(iwinfo 2>&1)" else output="iwinfo not available" success=0 fi ;; *) output="unknown test" success=0 ;; esac json_init json_add_boolean "success" "$success" json_add_string "test" "${test:-unknown}" json_add_string "output" "$output" json_dump } remote_status() { local section="system-hub.remote" local enabled=$(uci -q get $section.rustdesk_enabled || echo 0) local relay_server=$(uci -q get $section.rustdesk_server || echo "") local relay_key=$(uci -q get $section.rustdesk_key || echo "") local stored_id=$(uci -q get $section.rustdesk_id || echo "") local stored_password=$(uci -q get $section.rustdesk_password || echo "") local allow_unattended=$(uci -q get $section.allow_unattended || echo 0) local require_approval=$(uci -q get $section.require_approval || echo 1) local notify_on_connect=$(uci -q get $section.notify_on_connect || echo 1) local installed=0 command -v rustdesk >/dev/null 2>&1 && installed=1 local running=0 if [ -x /etc/init.d/rustdesk ]; then /etc/init.d/rustdesk status >/dev/null 2>&1 && running=1 fi json_init json_add_boolean "installed" "$installed" json_add_boolean "running" "$running" json_add_boolean "enabled" "$enabled" json_add_string "server" "$relay_server" json_add_string "key" "$relay_key" json_add_string "id" "$stored_id" json_add_string "password" "$stored_password" json_add_boolean "allow_unattended" "$allow_unattended" json_add_boolean "require_approval" "$require_approval" json_add_boolean "notify_on_connect" "$notify_on_connect" json_dump } remote_install() { json_init if command -v rustdesk >/dev/null 2>&1; then json_add_boolean "success" 0 json_add_string "error" "already_installed" json_dump return fi if ! command -v opkg >/dev/null 2>&1; then json_add_boolean "success" 0 json_add_string "error" "opkg_missing" json_dump return fi opkg update >/tmp/rustdesk-install.log 2>&1 if opkg install rustdesk >>/tmp/rustdesk-install.log 2>&1; then json_add_boolean "success" 1 json_add_string "message" "RustDesk installed" else local err="$(tail -n 20 /tmp/rustdesk-install.log 2>/dev/null)" json_add_boolean "success" 0 json_add_string "error" "${err:-install_failed}" fi json_dump } remote_configure() { read input json_load "$input" local section="system-hub.remote" local server key enabled json_get_var server relay_server json_get_var key relay_key json_get_var enabled rustdesk_enabled [ -n "$server" ] && uci set $section.rustdesk_server="$server" [ -n "$key" ] && uci set $section.rustdesk_key="$key" [ -n "$enabled" ] && uci set $section.rustdesk_enabled="$enabled" uci commit system-hub if [ -n "$server" ] || [ -n "$key" ]; then mkdir -p /etc/rustdesk cat > /etc/rustdesk/config.toml </dev/null 2>&1 || true /etc/init.d/rustdesk restart >/dev/null 2>&1 || true elif [ -x /etc/init.d/rustdesk ]; then /etc/init.d/rustdesk stop >/dev/null 2>&1 || true /etc/init.d/rustdesk disable >/dev/null 2>&1 || true fi json_init json_add_boolean "success" 1 json_dump } remote_get_credentials() { local section="system-hub.remote" local rid="" rpass="" if command -v rustdesk >/dev/null 2>&1; then rid=$(rustdesk --get-id 2>/dev/null || echo "") rpass=$(rustdesk --password 2>/dev/null || echo "") fi [ -z "$rid" ] && rid=$(uci -q get $section.rustdesk_id || echo "") [ -z "$rpass" ] && rpass=$(uci -q get $section.rustdesk_password || echo "") json_init json_add_boolean "success" 1 json_add_string "id" "$rid" json_add_string "password" "$rpass" json_dump } remote_service_action() { read input json_load "$input" json_get_var action action json_init if [ ! -x /etc/init.d/rustdesk ]; then json_add_boolean "success" 0 json_add_string "error" "service_missing" json_dump return fi case "$action" in start|stop|restart|enable|disable) if /etc/init.d/rustdesk "$action" >/dev/null 2>&1; then json_add_boolean "success" 1 json_add_string "message" "$action" else json_add_boolean "success" 0 json_add_string "error" "action_failed" fi ;; *) json_add_boolean "success" 0 json_add_string "error" "invalid_action" ;; esac json_dump } remote_save_settings() { read input json_load "$input" local section="system-hub.remote" local allow require notify json_get_var allow allow_unattended json_get_var require require_approval json_get_var notify notify_on_connect [ -n "$allow" ] && uci set $section.allow_unattended="$allow" [ -n "$require" ] && uci set $section.require_approval="$require" [ -n "$notify" ] && uci set $section.notify_on_connect="$notify" uci commit system-hub json_init json_add_boolean "success" 1 json_dump } upload_diagnostics() { read input json_load "$input" json_get_var name name local upload_url=$(uci -q get system-hub.diagnostics.upload_url) local upload_token=$(uci -q get system-hub.diagnostics.upload_token) json_init if [ -z "$upload_url" ]; then json_add_boolean "success" 0 json_add_string "error" "upload_url_missing" json_dump return fi if ! command -v curl >/dev/null 2>&1; then json_add_boolean "success" 0 json_add_string "error" "curl_missing" json_dump return fi local safe="$(safe_filename "$name")" if [ -z "$safe" ]; then # Use latest archive if not specified safe="$(ls -t "$DIAG_DIR"/diagnostics-*.tar.gz 2>/dev/null | head -n1)" [ -n "$safe" ] && safe="$(basename "$safe")" fi local file="$DIAG_DIR/$safe" if [ -z "$safe" ] || [ ! -f "$file" ]; then json_add_boolean "success" 0 json_add_string "error" "archive_not_found" json_dump return fi local response if [ -n "$upload_token" ]; then response=$(curl -s -S -o /tmp/system-hub-upload.log -w "%{http_code}" \ -H "Authorization: Bearer $upload_token" \ -F "file=@$file" \ -F "hostname=$(hostname)" \ -F "timestamp=$(date +%s)" \ "$upload_url" 2>&1) else response=$(curl -s -S -o /tmp/system-hub-upload.log -w "%{http_code}" \ -F "file=@$file" \ -F "hostname=$(hostname)" \ -F "timestamp=$(date +%s)" \ "$upload_url" 2>&1) fi local curl_exit=$? local body="$(cat /tmp/system-hub-upload.log 2>/dev/null || true)" rm -f /tmp/system-hub-upload.log if [ $curl_exit -eq 0 ]; then json_add_boolean "success" 1 json_add_string "status" "$response" json_add_string "body" "$body" else json_add_boolean "success" 0 json_add_string "error" "upload_failed" json_add_string "details" "$body" fi json_dump } # Get settings get_settings() { json_init # Load UCI config if it exists local config_loaded=0 if [ -f "/etc/config/system-hub" ]; then config_load system-hub config_loaded=1 fi # General settings json_add_object "general" config_get auto_refresh general auto_refresh "1" config_get health_check general health_check "1" config_get debug_mode general debug_mode "0" config_get refresh_interval general refresh_interval "30" config_get log_retention general log_retention "30" json_add_boolean "auto_refresh" "${auto_refresh:-1}" json_add_boolean "health_check" "${health_check:-1}" json_add_boolean "debug_mode" "${debug_mode:-0}" json_add_int "refresh_interval" "${refresh_interval:-30}" json_add_int "log_retention" "${log_retention:-30}" json_close_object # Alert thresholds json_add_object "thresholds" config_get cpu_warning thresholds cpu_warning "80" config_get cpu_critical thresholds cpu_critical "95" config_get mem_warning thresholds mem_warning "80" config_get mem_critical thresholds mem_critical "95" config_get disk_warning thresholds disk_warning "80" config_get disk_critical thresholds disk_critical "95" config_get temp_warning thresholds temp_warning "70" config_get temp_critical thresholds temp_critical "85" json_add_int "cpu_warning" "${cpu_warning:-80}" json_add_int "cpu_critical" "${cpu_critical:-95}" json_add_int "mem_warning" "${mem_warning:-80}" json_add_int "mem_critical" "${mem_critical:-95}" json_add_int "disk_warning" "${disk_warning:-80}" json_add_int "disk_critical" "${disk_critical:-95}" json_add_int "temp_warning" "${temp_warning:-70}" json_add_int "temp_critical" "${temp_critical:-85}" json_close_object # Scheduled tasks json_add_object "schedules" config_get health_report schedules health_report "1" config_get backup_weekly schedules backup_weekly "1" config_get log_cleanup schedules log_cleanup "1" json_add_boolean "health_report" "${health_report:-1}" json_add_boolean "backup_weekly" "${backup_weekly:-1}" json_add_boolean "log_cleanup" "${log_cleanup:-1}" json_close_object # Upload settings json_add_object "upload" config_get auto_upload upload auto_upload "0" config_get upload_url upload url "" config_get upload_token upload token "" json_add_boolean "auto_upload" "${auto_upload:-0}" json_add_string "url" "${upload_url:-}" json_add_string "token" "${upload_token:-}" json_close_object # Support info json_add_object "support" config_get support_provider support provider "CyberMind.fr" config_get support_email support email "support@cybermind.fr" config_get support_docs support docs "https://docs.cybermind.fr" json_add_string "provider" "${support_provider:-CyberMind.fr}" json_add_string "email" "${support_email:-support@cybermind.fr}" json_add_string "docs" "${support_docs:-https://docs.cybermind.fr}" json_close_object json_dump } # Save settings save_settings() { read -r input json_load "$input" # Parse settings from input local section key value # Create UCI config if it doesn't exist if [ ! -f "/etc/config/system-hub" ]; then touch /etc/config/system-hub uci set system-hub.general=settings uci set system-hub.thresholds=thresholds uci set system-hub.schedules=schedules uci set system-hub.upload=upload uci set system-hub.support=support fi # This is a simplified version - in production you'd parse the JSON properly # For now, we'll extract specific values json_get_var auto_refresh auto_refresh json_get_var health_check health_check json_get_var debug_mode debug_mode json_get_var refresh_interval refresh_interval json_get_var log_retention log_retention json_get_var cpu_warning cpu_warning json_get_var cpu_critical cpu_critical json_get_var mem_warning mem_warning json_get_var mem_critical mem_critical json_get_var disk_warning disk_warning json_get_var disk_critical disk_critical json_get_var temp_warning temp_warning json_get_var temp_critical temp_critical json_cleanup # Save to UCI [ -n "$auto_refresh" ] && uci set system-hub.general.auto_refresh="$auto_refresh" [ -n "$health_check" ] && uci set system-hub.general.health_check="$health_check" [ -n "$debug_mode" ] && uci set system-hub.general.debug_mode="$debug_mode" [ -n "$refresh_interval" ] && uci set system-hub.general.refresh_interval="$refresh_interval" [ -n "$log_retention" ] && uci set system-hub.general.log_retention="$log_retention" [ -n "$cpu_warning" ] && uci set system-hub.thresholds.cpu_warning="$cpu_warning" [ -n "$cpu_critical" ] && uci set system-hub.thresholds.cpu_critical="$cpu_critical" [ -n "$mem_warning" ] && uci set system-hub.thresholds.mem_warning="$mem_warning" [ -n "$mem_critical" ] && uci set system-hub.thresholds.mem_critical="$mem_critical" [ -n "$disk_warning" ] && uci set system-hub.thresholds.disk_warning="$disk_warning" [ -n "$disk_critical" ] && uci set system-hub.thresholds.disk_critical="$disk_critical" [ -n "$temp_warning" ] && uci set system-hub.thresholds.temp_warning="$temp_warning" [ -n "$temp_critical" ] && uci set system-hub.thresholds.temp_critical="$temp_critical" uci commit system-hub json_init json_add_boolean "success" 1 json_add_string "message" "Settings saved successfully" json_dump } # Get components (leverages secubox module detection) get_components() { # Call secubox backend to get modules, which are the system components local result=$(ubus call luci.secubox modules 2>/dev/null) if [ -n "$result" ]; then # Pass through the secubox modules as components echo "$result" else # Fallback if secubox is not available json_init json_add_array "modules" json_close_array json_dump fi } # Get components by category get_components_by_category() { local input read -r input json_load "$input" local category json_get_var category category json_cleanup # Call secubox backend with category filter local result=$(ubus call luci.secubox modules_by_category "{\"category\":\"$category\"}" 2>/dev/null) if [ -n "$result" ]; then echo "$result" else # Fallback json_init json_add_array "modules" json_close_array json_dump fi } # Main dispatcher case "$1" in list) cat << 'EOF' { "status": {}, "get_system_info": {}, "get_health": {}, "get_components": {}, "get_components_by_category": { "category": "string" }, "list_services": {}, "service_action": { "service": "string", "action": "string" }, "get_logs": { "lines": 100, "filter": "" }, "backup_config": {}, "restore_config": { "file_name": "string", "data": "string" }, "reboot": {}, "get_storage": {}, "get_settings": {}, "save_settings": { "auto_refresh": 1, "health_check": 1, "debug_mode": 0, "refresh_interval": 30, "log_retention": 30, "cpu_warning": 80, "cpu_critical": 95, "mem_warning": 80, "mem_critical": 95, "disk_warning": 80, "disk_critical": 95, "temp_warning": 70, "temp_critical": 85 }, "collect_diagnostics": { "include_logs": 1, "include_config": 1, "include_network": 1, "anonymize": 1 }, "list_diagnostics": {}, "download_diagnostic": { "name": "string" }, "delete_diagnostic": { "name": "string" }, "run_diagnostic_test": { "test": "string" }, "upload_diagnostics": { "name": "string" }, "remote_status": {}, "remote_install": {}, "remote_configure": { "relay_server": "string", "relay_key": "string", "rustdesk_enabled": 1 }, "remote_get_credentials": {}, "remote_service_action": { "action": "start|stop|restart|enable|disable" }, "remote_save_settings": { "allow_unattended": 0, "require_approval": 1, "notify_on_connect": 1 } } EOF ;; call) case "$2" in status) status ;; get_system_info) get_system_info ;; get_health) get_health ;; get_components) get_components ;; get_components_by_category) get_components_by_category ;; list_services) list_services ;; service_action) service_action ;; get_logs) get_logs ;; backup_config) backup_config ;; restore_config) restore_config ;; reboot) reboot_system ;; get_storage) get_storage ;; get_settings) get_settings ;; save_settings) save_settings ;; collect_diagnostics) collect_diagnostics ;; list_diagnostics) list_diagnostics ;; download_diagnostic) download_diagnostic ;; delete_diagnostic) delete_diagnostic ;; run_diagnostic_test) run_diagnostic_test ;; upload_diagnostics) upload_diagnostics ;; remote_status) remote_status ;; remote_install) remote_install ;; remote_configure) remote_configure ;; remote_get_credentials) remote_get_credentials ;; remote_service_action) remote_service_action ;; remote_save_settings) remote_save_settings ;; *) json_init json_add_boolean "success" 0 json_add_string "error" "Unknown method: $2" json_dump ;; esac ;; esac