1763 lines
48 KiB
Bash
Executable File
1763 lines
48 KiB
Bash
Executable File
#!/bin/sh
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# SecuBox Master RPCD Backend
|
|
# Module discovery and unified status
|
|
# Copyright (C) 2025 CyberMind.fr
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
get_pkg_version() {
|
|
local version=""
|
|
|
|
# OpenWrt 25.12+ uses apk packages
|
|
if command -v apk >/dev/null 2>&1; then
|
|
local apk_line=$(apk info -v luci-app-secubox 2>/dev/null | grep -E '^luci-app-secubox-[0-9]' | head -n1)
|
|
if [ -n "$apk_line" ]; then
|
|
version=$(echo "$apk_line" | sed 's/^luci-app-secubox-//' | awk '{print $1}' | sed 's/-r[0-9]*$//')
|
|
else
|
|
version=$(apk info luci-app-secubox 2>/dev/null | awk -F': ' '/^Version/ {print $2; exit}')
|
|
fi
|
|
if [ -n "$version" ]; then
|
|
echo "$version"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# Legacy opkg metadata
|
|
local ctrl="/usr/lib/opkg/info/luci-app-secubox.control"
|
|
if [ -f "$ctrl" ]; then
|
|
version=$(awk -F': ' '/^Version/ { print $2; exit }' "$ctrl")
|
|
if [ -n "$version" ]; then
|
|
echo "$version"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
if command -v opkg >/dev/null 2>&1; then
|
|
version=$(opkg list-installed luci-app-secubox 2>/dev/null | awk '{print $3}' | sed 's/-r[0-9]*$//' | head -n1)
|
|
if [ -n "$version" ]; then
|
|
echo "$version"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
local cfg_version="$(uci -q get secubox.main.version)"
|
|
if [ -n "$cfg_version" ]; then
|
|
echo "$cfg_version"
|
|
elif [ -f "/usr/share/secubox/VERSION" ]; then
|
|
head -n1 /usr/share/secubox/VERSION
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
PKG_VERSION="$(get_pkg_version)"
|
|
|
|
PLUGIN_DIR="/usr/share/secubox/plugins"
|
|
PROFILE_DIR="/usr/share/secubox/profiles"
|
|
PROFILE_BACKUP_DIR="/etc/secubox-profiles/backups"
|
|
DEFAULT_STORAGE_PATH="/srv/secubox"
|
|
SECOBOX_APP="/usr/sbin/secubox-app"
|
|
OPKG_UPDATED=0
|
|
|
|
manifest_files() {
|
|
if [ -d "$PLUGIN_DIR/catalog" ]; then
|
|
for file in "$PLUGIN_DIR"/catalog/*.json; do
|
|
[ -f "$file" ] || continue
|
|
echo "$file"
|
|
done
|
|
fi
|
|
for file in "$PLUGIN_DIR"/*/manifest.json; do
|
|
[ -f "$file" ] || continue
|
|
echo "$file"
|
|
done
|
|
}
|
|
|
|
manifest_file_for_id() {
|
|
local id="$1"
|
|
local catalog="$PLUGIN_DIR/catalog/$id.json"
|
|
local legacy="$PLUGIN_DIR/$id/manifest.json"
|
|
if [ -f "$catalog" ]; then
|
|
printf '%s' "$catalog"
|
|
return 0
|
|
fi
|
|
if [ -f "$legacy" ]; then
|
|
printf '%s' "$legacy"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
validate_manifest_json() {
|
|
local manifest="$1"
|
|
local id name runtime packages category
|
|
id=$(jsonfilter -s "$manifest" -e '@.id' 2>/dev/null)
|
|
name=$(jsonfilter -s "$manifest" -e '@.name' 2>/dev/null)
|
|
category=$(jsonfilter -s "$manifest" -e '@.category' 2>/dev/null)
|
|
runtime=$(jsonfilter -s "$manifest" -e '@.runtime' 2>/dev/null)
|
|
[ -n "$runtime" ] || runtime=$(jsonfilter -s "$manifest" -e '@.type' 2>/dev/null)
|
|
packages=$(jsonfilter -s "$manifest" -e '@.packages[*]' 2>/dev/null)
|
|
[ -n "$id" ] && [ -n "$name" ] && [ -n "$runtime" ] && [ -n "$category" ] && [ -n "$packages" ]
|
|
}
|
|
|
|
# Module registry - auto-detected from /usr/libexec/rpcd/
|
|
detect_modules() {
|
|
local modules=""
|
|
local scripts="luci.crowdsec-dashboard luci.netdata-dashboard luci.netifyd-dashboard luci.wireguard-dashboard luci.network-modes luci.client-guardian luci.system-hub luci.bandwidth-manager luci.auth-guardian luci.media-flow luci.vhost-manager luci.cdn-cache luci.traffic-shaper luci.ksm-manager"
|
|
|
|
for script in $scripts; do
|
|
if [ -x "/usr/libexec/rpcd/$script" ]; then
|
|
# Remove luci. prefix, remove -dashboard suffix, convert dashes to underscores
|
|
local module_id=$(echo "$script" | sed 's/^luci\.//' | sed 's/-dashboard$//' | sed 's/-/_/g')
|
|
modules="$modules $module_id"
|
|
fi
|
|
done
|
|
|
|
echo "$modules"
|
|
}
|
|
|
|
MODULES=$(detect_modules)
|
|
|
|
is_root_password_set() {
|
|
local hash
|
|
hash=$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)
|
|
[ -n "$hash" ] && [ "$hash" != "!" ] && [ "$hash" != "*" ]
|
|
}
|
|
|
|
get_storage_path() {
|
|
local path
|
|
path=$(uci -q get secubox.main.storage_path)
|
|
if [ -z "$path" ]; then
|
|
path="$DEFAULT_STORAGE_PATH"
|
|
fi
|
|
echo "$path"
|
|
}
|
|
|
|
package_installed() {
|
|
local pkg="$1"
|
|
if command -v opkg >/dev/null 2>&1; then
|
|
opkg status "$pkg" >/dev/null 2>&1 && return 0
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
apk info -e "$pkg" >/dev/null 2>&1 && return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
packages_state() {
|
|
local manifest="$1"
|
|
local total=0 installed=0 missing=0 pkg
|
|
for pkg in $(jsonfilter -s "$manifest" -e '@.packages[*]' 2>/dev/null); do
|
|
total=$((total + 1))
|
|
if package_installed "$pkg"; then
|
|
installed=$((installed + 1))
|
|
else
|
|
missing=$((missing + 1))
|
|
fi
|
|
done
|
|
if [ "$total" -eq 0 ]; then
|
|
echo "n/a"
|
|
elif [ "$missing" -eq 0 ]; then
|
|
echo "installed"
|
|
elif [ "$installed" -eq 0 ]; then
|
|
echo "missing"
|
|
else
|
|
echo "partial"
|
|
fi
|
|
}
|
|
|
|
ensure_directory() {
|
|
local dir="$1"
|
|
[ -d "$dir" ] || mkdir -p "$dir"
|
|
}
|
|
|
|
apply_network_mode() {
|
|
local mode="$1"
|
|
[ -n "$mode" ] || return
|
|
if command -v ubus >/dev/null 2>&1; then
|
|
if ubus call luci.network-modes set_mode "{\"mode\":\"$mode\"}" >/dev/null 2>&1; then
|
|
ubus call luci.network-modes apply_mode >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
ensure_opkg_updated() {
|
|
[ "$OPKG_UPDATED" -eq 1 ] && return
|
|
if command -v opkg >/dev/null 2>&1; then
|
|
opkg update >/dev/null 2>&1 && OPKG_UPDATED=1
|
|
fi
|
|
}
|
|
|
|
install_package_if_missing() {
|
|
local pkg="$1"
|
|
package_installed "$pkg" && return
|
|
if command -v opkg >/dev/null 2>&1; then
|
|
ensure_opkg_updated
|
|
opkg install "$pkg" >/dev/null 2>&1
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
apk add "$pkg" >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
# Check if a module is installed (supports both opkg and apk)
|
|
check_module_installed() {
|
|
local module="$1"
|
|
local package config
|
|
|
|
config_get package "$module" package ""
|
|
config_get config "$module" config ""
|
|
|
|
# Check if package is installed via apk (OpenWrt 25.12+) or opkg (24.10 and earlier)
|
|
if [ -x "/usr/bin/apk" ]; then
|
|
# OpenWrt 25.12+ uses apk
|
|
if apk list --installed 2>/dev/null | grep -q "^${package}-"; then
|
|
echo "1"
|
|
return
|
|
fi
|
|
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
|
|
# OpenWrt 24.10 and earlier uses opkg
|
|
if opkg list-installed 2>/dev/null | grep -q "^${package} "; then
|
|
echo "1"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# Fallback: check if config file exists
|
|
if [ -f "/etc/config/${config}" ]; then
|
|
echo "1"
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# Check if a module is enabled (UCI config)
|
|
check_module_enabled() {
|
|
local module="$1"
|
|
local enabled
|
|
|
|
config_load secubox
|
|
config_get enabled "$module" enabled "1"
|
|
|
|
echo "$enabled"
|
|
}
|
|
|
|
# Check if a module service is running
|
|
check_module_running() {
|
|
local module="$1"
|
|
local config
|
|
|
|
config_load secubox
|
|
config_get config "$module" config ""
|
|
|
|
case "$module" in
|
|
crowdsec)
|
|
pgrep -f crowdsec > /dev/null 2>&1 && echo "1" || echo "0"
|
|
;;
|
|
netdata)
|
|
pgrep -f netdata > /dev/null 2>&1 && echo "1" || echo "0"
|
|
;;
|
|
netifyd)
|
|
pgrep -f netifyd > /dev/null 2>&1 && echo "1" || echo "0"
|
|
;;
|
|
wireguard)
|
|
[ -n "$(wg show 2>/dev/null)" ] && echo "1" || echo "0"
|
|
;;
|
|
cdn_cache)
|
|
pgrep -f "nginx.*cdn-cache" > /dev/null 2>&1 && echo "1" || echo "0"
|
|
;;
|
|
*)
|
|
# Generic check via init.d
|
|
if [ -x "/etc/init.d/${config}" ]; then
|
|
/etc/init.d/${config} running > /dev/null 2>&1 && echo "1" || echo "0"
|
|
else
|
|
echo "0"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Determine module status based on enabled + running
|
|
get_module_status() {
|
|
local enabled="$1"
|
|
local running="$2"
|
|
|
|
if [ "$enabled" = "1" ] && [ "$running" = "1" ]; then
|
|
echo "active"
|
|
elif [ "$enabled" = "1" ] && [ "$running" = "0" ]; then
|
|
echo "error"
|
|
elif [ "$enabled" = "0" ] && [ "$running" = "0" ]; then
|
|
echo "disabled"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
# Get overall system status
|
|
get_status() {
|
|
local total=0
|
|
local installed=0
|
|
local running=0
|
|
|
|
json_init
|
|
json_add_string "version" "$PKG_VERSION"
|
|
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'SecuBox')"
|
|
json_add_boolean "enabled" "$(uci -q get secubox.main.enabled || echo 1)"
|
|
|
|
# System info
|
|
local uptime=$(cat /proc/uptime | cut -d. -f1)
|
|
local load=$(cat /proc/loadavg | cut -d' ' -f1-3)
|
|
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
|
|
local mem_used=$((mem_total - mem_free))
|
|
local mem_pct=$((mem_used * 100 / mem_total))
|
|
|
|
json_add_int "uptime" "$uptime"
|
|
json_add_string "load" "$load"
|
|
json_add_int "memory_total" "$mem_total"
|
|
json_add_int "memory_used" "$mem_used"
|
|
json_add_int "memory_percent" "$mem_pct"
|
|
|
|
# Count modules from UCI config (same as get_modules)
|
|
config_load secubox 2>/dev/null || true
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
for module in $module_sections; do
|
|
total=$((total + 1))
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_running=$(check_module_running "$module")
|
|
|
|
if [ "$is_installed" = "1" ]; then
|
|
installed=$((installed + 1))
|
|
fi
|
|
if [ "$is_running" = "1" ]; then
|
|
running=$((running + 1))
|
|
fi
|
|
done
|
|
|
|
json_add_int "modules_total" "$total"
|
|
json_add_int "modules_installed" "$installed"
|
|
json_add_int "modules_running" "$running"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Detect real installed luci-app packages (supports both opkg and apk)
|
|
detect_real_modules() {
|
|
if [ -x "/usr/bin/apk" ]; then
|
|
# OpenWrt 25.12+ uses apk
|
|
apk list --installed 2>/dev/null | grep "^luci-app-" | grep -v "^luci-app-secubox-" | while IFS=- read -r pkg rest; do
|
|
# Parse apk output: luci-app-network-modes-0.3.1-r1 noarch {...} (license) [installed]
|
|
local package=$(echo "$pkg-$rest" | awk '{print $1}')
|
|
local version=$(echo "$package" | sed 's/^luci-app-[^-]*-//' | sed 's/-r[0-9]*$//')
|
|
local pkg_name=$(echo "$package" | sed 's/-[0-9].*//')
|
|
local module_id=$(echo "$pkg_name" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/_/g')
|
|
echo "$module_id:$pkg_name:$version"
|
|
done
|
|
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
|
|
# OpenWrt 24.10 and earlier uses opkg
|
|
opkg list-installed 2>/dev/null | grep "^luci-app-" | grep -v "^luci-app-secubox " | while read package version; do
|
|
# Extract module ID from package name
|
|
local module_id=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/_/g')
|
|
echo "$module_id:$package:$version"
|
|
done
|
|
fi
|
|
}
|
|
|
|
# Get detailed modules list (UCI + auto-detected)
|
|
get_modules() {
|
|
json_init
|
|
json_add_array "modules"
|
|
|
|
config_load secubox 2>/dev/null || true
|
|
|
|
# Create associative array to track processed modules
|
|
local processed_modules=""
|
|
|
|
# First, add modules from UCI config
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
for module in $module_sections; do
|
|
local name desc category icon color package config version
|
|
|
|
config_get name "$module" name "$module"
|
|
config_get desc "$module" description ""
|
|
config_get category "$module" category "other"
|
|
config_get icon "$module" icon "📦"
|
|
config_get color "$module" color "#64748b"
|
|
config_get package "$module" package ""
|
|
config_get config "$module" config ""
|
|
config_get version "$module" version "0.0.9"
|
|
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_enabled=$(check_module_enabled "$module")
|
|
local is_running=$(check_module_running "$module")
|
|
local status=$(get_module_status "$is_enabled" "$is_running")
|
|
|
|
# Get real version from package manager if installed
|
|
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
|
|
if [ -x "/usr/bin/apk" ]; then
|
|
# OpenWrt 25.12+ uses apk
|
|
# apk output: luci-app-network-modes-0.3.1-r1 noarch {...}
|
|
local real_version=$(apk list --installed "$package" 2>/dev/null | head -1 | awk '{print $1}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
[ -n "$real_version" ] && version="$real_version"
|
|
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
|
|
# OpenWrt 24.10 and earlier uses opkg
|
|
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
|
|
[ -n "$real_version" ] && version="$real_version"
|
|
fi
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "category" "$category"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "package" "$package"
|
|
json_add_string "config" "$config"
|
|
json_add_string "version" "$version"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_add_boolean "in_uci" "1"
|
|
json_close_object
|
|
|
|
processed_modules="$processed_modules $module"
|
|
done
|
|
|
|
# Second, add auto-detected modules not in UCI
|
|
detect_real_modules | while IFS=: read module_id package version; do
|
|
# Skip if already processed
|
|
echo "$processed_modules" | grep -q " $module_id " && continue
|
|
|
|
# Auto-detect properties based on package name
|
|
local name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1)) substr($i,2)}}1')
|
|
local category="other"
|
|
local icon="📦"
|
|
local color="#64748b"
|
|
|
|
# Auto-categorize
|
|
case "$package" in
|
|
*crowdsec*|*guardian*|*auth*)
|
|
category="security"; icon="🛡️"; color="#22c55e"
|
|
;;
|
|
*netdata*|*monitoring*)
|
|
category="monitoring"; icon="📊"; color="#00ab44"
|
|
;;
|
|
*network*|*wireguard*|*bandwidth*|*traffic*)
|
|
category="network"; icon="🌐"; color="#3b82f6"
|
|
;;
|
|
*system*|*hub*)
|
|
category="system"; icon="⚙️"; color="#6366f1"
|
|
;;
|
|
esac
|
|
|
|
local clean_module=$(echo "$module_id" | sed 's/_/-/g')
|
|
local is_enabled=$(check_module_enabled "$module_id")
|
|
local is_running=$(check_module_running "$module_id")
|
|
local status=$(get_module_status "$is_enabled" "$is_running")
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module_id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "Auto-detected module"
|
|
json_add_string "category" "$category"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "package" "$package"
|
|
json_add_string "config" "$clean_module"
|
|
json_add_string "version" "$version"
|
|
json_add_boolean "installed" "1"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_add_boolean "in_uci" "0"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get modules by category
|
|
get_modules_by_category() {
|
|
local category="$1"
|
|
|
|
json_init
|
|
json_add_array "modules"
|
|
|
|
config_load secubox 2>/dev/null || true
|
|
local processed_modules=""
|
|
|
|
# List all module sections from UCI config
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
# First pass: UCI-defined modules in this category
|
|
for module in $module_sections; do
|
|
local mod_category
|
|
config_get mod_category "$module" category "other"
|
|
|
|
if [ "$mod_category" = "$category" ]; then
|
|
local name desc icon color package config version
|
|
|
|
config_get name "$module" name "$module"
|
|
config_get desc "$module" description ""
|
|
config_get icon "$module" icon "box"
|
|
config_get color "$module" color "#64748b"
|
|
config_get package "$module" package ""
|
|
config_get config "$module" config ""
|
|
config_get version "$module" version "0.0.9"
|
|
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_enabled=$(check_module_enabled "$module")
|
|
local is_running=$(check_module_running "$module")
|
|
local status=$(get_module_status "$is_enabled" "$is_running")
|
|
|
|
# Get real version from opkg if installed
|
|
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
|
|
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
|
|
[ -n "$real_version" ] && version="$real_version"
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "version" "$version"
|
|
json_add_string "package" "$package"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_add_boolean "in_uci" "1"
|
|
json_close_object
|
|
|
|
processed_modules="$processed_modules $module"
|
|
fi
|
|
done
|
|
|
|
# Second pass: Auto-detected modules not in UCI
|
|
detect_real_modules | while IFS=: read module_id package version; do
|
|
# Skip if already processed from UCI
|
|
echo "$processed_modules" | grep -q " $module_id " && continue
|
|
|
|
# Auto-categorize
|
|
local mod_category icon color name
|
|
case "$package" in
|
|
*crowdsec*|*guardian*|*auth*)
|
|
mod_category="security"; icon="🛡️"; color="#22c55e"
|
|
;;
|
|
*netdata*|*monitoring*)
|
|
mod_category="monitoring"; icon="📊"; color="#00ab44"
|
|
;;
|
|
*network*|*wireguard*|*bandwidth*|*traffic*|*cdn*)
|
|
mod_category="network"; icon="🌐"; color="#3b82f6"
|
|
;;
|
|
*system*|*hub*|*vhost*)
|
|
mod_category="system"; icon="⚙️"; color="#6366f1"
|
|
;;
|
|
*)
|
|
mod_category="other"; icon="📦"; color="#64748b"
|
|
;;
|
|
esac
|
|
|
|
# Only include if matches requested category
|
|
if [ "$mod_category" = "$category" ]; then
|
|
# Generate nice name from package
|
|
name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
|
|
|
|
local is_installed="1" # Must be installed if detected
|
|
local is_enabled=$(check_module_enabled "$module_id")
|
|
local is_running=$(check_module_running "$module_id")
|
|
local status=$(get_module_status "$is_enabled" "$is_running")
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module_id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "Auto-detected module"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "version" "$version"
|
|
json_add_string "package" "$package"
|
|
json_add_string "category" "$mod_category"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_add_boolean "in_uci" "0"
|
|
json_close_object
|
|
fi
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get single module info
|
|
get_module_info() {
|
|
local module="$1"
|
|
|
|
config_load secubox
|
|
|
|
local name desc category icon color package config version
|
|
|
|
config_get name "$module" name "$module"
|
|
config_get desc "$module" description ""
|
|
config_get category "$module" category "other"
|
|
config_get icon "$module" icon "box"
|
|
config_get color "$module" color "#64748b"
|
|
config_get package "$module" package ""
|
|
config_get config "$module" config ""
|
|
config_get version "$module" version "0.0.9"
|
|
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_enabled=$(check_module_enabled "$module")
|
|
local is_running=$(check_module_running "$module")
|
|
local status=$(get_module_status "$is_enabled" "$is_running")
|
|
|
|
json_init
|
|
json_add_string "id" "$module"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "category" "$category"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "package" "$package"
|
|
json_add_string "config" "$config"
|
|
json_add_string "version" "$version"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_dump
|
|
}
|
|
|
|
# Start a module
|
|
start_module() {
|
|
local module="$1"
|
|
local config
|
|
|
|
config_load secubox
|
|
config_get config "$module" config ""
|
|
|
|
if [ -x "/etc/init.d/${config}" ]; then
|
|
/etc/init.d/${config} start
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Module started"
|
|
json_dump
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Init script not found"
|
|
json_dump
|
|
fi
|
|
}
|
|
|
|
# Stop a module
|
|
stop_module() {
|
|
local module="$1"
|
|
local config
|
|
|
|
config_load secubox
|
|
config_get config "$module" config ""
|
|
|
|
if [ -x "/etc/init.d/${config}" ]; then
|
|
/etc/init.d/${config} stop
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Module stopped"
|
|
json_dump
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Init script not found"
|
|
json_dump
|
|
fi
|
|
}
|
|
|
|
# Restart a module (DEPRECATED - use disable/enable instead)
|
|
restart_module() {
|
|
local module="$1"
|
|
local config
|
|
|
|
config_load secubox
|
|
config_get config "$module" config ""
|
|
|
|
if [ -x "/etc/init.d/${config}" ]; then
|
|
/etc/init.d/${config} restart
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Module restarted"
|
|
json_dump
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Init script not found"
|
|
json_dump
|
|
fi
|
|
}
|
|
|
|
# Enable a module (NEW v0.3.1)
|
|
enable_module() {
|
|
local module="$1"
|
|
|
|
# Set enabled flag in UCI (v0.3.1)
|
|
# This only activates the module in configuration
|
|
# Use start_module() to actually start the service
|
|
uci set secubox.${module}.enabled='1'
|
|
uci commit secubox
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Module activé dans la configuration"
|
|
json_add_string "note" "Utilisez 'Démarrer' pour lancer le service"
|
|
json_dump
|
|
}
|
|
|
|
# Disable a module (NEW v0.3.1)
|
|
disable_module() {
|
|
local module="$1"
|
|
|
|
# Set disabled flag in UCI (v0.3.1)
|
|
# This only deactivates the module in configuration
|
|
# Use stop_module() to actually stop the service
|
|
uci set secubox.${module}.enabled='0'
|
|
uci commit secubox
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Module désactivé dans la configuration"
|
|
json_add_string "note" "Utilisez 'Arrêter' pour stopper le service"
|
|
json_dump
|
|
}
|
|
|
|
# Get health report
|
|
get_health() {
|
|
json_init
|
|
json_add_array "checks"
|
|
|
|
config_load secubox 2>/dev/null || true
|
|
|
|
# List all module sections from UCI config
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
# Check each installed module
|
|
for module in $module_sections; do
|
|
local is_installed=$(check_module_installed "$module")
|
|
|
|
if [ "$is_installed" = "1" ]; then
|
|
local is_running=$(check_module_running "$module")
|
|
local name
|
|
|
|
config_get name "$module" name "$module"
|
|
|
|
local status="ok"
|
|
local message="Running normally"
|
|
|
|
if [ "$is_running" != "1" ]; then
|
|
status="warning"
|
|
message="Service not running"
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "module" "$module"
|
|
json_add_string "name" "$name"
|
|
json_add_string "status" "$status"
|
|
json_add_string "message" "$message"
|
|
json_close_object
|
|
fi
|
|
done
|
|
|
|
json_close_array
|
|
|
|
# Overall health
|
|
local overall="healthy"
|
|
# Could add more sophisticated health checks here
|
|
|
|
json_add_string "overall" "$overall"
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Generate diagnostics bundle
|
|
get_diagnostics() {
|
|
json_init
|
|
|
|
# System info
|
|
json_add_object "system"
|
|
json_add_string "hostname" "$(uci -q get system.@system[0].hostname)"
|
|
json_add_string "model" "$(cat /tmp/sysinfo/model 2>/dev/null || echo 'Unknown')"
|
|
json_add_string "openwrt_version" "$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d= -f2 | tr -d \"\')"
|
|
json_add_string "kernel" "$(uname -r)"
|
|
json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)"
|
|
json_close_object
|
|
|
|
# Modules status
|
|
json_add_array "modules"
|
|
config_load secubox 2>/dev/null || true
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
for module in $module_sections; do
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_running=$(check_module_running "$module")
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "running" "$is_running"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
|
|
# Network interfaces
|
|
json_add_array "interfaces"
|
|
for iface in $(ls /sys/class/net/); do
|
|
local mac=$(cat /sys/class/net/$iface/address 2>/dev/null)
|
|
local state=$(cat /sys/class/net/$iface/operstate 2>/dev/null)
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$iface"
|
|
json_add_string "mac" "$mac"
|
|
json_add_string "state" "$state"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
|
|
json_add_int "generated" "$(date +%s)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get system health metrics
|
|
get_system_health() {
|
|
json_init
|
|
|
|
# CPU usage (based on load average)
|
|
local load=$(cat /proc/loadavg | cut -d' ' -f1)
|
|
local cpu_cores=$(grep -c processor /proc/cpuinfo)
|
|
local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load / $cpu_cores) * 100}")
|
|
|
|
# Memory
|
|
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
local mem_avail=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
|
|
local mem_used=$((mem_total - mem_avail))
|
|
local mem_pct=$((mem_used * 100 / mem_total))
|
|
|
|
# Disk usage (root filesystem)
|
|
local disk_info=$(df / | tail -1)
|
|
local disk_total=$(echo "$disk_info" | awk '{print $2}')
|
|
local disk_used=$(echo "$disk_info" | awk '{print $3}')
|
|
local disk_pct=$(echo "$disk_info" | awk '{print $5}' | tr -d '%')
|
|
|
|
# Network stats (sum all interfaces)
|
|
local net_rx=0
|
|
local net_tx=0
|
|
for iface in $(ls /sys/class/net/ | grep -v lo); do
|
|
if [ -f "/sys/class/net/$iface/statistics/rx_bytes" ]; then
|
|
local rx=$(cat /sys/class/net/$iface/statistics/rx_bytes 2>/dev/null || echo 0)
|
|
local tx=$(cat /sys/class/net/$iface/statistics/tx_bytes 2>/dev/null || echo 0)
|
|
net_rx=$((net_rx + rx))
|
|
net_tx=$((net_tx + tx))
|
|
fi
|
|
done
|
|
|
|
# Uptime
|
|
local uptime=$(cat /proc/uptime | cut -d. -f1)
|
|
|
|
# Load averages
|
|
local load_1min=$(cat /proc/loadavg | cut -d' ' -f1)
|
|
local load_5min=$(cat /proc/loadavg | cut -d' ' -f2)
|
|
local load_15min=$(cat /proc/loadavg | cut -d' ' -f3)
|
|
|
|
json_add_object "cpu"
|
|
json_add_int "percent" "$cpu_pct"
|
|
json_add_int "cores" "$cpu_cores"
|
|
json_add_string "load_1min" "$load_1min"
|
|
json_add_string "load_5min" "$load_5min"
|
|
json_add_string "load_15min" "$load_15min"
|
|
json_close_object
|
|
|
|
json_add_object "memory"
|
|
json_add_int "percent" "$mem_pct"
|
|
json_add_int "total_kb" "$mem_total"
|
|
json_add_int "used_kb" "$mem_used"
|
|
json_add_int "available_kb" "$mem_avail"
|
|
json_close_object
|
|
|
|
json_add_object "disk"
|
|
json_add_int "percent" "$disk_pct"
|
|
json_add_int "total_kb" "$disk_total"
|
|
json_add_int "used_kb" "$disk_used"
|
|
json_close_object
|
|
|
|
json_add_object "network"
|
|
json_add_int "rx_bytes" "$net_rx"
|
|
json_add_int "tx_bytes" "$net_tx"
|
|
json_close_object
|
|
|
|
json_add_int "uptime" "$uptime"
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get aggregated alerts from all modules
|
|
get_alerts() {
|
|
json_init
|
|
json_add_array "alerts"
|
|
|
|
local alert_count=0
|
|
|
|
# Check each installed module for alerts
|
|
config_load secubox 2>/dev/null || true
|
|
|
|
# List all module sections from UCI config
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
for module in $module_sections; do
|
|
local is_installed=$(check_module_installed "$module")
|
|
|
|
if [ "$is_installed" = "1" ]; then
|
|
# Check if module service is not running
|
|
local is_running=$(check_module_running "$module")
|
|
if [ "$is_running" != "1" ]; then
|
|
local name
|
|
config_get name "$module" name "$module"
|
|
|
|
json_add_object ""
|
|
json_add_string "module" "$module"
|
|
json_add_string "message" "$name service is not running"
|
|
json_add_string "severity" "warning"
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
json_close_object
|
|
|
|
alert_count=$((alert_count + 1))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
json_close_array
|
|
json_add_int "count" "$alert_count"
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Execute quick actions
|
|
quick_action() {
|
|
local action="$1"
|
|
|
|
json_init
|
|
|
|
case "$action" in
|
|
restart_rpcd)
|
|
/etc/init.d/rpcd restart >/dev/null 2>&1
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "RPCD service restarted"
|
|
;;
|
|
restart_uhttpd)
|
|
/etc/init.d/uhttpd restart >/dev/null 2>&1
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "uHTTPd service restarted"
|
|
;;
|
|
clear_cache)
|
|
sync
|
|
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "System cache cleared"
|
|
;;
|
|
backup_config)
|
|
local backup_file="/tmp/backup-$(date +%Y%m%d-%H%M%S).tar.gz"
|
|
sysupgrade -b "$backup_file" >/dev/null 2>&1
|
|
if [ -f "$backup_file" ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Configuration backup created"
|
|
json_add_string "file" "$backup_file"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Backup failed"
|
|
fi
|
|
;;
|
|
restart_network)
|
|
/etc/init.d/network restart >/dev/null 2>&1
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Network services restarted"
|
|
;;
|
|
restart_firewall)
|
|
/etc/init.d/firewall restart >/dev/null 2>&1
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall restarted"
|
|
;;
|
|
*)
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Unknown action: $action"
|
|
;;
|
|
esac
|
|
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
json_dump
|
|
}
|
|
|
|
# Get all dashboard data in one call
|
|
get_dashboard_data() {
|
|
json_init
|
|
|
|
# Get status info
|
|
local uptime=$(cat /proc/uptime | cut -d. -f1)
|
|
local load=$(cat /proc/loadavg | cut -d' ' -f1-3)
|
|
local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
|
|
local mem_used=$((mem_total - mem_free))
|
|
local mem_pct=$((mem_used * 100 / mem_total))
|
|
|
|
json_add_object "status"
|
|
json_add_string "version" "$PKG_VERSION"
|
|
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'SecuBox')"
|
|
json_add_int "uptime" "$uptime"
|
|
json_add_string "load" "$load"
|
|
json_add_int "memory_total" "$mem_total"
|
|
json_add_int "memory_used" "$mem_used"
|
|
json_add_int "memory_percent" "$mem_pct"
|
|
json_close_object
|
|
|
|
# Load config once for all modules
|
|
config_load secubox 2>/dev/null || true
|
|
|
|
# Get modules list
|
|
json_add_array "modules"
|
|
local total=0 installed=0 running=0
|
|
|
|
# List all module sections from UCI config
|
|
local module_sections=$(uci -q show secubox | grep "=module$" | cut -d. -f2 | cut -d= -f1)
|
|
|
|
for module in $module_sections; do
|
|
local name desc category icon color package version
|
|
config_get name "$module" name "$module"
|
|
config_get desc "$module" description ""
|
|
config_get category "$module" category "other"
|
|
config_get icon "$module" icon "box"
|
|
config_get color "$module" color "#64748b"
|
|
config_get package "$module" package ""
|
|
config_get version "$module" version "0.0.9"
|
|
|
|
local is_installed=$(check_module_installed "$module")
|
|
local is_enabled="0"
|
|
local is_running="0"
|
|
local status="disabled"
|
|
|
|
total=$((total + 1))
|
|
|
|
if [ "$is_installed" = "1" ]; then
|
|
is_enabled=$(check_module_enabled "$module")
|
|
is_running=$(check_module_running "$module")
|
|
status=$(get_module_status "$is_enabled" "$is_running")
|
|
installed=$((installed + 1))
|
|
[ "$is_running" = "1" ] && running=$((running + 1))
|
|
|
|
# Get real version from package manager if installed
|
|
if [ -n "$package" ]; then
|
|
if [ -x "/usr/bin/apk" ]; then
|
|
# OpenWrt 25.12+ uses apk
|
|
local real_version=$(apk list --installed "$package" 2>/dev/null | head -1 | awk '{print $1}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
[ -n "$real_version" ] && version="$real_version"
|
|
elif [ -x "/bin/opkg" ] || [ -x "/usr/bin/opkg" ]; then
|
|
# OpenWrt 24.10 and earlier uses opkg
|
|
local real_version=$(opkg list-installed "$package" 2>/dev/null | awk '{print $3}' | sed 's/-.*$//')
|
|
[ -n "$real_version" ] && version="$real_version"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$module"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "category" "$category"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "color" "$color"
|
|
json_add_string "version" "$version"
|
|
json_add_boolean "installed" "$is_installed"
|
|
json_add_boolean "enabled" "$is_enabled"
|
|
json_add_boolean "running" "$is_running"
|
|
json_add_string "status" "$status"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
|
|
json_add_object "counts"
|
|
json_add_int "total" "$total"
|
|
json_add_int "installed" "$installed"
|
|
json_add_int "running" "$running"
|
|
json_close_object
|
|
|
|
# Get system health
|
|
local cpu_cores=$(grep -c processor /proc/cpuinfo)
|
|
local load_val=$(echo "$load" | cut -d' ' -f1)
|
|
local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load_val / $cpu_cores) * 100}" 2>/dev/null || echo "0")
|
|
local disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
|
|
|
|
json_add_object "health"
|
|
json_add_int "cpu_percent" "$cpu_pct"
|
|
json_add_int "memory_percent" "$mem_pct"
|
|
json_add_int "disk_percent" "$disk_pct"
|
|
json_close_object
|
|
|
|
# Empty alerts array
|
|
json_add_array "alerts"
|
|
json_close_array
|
|
|
|
json_add_int "timestamp" "$(date +%s)"
|
|
json_dump
|
|
}
|
|
|
|
# Get theme setting
|
|
get_theme() {
|
|
local theme="dark"
|
|
|
|
# Load secubox config
|
|
if [ -f "/etc/config/secubox" ]; then
|
|
config_load secubox
|
|
config_get theme main theme "dark"
|
|
fi
|
|
|
|
json_init
|
|
json_add_string "theme" "$theme"
|
|
json_dump
|
|
}
|
|
|
|
# Persist theme setting
|
|
set_theme() {
|
|
local input theme="dark"
|
|
|
|
read -r input
|
|
[ -n "$input" ] && json_load "$input"
|
|
json_get_var theme theme "dark"
|
|
|
|
case "$theme" in
|
|
dark|light|system|cyberpunk)
|
|
;;
|
|
*)
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Invalid theme"
|
|
json_dump
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
uci -q set secubox.main.theme="$theme"
|
|
uci -q commit secubox
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "theme" "$theme"
|
|
json_add_string "message" "Theme updated"
|
|
json_dump
|
|
}
|
|
|
|
# Dismiss a specific alert
|
|
dismiss_alert() {
|
|
local alert_id="$1"
|
|
|
|
# Store dismissed alerts in tmp file
|
|
local dismissed_file="/tmp/secubox_dismissed_alerts"
|
|
|
|
# Append alert_id to dismissed list
|
|
echo "$alert_id" >> "$dismissed_file"
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Alert dismissed"
|
|
json_dump
|
|
}
|
|
|
|
# Clear all alerts
|
|
clear_alerts() {
|
|
# Clear dismissed alerts file
|
|
local dismissed_file="/tmp/secubox_dismissed_alerts"
|
|
rm -f "$dismissed_file"
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "All alerts cleared"
|
|
json_dump
|
|
}
|
|
|
|
# Fix permissions (v0.3.1)
|
|
fix_permissions() {
|
|
local fix_script="/usr/libexec/secubox/fix-permissions.sh"
|
|
|
|
if [ ! -x "$fix_script" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Fix script not found or not executable"
|
|
json_dump
|
|
return 1
|
|
fi
|
|
|
|
# Run the fix script and capture output
|
|
local output=$($fix_script 2>&1)
|
|
local exit_code=$?
|
|
|
|
json_init
|
|
if [ $exit_code -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Permissions fixed successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "message" "Failed to fix permissions"
|
|
fi
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
}
|
|
|
|
# Main dispatcher
|
|
first_run_status() {
|
|
local timezone storage network_json mode_name mode_id storage_path
|
|
storage_path=$(get_storage_path)
|
|
local storage_ready=0
|
|
[ -d "$storage_path" ] && storage_ready=1
|
|
local password_state=0
|
|
if is_root_password_set; then
|
|
password_state=1
|
|
fi
|
|
timezone=$(uci -q get system.@system[0].timezone || echo "UTC")
|
|
mode_id="unknown"
|
|
mode_name="N/A"
|
|
if command -v ubus >/dev/null 2>&1; then
|
|
network_json=$(ubus call luci.network-modes status 2>/dev/null || printf '')
|
|
if [ -n "$network_json" ]; then
|
|
mode_id=$(jsonfilter -s "$network_json" -e '@.current_mode' 2>/dev/null || echo "$mode_id")
|
|
mode_name=$(jsonfilter -s "$network_json" -e '@.mode_name' 2>/dev/null || echo "$mode_name")
|
|
fi
|
|
fi
|
|
json_init
|
|
json_add_boolean "password_set" "$password_state"
|
|
json_add_string "timezone" "$timezone"
|
|
json_add_string "storage_path" "$storage_path"
|
|
json_add_boolean "storage_ready" "$storage_ready"
|
|
json_add_string "network_mode" "$mode_id"
|
|
json_add_string "network_mode_name" "$mode_name"
|
|
json_add_array "recommended_modes"
|
|
json_add_string "" "router"
|
|
json_add_string "" "dmz"
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
apply_first_run() {
|
|
local input
|
|
read input
|
|
json_load "$input"
|
|
json_get_var timezone timezone
|
|
json_get_var storage_path storage_path
|
|
json_get_var network_mode network_mode
|
|
json_init
|
|
json_add_array "messages"
|
|
if [ -n "$timezone" ]; then
|
|
uci set system.@system[0].timezone="$timezone"
|
|
uci set system.@system[0].zonename="$timezone"
|
|
uci commit system
|
|
if [ -f "/usr/share/zoneinfo/$timezone" ]; then
|
|
ln -sf "/usr/share/zoneinfo/$timezone" /etc/localtime
|
|
fi
|
|
json_add_string "" "Timezone updated"
|
|
fi
|
|
if [ -n "$storage_path" ]; then
|
|
mkdir -p "$storage_path"
|
|
chmod 755 "$storage_path"
|
|
uci set secubox.main.storage_path="$storage_path"
|
|
uci commit secubox
|
|
json_add_string "" "Storage prepared at $storage_path"
|
|
fi
|
|
if [ -n "$network_mode" ]; then
|
|
if command -v ubus >/dev/null 2>&1; then
|
|
if ubus call luci.network-modes set_mode "{\"mode\":\"$network_mode\"}" >/dev/null 2>&1; then
|
|
if ubus call luci.network-modes apply_mode >/dev/null 2>&1; then
|
|
json_add_string "" "Network mode $network_mode applied"
|
|
else
|
|
json_add_string "" "Failed to apply network mode"
|
|
fi
|
|
else
|
|
json_add_string "" "Mode $network_mode unsupported"
|
|
fi
|
|
else
|
|
json_add_string "" "ubus unavailable; cannot change network mode"
|
|
fi
|
|
fi
|
|
json_close_array
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
}
|
|
|
|
list_apps() {
|
|
ensure_directory "$PLUGIN_DIR"
|
|
json_init
|
|
json_add_array "apps"
|
|
local manifest_file manifest id name runtime type version description state wizard_field category maturity seen_ids=""
|
|
while IFS= read -r manifest_file; do
|
|
[ -f "$manifest_file" ] || continue
|
|
manifest=$(cat "$manifest_file")
|
|
id=$(jsonfilter -s "$manifest" -e '@.id' 2>/dev/null)
|
|
[ -n "$id" ] || continue
|
|
case " $seen_ids " in
|
|
*" $id "*) continue ;;
|
|
esac
|
|
if ! validate_manifest_json "$manifest"; then
|
|
continue
|
|
fi
|
|
seen_ids="$seen_ids $id"
|
|
name=$(jsonfilter -s "$manifest" -e '@.name' 2>/dev/null)
|
|
type=$(jsonfilter -s "$manifest" -e '@.type' 2>/dev/null)
|
|
runtime=$(jsonfilter -s "$manifest" -e '@.runtime' 2>/dev/null)
|
|
[ -n "$type" ] || type="$runtime"
|
|
version=$(jsonfilter -s "$manifest" -e '@.version' 2>/dev/null)
|
|
description=$(jsonfilter -s "$manifest" -e '@.description' 2>/dev/null)
|
|
category=$(jsonfilter -s "$manifest" -e '@.category' 2>/dev/null)
|
|
maturity=$(jsonfilter -s "$manifest" -e '@.maturity' 2>/dev/null)
|
|
state=$(packages_state "$manifest")
|
|
wizard_field=$(jsonfilter -s "$manifest" -e '@.wizard.fields[0].id' 2>/dev/null)
|
|
json_add_object
|
|
json_add_string "id" "$id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "runtime" "$runtime"
|
|
json_add_string "type" "$type"
|
|
json_add_string "version" "$version"
|
|
json_add_string "description" "$description"
|
|
json_add_string "category" "$category"
|
|
json_add_string "maturity" "$maturity"
|
|
json_add_string "state" "$state"
|
|
json_add_boolean "has_wizard" "$([ -n "$wizard_field" ] && echo 1 || echo 0)"
|
|
json_close_object
|
|
done <<EOF
|
|
$(manifest_files | sort)
|
|
EOF
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
get_app_manifest() {
|
|
local input app_id file manifest state
|
|
read input
|
|
app_id=$(jsonfilter -s "$input" -e '@.app_id' 2>/dev/null)
|
|
if [ -z "$app_id" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "app_id required"
|
|
json_dump
|
|
return
|
|
fi
|
|
file=$(manifest_file_for_id "$app_id")
|
|
if [ -z "$file" ] || [ ! -f "$file" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Manifest not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
manifest=$(cat "$file")
|
|
state=$(packages_state "$manifest")
|
|
json_load "$manifest"
|
|
json_add_string "state" "$state"
|
|
json_dump
|
|
}
|
|
|
|
apply_app_wizard() {
|
|
local input app_id file manifest config section fields field option value
|
|
read input
|
|
app_id=$(jsonfilter -s "$input" -e '@.app_id' 2>/dev/null)
|
|
if [ -z "$app_id" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "app_id required"
|
|
json_dump
|
|
return
|
|
fi
|
|
file=$(manifest_file_for_id "$app_id")
|
|
if [ -z "$file" ] || [ ! -f "$file" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Manifest not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
manifest=$(cat "$file")
|
|
config=$(jsonfilter -s "$manifest" -e '@.wizard.uci.config' 2>/dev/null)
|
|
section=$(jsonfilter -s "$manifest" -e '@.wizard.uci.section' 2>/dev/null)
|
|
[ -n "$section" ] || section="main"
|
|
if [ -z "$config" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Wizard missing UCI config"
|
|
json_dump
|
|
return
|
|
fi
|
|
fields=$(jsonfilter -s "$manifest" -e '@.wizard.fields[*].id' 2>/dev/null)
|
|
for field in $fields; do
|
|
option=$(jsonfilter -s "$manifest" -e "@.wizard.fields[@.id='$field'].uci_option" 2>/dev/null)
|
|
[ -n "$option" ] || continue
|
|
value=$(jsonfilter -s "$input" -e "@.values.$field" 2>/dev/null)
|
|
[ -n "$value" ] || continue
|
|
uci set ${config}.${section}.${option}="$value"
|
|
done
|
|
uci commit "$config"
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
}
|
|
|
|
list_profiles() {
|
|
ensure_directory "$PROFILE_DIR"
|
|
json_init
|
|
json_add_array "profiles"
|
|
local profile_file
|
|
for profile_file in "$PROFILE_DIR"/*.json; do
|
|
[ -f "$profile_file" ] || continue
|
|
local profile_json id name description network_mode apps state
|
|
profile_json=$(cat "$profile_file")
|
|
id=$(jsonfilter -s "$profile_json" -e '@.id' 2>/dev/null)
|
|
name=$(jsonfilter -s "$profile_json" -e '@.name' 2>/dev/null)
|
|
description=$(jsonfilter -s "$profile_json" -e '@.description' 2>/dev/null)
|
|
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
|
|
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
|
|
state=$(packages_state "$profile_json")
|
|
[ -n "$id" ] || continue
|
|
json_add_object
|
|
json_add_string "id" "$id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$description"
|
|
json_add_string "network_mode" "$network_mode"
|
|
json_add_string "state" "$state"
|
|
json_add_array "apps"
|
|
for app in $apps; do
|
|
json_add_string "" "$app"
|
|
done
|
|
json_close_array
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
apply_profile() {
|
|
local input profile_id profile_file profile_json network_mode packages apps backup_file notes=""
|
|
read input
|
|
json_load "$input"
|
|
json_get_var profile_id profile_id
|
|
json_cleanup
|
|
[ -n "$profile_id" ] || {
|
|
json_init; json_add_boolean "success" 0; json_add_string "error" "profile_id required"; json_dump; return;
|
|
}
|
|
profile_file="$PROFILE_DIR/$profile_id.json"
|
|
if [ ! -f "$profile_file" ]; then
|
|
json_init; json_add_boolean "success" 0; json_add_string "error" "Profile not found"; json_dump; return;
|
|
fi
|
|
profile_json=$(cat "$profile_file")
|
|
ensure_directory "$PROFILE_BACKUP_DIR"
|
|
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
backup_file="$PROFILE_BACKUP_DIR/profile_${profile_id}_${timestamp}.tar.gz"
|
|
tar -czf "$backup_file" /etc/config 2>/dev/null
|
|
notes="${notes}Backup saved to $backup_file\n"
|
|
uci set secubox.profile.last_backup="$backup_file"
|
|
uci set secubox.profile.last_profile="$profile_id"
|
|
uci commit secubox
|
|
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
|
|
if [ -n "$network_mode" ]; then
|
|
notes="${notes}Switched network mode to $network_mode\n"
|
|
apply_network_mode "$network_mode"
|
|
fi
|
|
packages=$(jsonfilter -s "$profile_json" -e '@.packages[*]' 2>/dev/null)
|
|
for pkg in $packages; do
|
|
install_package_if_missing "$pkg"
|
|
notes="${notes}Ensured package $pkg\n"
|
|
done
|
|
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
|
|
for app in $apps; do
|
|
if [ -x "$SECOBOX_APP" ]; then
|
|
$SECOBOX_APP install "$app" >/dev/null 2>&1 && notes="${notes}Installed app $app\n"
|
|
fi
|
|
done
|
|
json_load "$profile_json"
|
|
local uci_configs=""
|
|
if json_select uci >/dev/null 2>&1; then
|
|
local entries
|
|
json_get_keys entries
|
|
for entry in $entries; do
|
|
json_select "$entry"
|
|
local cfg section option value
|
|
json_get_var cfg config
|
|
json_get_var section section
|
|
json_get_var option option
|
|
json_get_var value value
|
|
section=${section:-main}
|
|
if [ -n "$cfg" ] && [ -n "$option" ]; then
|
|
uci set ${cfg}.${section}.${option}="$value"
|
|
uci_configs="${uci_configs} $cfg"
|
|
notes="${notes}Set ${cfg}.${section}.${option}=${value}\n"
|
|
fi
|
|
json_select ..
|
|
done
|
|
json_select ..
|
|
fi
|
|
json_cleanup
|
|
local committed=""
|
|
for cfg in $uci_configs; do
|
|
case " $committed " in
|
|
*" $cfg "*) continue;;
|
|
esac
|
|
uci commit "$cfg"
|
|
committed="$committed $cfg"
|
|
done
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_array "messages"
|
|
printf '%b' "$notes" | while IFS= read -r line; do
|
|
[ -n "$line" ] && json_add_string "" "$line"
|
|
done
|
|
json_close_array
|
|
json_add_string "backup" "$backup_file"
|
|
json_dump
|
|
}
|
|
|
|
rollback_profile() {
|
|
local latest_backup
|
|
latest_backup=$(ls -t "$PROFILE_BACKUP_DIR"/profile_*.tar.gz 2>/dev/null | head -1)
|
|
if [ -z "$latest_backup" ]; then
|
|
json_init; json_add_boolean "success" 0; json_add_string "error" "No backups available"; json_dump; return;
|
|
fi
|
|
cd /
|
|
tar -xzf "$latest_backup" 2>/dev/null
|
|
/etc/init.d/network reload >/dev/null 2>&1
|
|
/etc/init.d/firewall reload >/dev/null 2>&1
|
|
/etc/init.d/dnsmasq reload >/dev/null 2>&1
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Restored $latest_backup"
|
|
json_dump
|
|
}
|
|
case "$1" in
|
|
list)
|
|
json_init
|
|
json_add_object "status"
|
|
json_close_object
|
|
json_add_object "modules"
|
|
json_close_object
|
|
json_add_object "modules_by_category"
|
|
json_add_string "category" "string"
|
|
json_close_object
|
|
json_add_object "module_info"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "start_module"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "stop_module"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "restart_module"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "enable_module"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "disable_module"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "check_module_enabled"
|
|
json_add_string "module" "string"
|
|
json_close_object
|
|
json_add_object "health"
|
|
json_close_object
|
|
json_add_object "diagnostics"
|
|
json_close_object
|
|
json_add_object "get_system_health"
|
|
json_close_object
|
|
json_add_object "get_alerts"
|
|
json_close_object
|
|
json_add_object "quick_action"
|
|
json_add_string "action" "string"
|
|
json_close_object
|
|
json_add_object "get_dashboard_data"
|
|
json_close_object
|
|
json_add_object "get_theme"
|
|
json_close_object
|
|
json_add_object "set_theme"
|
|
json_add_string "theme" "string"
|
|
json_close_object
|
|
json_add_object "dismiss_alert"
|
|
json_add_string "alert_id" "string"
|
|
json_close_object
|
|
json_add_object "clear_alerts"
|
|
json_close_object
|
|
json_add_object "fix_permissions"
|
|
json_close_object
|
|
json_add_object "first_run_status"
|
|
json_close_object
|
|
json_add_object "apply_first_run"
|
|
json_close_object
|
|
json_add_object "list_apps"
|
|
json_close_object
|
|
json_add_object "get_app_manifest"
|
|
json_add_string "app_id" "string"
|
|
json_close_object
|
|
json_add_object "apply_app_wizard"
|
|
json_add_string "app_id" "string"
|
|
json_close_object
|
|
json_add_object "list_profiles"
|
|
json_close_object
|
|
json_add_object "apply_profile"
|
|
json_add_string "profile_id" "string"
|
|
json_close_object
|
|
json_add_object "rollback_profile"
|
|
json_close_object
|
|
json_add_object "first_run_status"
|
|
json_close_object
|
|
json_add_object "apply_first_run"
|
|
json_close_object
|
|
json_add_object "list_apps"
|
|
json_close_object
|
|
json_add_object "get_app_manifest"
|
|
json_add_string "app_id" "string"
|
|
json_close_object
|
|
json_add_object "apply_app_wizard"
|
|
json_add_string "app_id" "string"
|
|
json_close_object
|
|
json_dump
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
get_status
|
|
;;
|
|
modules)
|
|
get_modules
|
|
;;
|
|
modules_by_category)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var category category "security"
|
|
get_modules_by_category "$category"
|
|
;;
|
|
module_info)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
get_module_info "$module"
|
|
;;
|
|
start_module)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
start_module "$module"
|
|
;;
|
|
stop_module)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
stop_module "$module"
|
|
;;
|
|
restart_module)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
restart_module "$module"
|
|
;;
|
|
enable_module)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
enable_module "$module"
|
|
;;
|
|
disable_module)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
disable_module "$module"
|
|
;;
|
|
check_module_enabled)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var module module ""
|
|
local enabled=$(check_module_enabled "$module")
|
|
json_init
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_dump
|
|
;;
|
|
health)
|
|
get_health
|
|
;;
|
|
diagnostics)
|
|
get_diagnostics
|
|
;;
|
|
get_system_health)
|
|
get_system_health
|
|
;;
|
|
get_alerts)
|
|
get_alerts
|
|
;;
|
|
quick_action)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var action action ""
|
|
quick_action "$action"
|
|
;;
|
|
get_dashboard_data)
|
|
get_dashboard_data
|
|
;;
|
|
get_theme)
|
|
get_theme
|
|
;;
|
|
set_theme)
|
|
set_theme
|
|
;;
|
|
dismiss_alert)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var alert_id alert_id ""
|
|
dismiss_alert "$alert_id"
|
|
;;
|
|
clear_alerts)
|
|
clear_alerts
|
|
;;
|
|
fix_permissions)
|
|
fix_permissions
|
|
;;
|
|
first_run_status)
|
|
first_run_status
|
|
;;
|
|
apply_first_run)
|
|
apply_first_run
|
|
;;
|
|
list_apps)
|
|
list_apps
|
|
;;
|
|
get_app_manifest)
|
|
get_app_manifest
|
|
;;
|
|
apply_app_wizard)
|
|
apply_app_wizard
|
|
;;
|
|
list_profiles)
|
|
list_profiles
|
|
;;
|
|
apply_profile)
|
|
apply_profile
|
|
;;
|
|
rollback_profile)
|
|
rollback_profile
|
|
;;
|
|
*)
|
|
echo '{"error":"Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo '{"error":"Unknown command"}'
|
|
;;
|
|
esac
|