secubox-openwrt/secubox-repair.sh

1110 lines
31 KiB
Bash
Executable File

#!/bin/bash
#
# secubox-repair.sh
# =================
# Script générique pour vérifier et réparer tous les modules SecuBox
# Exécuter depuis Linux sur le dossier contenant les packages luci-app-*
#
# Usage:
# ./secubox-repair.sh # Vérification seulement
# ./secubox-repair.sh --fix # Vérification + réparation
# ./secubox-repair.sh --fix --deploy # + déploiement sur routeur
# ./secubox-repair.sh --help # Aide
#
# Auteur: CyberMind.fr
# License: Apache-2.0
#
set -e
# ============================================
# Configuration
# ============================================
VERSION="2.0.0"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="${SCRIPT_DIR}"
# Router SSH config (modifiable)
ROUTER_HOST="${ROUTER_HOST:-192.168.1.1}"
ROUTER_USER="${ROUTER_USER:-root}"
ROUTER_PORT="${ROUTER_PORT:-22}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m'
# Counters
ERRORS=0
WARNINGS=0
FIXES=0
MODULES_CHECKED=0
# Options
DO_FIX=false
DO_DEPLOY=false
VERBOSE=false
# ============================================
# SecuBox Modules Definition
# ============================================
declare -A MODULE_INFO=(
["secubox"]="SecuBox Hub|Central dashboard for all modules|+luci-base +rpcd +curl +jq"
["crowdsec-dashboard"]="CrowdSec Dashboard|Collaborative threat intelligence|+luci-base +rpcd +crowdsec"
["netdata-dashboard"]="Netdata Dashboard|Real-time system monitoring|+luci-base +rpcd +netdata"
["netifyd-dashboard"]="Netifyd Dashboard|Deep packet inspection|+luci-base +rpcd +netifyd"
["wireguard-dashboard"]="WireGuard Dashboard|VPN management with QR codes|+luci-base +rpcd +wireguard-tools +qrencode"
["network-modes"]="Network Modes|Network topology switcher|+luci-base +rpcd"
["client-guardian"]="Client Guardian|NAC and captive portal|+luci-base +rpcd +nodogsplash"
["system-hub"]="System Hub|System control center|+luci-base +rpcd"
["bandwidth-manager"]="Bandwidth Manager|QoS and quota management|+luci-base +rpcd +tc-full +kmod-sched-cake"
["auth-guardian"]="Auth Guardian|OAuth and voucher portal|+luci-base +rpcd +curl"
["media-flow"]="Media Flow|Streaming detection|+luci-base +rpcd +netifyd"
["vhost-manager"]="VHost Manager|Reverse proxy and SSL|+luci-base +rpcd +nginx-ssl"
["cdn-cache"]="CDN Cache|Local content cache|+luci-base +rpcd +nginx"
["traffic-shaper"]="Traffic Shaper|Advanced traffic control|+luci-base +rpcd +tc-full"
)
# ============================================
# Helper Functions
# ============================================
print_header() {
echo ""
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ${BOLD}$1${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
}
print_section() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
print_module() {
echo ""
echo -e "${MAGENTA}┌──────────────────────────────────────────────────────────────────────┐${NC}"
echo -e "${MAGENTA}${NC} 📦 ${BOLD}$1${NC}"
echo -e "${MAGENTA}└──────────────────────────────────────────────────────────────────────┘${NC}"
}
ok() {
echo -e " ${GREEN}${NC} $1"
}
warn() {
echo -e " ${YELLOW}${NC} $1"
((WARNINGS++))
}
error() {
echo -e " ${RED}${NC} $1"
((ERRORS++))
}
info() {
echo -e " ${CYAN}${NC} $1"
}
fixed() {
echo -e " ${GREEN}🔧${NC} $1"
((FIXES++))
}
verbose() {
if $VERBOSE; then
echo -e " ${CYAN} $1${NC}"
fi
}
# ============================================
# Validation Functions
# ============================================
# Validate JSON file
validate_json() {
local file="$1"
if command -v jq &> /dev/null; then
if jq empty "$file" 2>/dev/null; then
return 0
else
return 1
fi
elif command -v python3 &> /dev/null; then
if python3 -c "import json; json.load(open('$file'))" 2>/dev/null; then
return 0
else
return 1
fi
else
# Can't validate, assume OK
return 0
fi
}
# Validate JavaScript file
validate_js() {
local file="$1"
if command -v node &> /dev/null; then
if node --check "$file" 2>/dev/null; then
return 0
else
return 1
fi
else
# Basic syntax check with grep
if grep -qE '(function|const|let|var|class|import|export)' "$file"; then
return 0
else
return 1
fi
fi
}
# ============================================
# Check Functions
# ============================================
check_makefile() {
local pkg_dir="$1"
local pkg_name="$2"
local makefile="$pkg_dir/Makefile"
info "Checking Makefile..."
if [[ ! -f "$makefile" ]]; then
error "Makefile missing!"
if $DO_FIX; then
generate_makefile "$pkg_dir" "$pkg_name"
fi
return 1
fi
# Check required fields
local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE")
local missing_fields=()
for field in "${required_fields[@]}"; do
if ! grep -q "^${field}:=" "$makefile"; then
missing_fields+=("$field")
fi
done
if [[ ${#missing_fields[@]} -gt 0 ]]; then
warn "Missing fields: ${missing_fields[*]}"
fi
# Check PKG_NAME matches directory
local makefile_pkg_name=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2)
if [[ "$makefile_pkg_name" != "luci-app-$pkg_name" ]]; then
error "PKG_NAME mismatch: expected 'luci-app-$pkg_name', got '$makefile_pkg_name'"
if $DO_FIX; then
sed -i "s/^PKG_NAME:=.*/PKG_NAME:=luci-app-$pkg_name/" "$makefile"
fixed "PKG_NAME corrected"
fi
fi
# Check for luci.mk include
if ! grep -q 'include.*feeds/luci/luci\.mk' "$makefile"; then
if grep -q 'include.*package\.mk' "$makefile"; then
warn "Uses package.mk instead of luci.mk (may be intentional)"
else
error "Missing include for luci.mk"
if $DO_FIX; then
generate_makefile "$pkg_dir" "$pkg_name"
fi
fi
else
ok "Makefile valid with luci.mk"
fi
return 0
}
check_rpcd_script() {
local pkg_dir="$1"
local pkg_name="$2"
local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd"
local rpcd_script="$rpcd_dir/$pkg_name"
info "Checking RPCD script..."
# Check for rpcd directory
if [[ ! -d "$rpcd_dir" ]]; then
warn "RPCD directory missing: $rpcd_dir"
if $DO_FIX; then
mkdir -p "$rpcd_dir"
fixed "Created RPCD directory"
fi
fi
# Check for rpcd script
if [[ ! -f "$rpcd_script" ]]; then
error "RPCD script missing: $rpcd_script"
if $DO_FIX; then
generate_rpcd_script "$pkg_dir" "$pkg_name"
fi
return 1
fi
# Check if executable
if [[ ! -x "$rpcd_script" ]]; then
warn "RPCD script not executable"
if $DO_FIX; then
chmod +x "$rpcd_script"
fixed "Made RPCD script executable"
fi
fi
# Check shebang
local first_line=$(head -1 "$rpcd_script")
if [[ "$first_line" != "#!/bin/sh" && "$first_line" != "#!/bin/bash" ]]; then
warn "RPCD script missing proper shebang"
fi
# Check for required functions
if ! grep -q 'json_init\|json_add' "$rpcd_script"; then
warn "RPCD script may be missing JSON functions"
fi
# Check list and call handlers
if ! grep -q 'case.*list' "$rpcd_script"; then
error "RPCD script missing 'list' handler"
fi
if ! grep -q 'case.*call' "$rpcd_script"; then
error "RPCD script missing 'call' handler"
fi
# Check for status method
if ! grep -q 'status' "$rpcd_script"; then
warn "RPCD script missing 'status' method"
fi
ok "RPCD script exists"
return 0
}
check_acl_file() {
local pkg_dir="$1"
local pkg_name="$2"
local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d"
local acl_file="$acl_dir/luci-app-${pkg_name}.json"
info "Checking ACL file..."
# Check for acl directory
if [[ ! -d "$acl_dir" ]]; then
warn "ACL directory missing"
if $DO_FIX; then
mkdir -p "$acl_dir"
fixed "Created ACL directory"
fi
fi
# Check for acl file
if [[ ! -f "$acl_file" ]]; then
error "ACL file missing: $acl_file"
if $DO_FIX; then
generate_acl_file "$pkg_dir" "$pkg_name"
fi
return 1
fi
# Validate JSON
if ! validate_json "$acl_file"; then
error "ACL file has invalid JSON syntax"
if $DO_FIX; then
generate_acl_file "$pkg_dir" "$pkg_name"
fi
return 1
fi
# Check for correct ubus permission
local ubus_name="luci.$pkg_name"
if ! grep -q "$ubus_name" "$acl_file"; then
warn "ACL may be missing '$ubus_name' permission"
if $DO_FIX; then
generate_acl_file "$pkg_dir" "$pkg_name"
fi
fi
ok "ACL file valid"
return 0
}
check_menu_file() {
local pkg_dir="$1"
local pkg_name="$2"
local menu_dir="$pkg_dir/root/usr/share/luci/menu.d"
local menu_file="$menu_dir/luci-app-${pkg_name}.json"
info "Checking Menu file..."
# Check for menu directory
if [[ ! -d "$menu_dir" ]]; then
warn "Menu directory missing"
if $DO_FIX; then
mkdir -p "$menu_dir"
fixed "Created Menu directory"
fi
fi
# Check for menu file
if [[ ! -f "$menu_file" ]]; then
error "Menu file missing: $menu_file"
if $DO_FIX; then
generate_menu_file "$pkg_dir" "$pkg_name"
fi
return 1
fi
# Validate JSON
if ! validate_json "$menu_file"; then
error "Menu file has invalid JSON syntax"
if $DO_FIX; then
generate_menu_file "$pkg_dir" "$pkg_name"
fi
return 1
fi
ok "Menu file valid"
return 0
}
check_view_files() {
local pkg_dir="$1"
local pkg_name="$2"
local view_dir="$pkg_dir/htdocs/luci-static/resources/view"
info "Checking View files..."
# Check htdocs structure
if [[ ! -d "$pkg_dir/htdocs/luci-static/resources" ]]; then
warn "htdocs structure missing"
if $DO_FIX; then
mkdir -p "$pkg_dir/htdocs/luci-static/resources/view/${pkg_name//-/_}"
fixed "Created htdocs structure"
fi
fi
# Find JS view files
local js_files=$(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null | wc -l)
if [[ $js_files -eq 0 ]]; then
warn "No JavaScript view files found"
if $DO_FIX; then
generate_view_file "$pkg_dir" "$pkg_name"
fi
else
# Validate each JS file
while IFS= read -r js_file; do
if [[ -n "$js_file" ]]; then
if validate_js "$js_file"; then
verbose "Valid: $js_file"
else
error "Invalid JS syntax: $js_file"
fi
fi
done < <(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null)
ok "Found $js_files JavaScript view file(s)"
fi
return 0
}
check_config_file() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local config_dir="$pkg_dir/root/etc/config"
local config_file="$config_dir/$pkg_name_underscore"
info "Checking UCI config..."
if [[ ! -d "$config_dir" ]]; then
if $DO_FIX; then
mkdir -p "$config_dir"
fi
fi
if [[ ! -f "$config_file" ]]; then
warn "UCI config missing (optional)"
if $DO_FIX; then
generate_config_file "$pkg_dir" "$pkg_name"
fi
else
ok "UCI config exists"
fi
return 0
}
check_malformed_dirs() {
local pkg_dir="$1"
local pkg_name="$2"
info "Checking for malformed directories..."
# Check for {htdocs or other malformed dirs
local malformed=$(find "$pkg_dir" -type d -name '{*' 2>/dev/null)
if [[ -n "$malformed" ]]; then
error "Found malformed directories:"
echo "$malformed" | while read -r dir; do
echo " - $dir"
if $DO_FIX; then
rm -rf "$dir"
fixed "Removed: $dir"
fi
done
else
ok "No malformed directories"
fi
return 0
}
check_permissions() {
local pkg_dir="$1"
local pkg_name="$2"
info "Checking file permissions..."
local files_fixed=0
# RPCD scripts
while IFS= read -r script; do
if [[ -n "$script" && ! -x "$script" ]]; then
warn "Not executable: $script"
if $DO_FIX; then
chmod +x "$script"
((files_fixed++))
fi
fi
done < <(find "$pkg_dir" -path "*/usr/libexec/rpcd/*" -type f 2>/dev/null)
# Init scripts
while IFS= read -r script; do
if [[ -n "$script" && ! -x "$script" ]]; then
warn "Not executable: $script"
if $DO_FIX; then
chmod +x "$script"
((files_fixed++))
fi
fi
done < <(find "$pkg_dir" -path "*/etc/init.d/*" -type f 2>/dev/null)
# UCI defaults
while IFS= read -r script; do
if [[ -n "$script" && ! -x "$script" ]]; then
warn "Not executable: $script"
if $DO_FIX; then
chmod +x "$script"
((files_fixed++))
fi
fi
done < <(find "$pkg_dir" -path "*/etc/uci-defaults/*" -type f 2>/dev/null)
if [[ $files_fixed -gt 0 ]]; then
fixed "Fixed permissions on $files_fixed file(s)"
else
ok "Permissions OK"
fi
return 0
}
# ============================================
# Generate Functions
# ============================================
generate_makefile() {
local pkg_dir="$1"
local pkg_name="$2"
local makefile="$pkg_dir/Makefile"
# Get module info
local info="${MODULE_INFO[$pkg_name]}"
local title=$(echo "$info" | cut -d'|' -f1)
local desc=$(echo "$info" | cut -d'|' -f2)
local depends=$(echo "$info" | cut -d'|' -f3)
# Defaults if not in MODULE_INFO
title="${title:-SecuBox Module}"
desc="${desc:-SecuBox LuCI application}"
depends="${depends:-+luci-base +rpcd}"
cat > "$makefile" << MAKEFILE_EOF
include \$(TOPDIR)/rules.mk
PKG_NAME:=luci-app-${pkg_name}
PKG_VERSION:=${VERSION}
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
LUCI_TITLE:=LuCI - ${title}
LUCI_DESCRIPTION:=${desc}
LUCI_DEPENDS:=${depends}
LUCI_PKGARCH:=all
include \$(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot
MAKEFILE_EOF
fixed "Generated Makefile"
}
generate_rpcd_script() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd"
local rpcd_script="$rpcd_dir/$pkg_name"
mkdir -p "$rpcd_dir"
cat > "$rpcd_script" << 'RPCD_EOF'
#!/bin/sh
# RPCD backend for PKG_NAME_PLACEHOLDER
# Provides ubus interface: luci.PKG_NAME_PLACEHOLDER
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
# Configuration
MODULE_NAME="PKG_NAME_PLACEHOLDER"
MODULE_VERSION="VERSION_PLACEHOLDER"
UCI_CONFIG="PKG_NAME_UNDERSCORE_PLACEHOLDER"
# Initialize JSON output
json_init
case "$1" in
list)
# List available methods
json_add_object "status"
json_close_object
json_add_object "get_config"
json_close_object
json_add_object "set_config"
json_add_string "config" "object"
json_close_object
json_add_object "get_stats"
json_close_object
json_dump
;;
call)
case "$2" in
status)
# Return module status
json_add_string "module" "$MODULE_NAME"
json_add_string "version" "$MODULE_VERSION"
json_add_boolean "enabled" 1
json_add_string "status" "running"
json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)"
json_dump
;;
get_config)
# Return current configuration
json_add_object "config"
if [ -f "/etc/config/$UCI_CONFIG" ]; then
config_load "$UCI_CONFIG"
json_add_boolean "enabled" 1
else
json_add_boolean "enabled" 0
fi
json_close_object
json_dump
;;
set_config)
# Set configuration
read -r input
# Parse and apply config
# uci set $UCI_CONFIG...
# uci commit $UCI_CONFIG
json_add_boolean "success" 1
json_add_string "message" "Configuration updated"
json_dump
;;
get_stats)
# Return statistics
json_add_object "stats"
json_add_int "uptime" "$(cat /proc/uptime 2>/dev/null | cut -d. -f1 || echo 0)"
json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)"
json_close_object
json_dump
;;
*)
json_add_int "error" -32601
json_add_string "message" "Method not found: $2"
json_dump
;;
esac
;;
*)
echo "Usage: $0 {list|call}" >&2
exit 1
;;
esac
RPCD_EOF
# Replace placeholders
sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$rpcd_script"
sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$rpcd_script"
sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" "$rpcd_script"
chmod +x "$rpcd_script"
fixed "Generated RPCD script"
}
generate_acl_file() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d"
local acl_file="$acl_dir/luci-app-${pkg_name}.json"
mkdir -p "$acl_dir"
cat > "$acl_file" << ACL_EOF
{
"luci-app-${pkg_name}": {
"description": "Grant access to LuCI app ${pkg_name}",
"read": {
"ubus": {
"luci.${pkg_name}": [
"status",
"get_config",
"get_stats"
]
},
"uci": ["${pkg_name_underscore}"]
},
"write": {
"ubus": {
"luci.${pkg_name}": [
"set_config"
]
},
"uci": ["${pkg_name_underscore}"]
}
}
}
ACL_EOF
fixed "Generated ACL file"
}
generate_menu_file() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local menu_dir="$pkg_dir/root/usr/share/luci/menu.d"
local menu_file="$menu_dir/luci-app-${pkg_name}.json"
mkdir -p "$menu_dir"
# Generate title from pkg_name
local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
# Get from MODULE_INFO if available
if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then
title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1)
fi
cat > "$menu_file" << MENU_EOF
{
"admin/services/${pkg_name_underscore}": {
"title": "${title}",
"order": 50,
"action": {
"type": "view",
"path": "${pkg_name_underscore}/main"
},
"depends": {
"acl": ["luci-app-${pkg_name}"],
"uci": {
"${pkg_name_underscore}": true
}
}
}
}
MENU_EOF
fixed "Generated Menu file"
}
generate_view_file() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local view_dir="$pkg_dir/htdocs/luci-static/resources/view/${pkg_name_underscore}"
local view_file="$view_dir/main.js"
mkdir -p "$view_dir"
# Generate title
local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then
title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1)
fi
cat > "$view_file" << 'VIEW_EOF'
'use strict';
'require view';
'require rpc';
'require ui';
'require form';
var callStatus = rpc.declare({
object: 'luci.PKG_NAME_PLACEHOLDER',
method: 'status',
expect: { }
});
return view.extend({
load: function() {
return Promise.all([
callStatus()
]);
},
render: function(data) {
var status = data[0] || {};
var m, s, o;
m = new form.Map('PKG_NAME_UNDERSCORE_PLACEHOLDER', _('TITLE_PLACEHOLDER'),
_('DESCRIPTION_PLACEHOLDER'));
s = m.section(form.NamedSection, 'status', 'status', _('Status'));
s.anonymous = true;
o = s.option(form.DummyValue, '_status', _('Module Status'));
o.rawhtml = true;
o.cfgvalue = function() {
var running = status.status === 'running';
return '<span class="label ' + (running ? 'success' : 'danger') + '">' +
(running ? _('Running') : _('Stopped')) + '</span>';
};
o = s.option(form.DummyValue, '_version', _('Version'));
o.cfgvalue = function() {
return status.version || 'Unknown';
};
return m.render();
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});
VIEW_EOF
# Replace placeholders
sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$view_file"
sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$view_file"
sed -i "s/TITLE_PLACEHOLDER/$title/g" "$view_file"
sed -i "s/DESCRIPTION_PLACEHOLDER/SecuBox $title module/g" "$view_file"
fixed "Generated View file"
}
generate_config_file() {
local pkg_dir="$1"
local pkg_name="$2"
local pkg_name_underscore="${pkg_name//-/_}"
local config_dir="$pkg_dir/root/etc/config"
local config_file="$config_dir/$pkg_name_underscore"
mkdir -p "$config_dir"
cat > "$config_file" << CONFIG_EOF
config global 'global'
option enabled '1'
option version '${VERSION}'
CONFIG_EOF
fixed "Generated UCI config"
}
# ============================================
# Deploy Functions
# ============================================
deploy_to_router() {
local pkg_dir="$1"
local pkg_name="$2"
print_section "Deploying $pkg_name to router ($ROUTER_HOST)"
# Test SSH connection
if ! ssh -q -o ConnectTimeout=5 -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "echo ok" &>/dev/null; then
error "Cannot connect to router via SSH"
echo " Check: ROUTER_HOST=$ROUTER_HOST ROUTER_USER=$ROUTER_USER ROUTER_PORT=$ROUTER_PORT"
return 1
fi
ok "SSH connection OK"
# Create directories on router
info "Creating directories..."
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /usr/libexec/rpcd /usr/share/rpcd/acl.d /usr/share/luci/menu.d"
# Deploy RPCD script
local rpcd_script="$pkg_dir/root/usr/libexec/rpcd/$pkg_name"
if [[ -f "$rpcd_script" ]]; then
info "Deploying RPCD script..."
scp -P "$ROUTER_PORT" "$rpcd_script" "$ROUTER_USER@$ROUTER_HOST:/usr/libexec/rpcd/"
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "chmod +x /usr/libexec/rpcd/$pkg_name"
ok "RPCD script deployed"
fi
# Deploy ACL file
local acl_file="$pkg_dir/root/usr/share/rpcd/acl.d/luci-app-${pkg_name}.json"
if [[ -f "$acl_file" ]]; then
info "Deploying ACL file..."
scp -P "$ROUTER_PORT" "$acl_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/rpcd/acl.d/"
ok "ACL file deployed"
fi
# Deploy Menu file
local menu_file="$pkg_dir/root/usr/share/luci/menu.d/luci-app-${pkg_name}.json"
if [[ -f "$menu_file" ]]; then
info "Deploying Menu file..."
scp -P "$ROUTER_PORT" "$menu_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/luci/menu.d/"
ok "Menu file deployed"
fi
# Deploy View files
if [[ -d "$pkg_dir/htdocs" ]]; then
info "Deploying View files..."
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /www/luci-static/resources/view"
scp -r -P "$ROUTER_PORT" "$pkg_dir/htdocs/luci-static/resources/"* "$ROUTER_USER@$ROUTER_HOST:/www/luci-static/resources/" 2>/dev/null || true
ok "View files deployed"
fi
# Restart rpcd
info "Restarting rpcd..."
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "/etc/init.d/rpcd restart"
# Clear LuCI cache
info "Clearing LuCI cache..."
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "rm -rf /tmp/luci-*"
# Test
sleep 2
info "Testing ubus registration..."
if ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus list luci.$pkg_name" &>/dev/null; then
ok "luci.$pkg_name is registered!"
# Test status call
info "Testing status call..."
ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus call luci.$pkg_name status"
else
error "luci.$pkg_name is NOT registered"
echo " Check logs: ssh $ROUTER_USER@$ROUTER_HOST logread | grep rpcd"
fi
}
# ============================================
# Main Check Function
# ============================================
check_module() {
local pkg_dir="$1"
local pkg_name=$(basename "$pkg_dir" | sed 's/^luci-app-//')
print_module "luci-app-$pkg_name"
((MODULES_CHECKED++))
# Run all checks
check_malformed_dirs "$pkg_dir" "$pkg_name"
check_makefile "$pkg_dir" "$pkg_name"
check_rpcd_script "$pkg_dir" "$pkg_name"
check_acl_file "$pkg_dir" "$pkg_name"
check_menu_file "$pkg_dir" "$pkg_name"
check_view_files "$pkg_dir" "$pkg_name"
check_config_file "$pkg_dir" "$pkg_name"
check_permissions "$pkg_dir" "$pkg_name"
# Deploy if requested
if $DO_DEPLOY; then
deploy_to_router "$pkg_dir" "$pkg_name"
fi
}
# ============================================
# Summary
# ============================================
print_summary() {
print_header "Summary"
echo ""
echo -e " ${BOLD}Modules checked:${NC} $MODULES_CHECKED"
echo -e " ${GREEN}Fixes applied:${NC} $FIXES"
echo -e " ${YELLOW}Warnings:${NC} $WARNINGS"
echo -e " ${RED}Errors:${NC} $ERRORS"
echo ""
if [[ $ERRORS -gt 0 ]]; then
echo -e " ${RED}⚠ Some errors remain. Run with --fix to repair.${NC}"
elif [[ $WARNINGS -gt 0 ]]; then
echo -e " ${YELLOW}⚠ Some warnings. Review and fix manually if needed.${NC}"
else
echo -e " ${GREEN}✓ All modules validated successfully!${NC}"
fi
echo ""
if ! $DO_DEPLOY && $DO_FIX; then
echo -e " ${CYAN}Tip: Use --deploy to push fixes to router${NC}"
echo ""
fi
}
# ============================================
# Help
# ============================================
show_help() {
cat << EOF
${BOLD}SecuBox Module Repair Tool v${VERSION}${NC}
Usage: $0 [OPTIONS] [MODULE_NAME]
Options:
--fix Apply automatic fixes
--deploy Deploy fixed modules to router (requires SSH)
--verbose Show detailed output
--help Show this help
Environment Variables:
ROUTER_HOST Router IP address (default: 192.168.1.1)
ROUTER_USER SSH user (default: root)
ROUTER_PORT SSH port (default: 22)
Examples:
$0 Check all modules
$0 --fix Check and fix all modules
$0 --fix vhost-manager Fix specific module
$0 --fix --deploy Fix and deploy to router
ROUTER_HOST=192.168.8.191 $0 --fix --deploy
EOF
}
# ============================================
# Main
# ============================================
main() {
# Parse arguments
local target_module=""
while [[ $# -gt 0 ]]; do
case "$1" in
--fix)
DO_FIX=true
shift
;;
--deploy)
DO_DEPLOY=true
shift
;;
--verbose|-v)
VERBOSE=true
shift
;;
--help|-h)
show_help
exit 0
;;
-*)
echo "Unknown option: $1"
show_help
exit 1
;;
*)
target_module="$1"
shift
;;
esac
done
# Header
print_header "SecuBox Module Repair Tool v${VERSION}"
echo ""
echo -e " ${CYAN}Working directory:${NC} $WORK_DIR"
echo -e " ${CYAN}Fix mode:${NC} $($DO_FIX && echo 'ON' || echo 'OFF')"
echo -e " ${CYAN}Deploy mode:${NC} $($DO_DEPLOY && echo 'ON' || echo 'OFF')"
if $DO_DEPLOY; then
echo -e " ${CYAN}Router:${NC} $ROUTER_USER@$ROUTER_HOST:$ROUTER_PORT"
fi
# Find modules
cd "$WORK_DIR"
if [[ -n "$target_module" ]]; then
# Check specific module
local pkg_dir="$WORK_DIR/luci-app-$target_module"
if [[ ! -d "$pkg_dir" ]]; then
pkg_dir="$WORK_DIR/$target_module"
fi
if [[ -d "$pkg_dir" ]]; then
check_module "$pkg_dir"
else
error "Module not found: $target_module"
exit 1
fi
else
# Check all modules
for pkg_dir in luci-app-*/; do
if [[ -d "$pkg_dir" ]]; then
check_module "${pkg_dir%/}"
fi
done
fi
# Summary
print_summary
# Exit code
if [[ $ERRORS -gt 0 ]]; then
exit 1
fi
exit 0
}
# Run
main "$@"