Implements comprehensive system control and monitoring dashboard with health metrics, service management, system logs, and backup/restore functionality. Features: - Real-time system monitoring with visual gauges (CPU, RAM, Disk) - Comprehensive system information (hostname, model, uptime, kernel) - Health metrics with temperature monitoring and storage breakdown - Service management with start/stop/restart/enable/disable actions - System log viewer with filtering and configurable line count - Configuration backup creation and download (base64 encoded) - Configuration restore from backup file - System reboot functionality with confirmation Components: - RPCD backend (luci.system-hub): 10 ubus methods * status, get_system_info, get_health * list_services, service_action * get_logs, backup_config, restore_config * reboot, get_storage - 4 JavaScript views: overview, services, logs, backup - ACL with read/write permissions segregation - Comprehensive README with API documentation Technical implementation: - System info from /proc filesystem and sysinfo - Health metrics: CPU load, memory breakdown, disk usage, temperature - Service control via /etc/init.d scripts - Log retrieval via logread with filtering - Backup/restore using sysupgrade with base64 encoding - Visual gauges with SVG circular progress indicators - Color-coded health status (green/orange/red) Dashboard Features: - Circular gauges for CPU, Memory, Disk (120px with 10px stroke) - System information cards with detailed metrics - Temperature monitoring with thermal zone detection - Storage table for all mount points with progress bars - Service table with inline action buttons - Terminal-style log display (black bg, green text) - File upload for backup restore - Modal confirmations for destructive actions Architecture follows SecuBox standards: - RPCD naming convention (luci. prefix) - Menu paths match view file structure - All JavaScript in strict mode - Form-based configuration management - Comprehensive error handling Dependencies: coreutils, coreutils-base64 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
450 lines
11 KiB
Bash
Executable File
450 lines
11 KiB
Bash
Executable File
#!/bin/sh
|
|
# System Hub RPCD Backend
|
|
# Central system control and monitoring
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# 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 "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 "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 info
|
|
local cpu_model=$(awk -F': ' '/model name|Processor/ {print $2; exit}' /proc/cpuinfo 2>/dev/null || echo "Unknown")
|
|
local cpu_cores=$(grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo 1)
|
|
|
|
json_add_object "cpu"
|
|
json_add_string "model" "$cpu_model"
|
|
json_add_int "cores" "$cpu_cores"
|
|
json_close_object
|
|
|
|
# Load average
|
|
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}')
|
|
|
|
json_add_object "load"
|
|
json_add_string "1min" "$load1"
|
|
json_add_string "5min" "$load5"
|
|
json_add_string "15min" "$load15"
|
|
json_close_object
|
|
|
|
# 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 $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))
|
|
|
|
local mem_percent=0
|
|
if [ "$mem_total" -gt 0 ]; then
|
|
mem_percent=$(( (mem_used * 100) / mem_total ))
|
|
fi
|
|
|
|
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 "percent" "$mem_percent"
|
|
json_close_object
|
|
|
|
# Storage
|
|
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
|
|
|
|
# Temperature
|
|
json_add_array "temperatures"
|
|
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))
|
|
local zone_name=$(basename $(dirname "$zone"))
|
|
|
|
json_add_object ""
|
|
json_add_string "zone" "$zone_name"
|
|
json_add_int "celsius" "$temp_c"
|
|
json_close_object
|
|
fi
|
|
done
|
|
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
|
|
|
|
json_init
|
|
json_add_array "logs"
|
|
|
|
if [ -n "$filter" ]; then
|
|
logread | tail -n "$lines" | grep -i "$filter" | while IFS= read -r line; do
|
|
json_add_string "" "$line"
|
|
done
|
|
else
|
|
logread | tail -n "$lines" | while IFS= read -r line; do
|
|
json_add_string "" "$line"
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
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
|
|
json_get_var backup_data data
|
|
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 backup_file="/tmp/restore-$(date +%s).tar.gz"
|
|
|
|
# Decode base64
|
|
echo "$backup_data" | base64 -d > "$backup_file" 2>/dev/null
|
|
|
|
if [ ! -f "$backup_file" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Failed to decode backup data"
|
|
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
|
|
}
|
|
|
|
# Main dispatcher
|
|
case "$1" in
|
|
list)
|
|
cat << 'EOF'
|
|
{
|
|
"status": {},
|
|
"get_system_info": {},
|
|
"get_health": {},
|
|
"list_services": {},
|
|
"service_action": { "service": "string", "action": "string" },
|
|
"get_logs": { "lines": 100, "filter": "" },
|
|
"backup_config": {},
|
|
"restore_config": { "data": "string" },
|
|
"reboot": {},
|
|
"get_storage": {}
|
|
}
|
|
EOF
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status) status ;;
|
|
get_system_info) get_system_info ;;
|
|
get_health) get_health ;;
|
|
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 ;;
|
|
*)
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Unknown method: $2"
|
|
json_dump
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|