secubox-openwrt/secubox-tools/secubox-app

219 lines
5.4 KiB
Bash
Executable File

#!/bin/sh
# SecuBox Apps CLI
# Lists/installs/removes plugin manifests declared under plugins/*/manifest.json
set -eu
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
DEFAULT_PLUGINS_DIR="/usr/share/secubox/plugins"
if [ -n "${SECUBOX_PLUGINS_DIR:-}" ]; then
PLUGINS_DIR="$SECUBOX_PLUGINS_DIR"
elif [ -d "$SCRIPT_DIR/../plugins" ]; then
PLUGINS_DIR=$(cd "$SCRIPT_DIR/../plugins" && pwd)
else
PLUGINS_DIR="$DEFAULT_PLUGINS_DIR"
fi
OPKG_UPDATED=0
info() { printf '[INFO] %s\n' "$*"; }
warn() { printf '[WARN] %s\n' "$*" >&2; }
err() { printf '[ERROR] %s\n' "$*" >&2; }
usage() {
cat <<'USAGE'
SecuBox Apps CLI
Usage: secubox-app <command> [arguments]
Commands:
list Show available app manifests
show <app-id> Display manifest details
install <app-id> Install required packages + run install action
remove <app-id> Remove packages listed in manifest
status <app-id> Show install state and run status action if defined
update <app-id> Run plugin update action or opkg upgrade
Environment:
SECUBOX_PLUGINS_DIR Override manifest directory (default: /usr/share/secubox/plugins)
USAGE
}
require_tool() {
command -v "$1" >/dev/null 2>&1 || { err "Missing dependency: $1"; exit 1; }
}
require_opkg() { require_tool opkg; }
require_jsonfilter() { require_tool jsonfilter; }
manifest_path() {
local id="$1"
local file="$PLUGINS_DIR/$id/manifest.json"
[ -f "$file" ] || { err "Manifest not found for '$id' ($file)"; exit 1; }
printf '%s' "$file"
}
manifest_field() {
local file="$1"; shift
jsonfilter -s "$file" -e "$1" 2>/dev/null || true
}
manifest_packages() {
local file="$1"
jsonfilter -s "$file" -e '@.packages[*]' 2>/dev/null || true
}
manifest_action() {
local file="$1"; shift
jsonfilter -s "$file" -e "@.actions.$1" 2>/dev/null || true
}
ensure_opkg_updated() {
[ "$OPKG_UPDATED" -eq 1 ] && return
opkg update >/dev/null
OPKG_UPDATED=1
}
packages_installed() {
local all_installed=0
local partial=0
local pkg
for pkg in $1; do
if opkg status "$pkg" >/dev/null 2>&1; then
continue
else
all_installed=1
fi
[ -n "$partial" ]
done
}
plugin_state() {
local file="$1"
local pkgs pkg missing=0 installed=0 total=0
pkgs=$(manifest_packages "$file")
for pkg in $pkgs; do
total=$((total+1))
if opkg status "$pkg" >/dev/null 2>&1; 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
}
list_plugins() {
require_jsonfilter
require_opkg
printf '%-16s %-22s %-10s %-10s\n' "ID" "Name" "Type" "State"
printf '%-16s %-22s %-10s %-10s\n' "--" "----" "----" "-----"
find "$PLUGINS_DIR" -mindepth 2 -maxdepth 2 -name manifest.json | sort | while read -r file; do
local id name type state
id=$(manifest_field "$file" '@.id')
name=$(manifest_field "$file" '@.name')
type=$(manifest_field "$file" '@.type')
state=$(plugin_state "$file")
printf '%-16s %-22s %-10s %-10s\n' "$id" "${name:-Unknown}" "${type:-?}" "$state"
done
}
show_manifest() {
require_jsonfilter
local file=$(manifest_path "$1")
cat "$file"
}
install_plugin() {
require_jsonfilter
require_opkg
local file=$(manifest_path "$1")
local pkgs pkg
pkgs=$(manifest_packages "$file")
if [ -z "$pkgs" ]; then
warn "Manifest has no packages; nothing to install."
else
for pkg in $pkgs; do
if opkg status "$pkg" >/dev/null 2>&1; then
info "$pkg already installed"
else
info "Installing $pkg"
ensure_opkg_updated
opkg install "$pkg"
fi
done
fi
local install_cmd
install_cmd=$(manifest_action "$file" install)
if [ -n "$install_cmd" ]; then
info "Running install action: $install_cmd"
sh -c "$install_cmd"
fi
}
remove_plugin() {
require_jsonfilter
require_opkg
local file=$(manifest_path "$1")
local pkgs pkg
pkgs=$(manifest_packages "$file")
for pkg in $pkgs; do
if opkg status "$pkg" >/dev/null 2>&1; then
info "Removing $pkg"
opkg remove "$pkg"
else
info "$pkg not installed"
fi
done
}
plugin_status_cmd() {
require_jsonfilter
local file=$(manifest_path "$1")
local status_cmd
status_cmd=$(manifest_action "$file" status)
local state
state=$(plugin_state "$file")
printf 'App: %s\nState: %s\n' "$1" "$state"
if [ -n "$status_cmd" ]; then
printf 'Status command output:\n'
sh -c "$status_cmd" || true
fi
}
update_plugin() {
require_jsonfilter
require_opkg
local file=$(manifest_path "$1")
local update_cmd pkgs pkg
update_cmd=$(manifest_action "$file" update)
if [ -n "$update_cmd" ]; then
info "Running update action: $update_cmd"
sh -c "$update_cmd"
else
pkgs=$(manifest_packages "$file")
for pkg in $pkgs; do
info "Upgrading $pkg"
ensure_opkg_updated
opkg upgrade "$pkg" || true
done
fi
}
case "${1:-}" in
list) shift; list_plugins ;;
show) shift; [ $# -ge 1 ] || { err "show requires an app id"; exit 1; }; show_manifest "$1" ;;
install) shift; [ $# -ge 1 ] || { err "install requires an app id"; exit 1; }; install_plugin "$1" ;;
remove) shift; [ $# -ge 1 ] || { err "remove requires an app id"; exit 1; }; remove_plugin "$1" ;;
status) shift; [ $# -ge 1 ] || { err "status requires an app id"; exit 1; }; plugin_status_cmd "$1" ;;
update) shift; [ $# -ge 1 ] || { err "update requires an app id"; exit 1; }; update_plugin "$1" ;;
help|--help|-h|'') usage ;;
*) err "Unknown command: $1"; usage; exit 1 ;;
esac