#!/bin/sh # SecuBox LXC helper: create/start/stop LXC containers following SecuBox conventions set -eu CONFIG="/etc/config/lxcapps" STORAGE_ROOT="/srv/lxc" TEMPLATE_DIR="${SECUBOX_LXC_TEMPLATES:-/usr/share/secubox/lxc/templates}" usage() { cat <<'USAGE' SecuBox LXC Manager Usage: secubox-lxc [options] Commands: list Show containers defined in /etc/config/lxcapps show Display config values for a container create [options] Create container (downloads template if needed) start Start container via lxc-start stop Stop container via lxc-stop delete Stop and remove container directory status Show runtime state Options (create): --template PATH Rootfs tarball or template script (default debian) --bridge BRIDGE Bridge to attach (default br-lan) --ip ADDRESS Static IP address (optional) --gateway ADDRESS Gateway (optional) --dns ADDRESS DNS server (optional) --memory MB Memory limit (optional) Examples: secubox-lxc list secubox-lxc create secubox-lyrion --bridge br-dmz --ip 192.168.50.10 secubox-lxc start secubox-lyrion USAGE } require_tool() { command -v "$1" >/dev/null 2>&1 || { echo "[ERROR] Missing dependency: $1" >&2; exit 1; } } ensure_storage() { mkdir -p "$STORAGE_ROOT" } uci_get_container() { local name="$1" option="$2" uci -q get lxcapps."$name"."$2" } uci_set_container() { local name="$1" option="$2" value="$3" uci set lxcapps."$name"."$option"="$value" } list_containers() { uci -q show lxcapps 2>/dev/null | grep '=container$' | cut -d. -f2 | cut -d= -f1 } cmd_list() { require_tool lxc-info ensure_storage printf '%-18s %-20s %-10s %-16s\n' "Name" "Bridge" "State" "RootFS" printf '%-18s %-20s %-10s %-16s\n' "----" "------" "-----" "------" for name in $(list_containers); do local bridge rootfs state bridge=$(uci_get_container "$name" bridge || printf 'br-lan') rootfs="$STORAGE_ROOT/$name/rootfs" if lxc-info -n "$name" >/dev/null 2>&1; then state=$(lxc-info -n "$name" -s 2>/dev/null | awk '{print $2}') else state="N/A" fi printf '%-18s %-20s %-10s %-16s\n' "$name" "$bridge" "$state" "$rootfs" done } cmd_show() { local name="$1" uci -q show lxcapps."$name" } cmd_create() { require_tool lxc-create local name="$1"; shift local template="debian" local bridge="br-lan" local ip="" local gateway="" local dns="" local memory="" while [ $# -gt 0 ]; do case "$1" in --template) template="$2"; shift 2;; --bridge) bridge="$2"; shift 2;; --ip) ip="$2"; shift 2;; --gateway) gateway="$2"; shift 2;; --dns) dns="$2"; shift 2;; --memory) memory="$2"; shift 2;; *) echo "Unknown option: $1" >&2; exit 1;; esac done [ -n "$name" ] || { echo "Container name required" >&2; exit 1; } ensure_storage local container_dir="$STORAGE_ROOT/$name" if [ -d "$container_dir" ]; then echo "[WARN] Container directory already exists, skipping rootfs creation." else mkdir -p "$container_dir" local tmpl_path="$template" if [ -f "$TEMPLATE_DIR/$template" ]; then tmpl_path="$TEMPLATE_DIR/$template" fi lxc-create -n "$name" -P "$STORAGE_ROOT" -t "$tmpl_path" fi uci batch <<-EOF set lxcapps.$name=container set lxcapps.$name.bridge='$bridge' EOF [ -n "$ip" ] && uci_set_container "$name" ip "$ip" [ -n "$gateway" ] && uci_set_container "$name" gateway "$gateway" [ -n "$dns" ] && uci_set_container "$name" dns "$dns" [ -n "$memory" ] && uci_set_container "$name" memory "$memory" uci commit lxcapps cat </dev/null 2>&1 || true lxc-destroy -n "$name" rm -rf "$STORAGE_ROOT/$name" uci delete lxcapps."$name" uci commit lxcapps } cmd_status() { require_tool lxc-info local name="$1" lxc-info -n "$name" } case "${1:-}" in list) shift; cmd_list "$@" ;; show) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_show "$1" ;; create) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_create "$@" ;; start) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_start "$1" ;; stop) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_stop "$1" ;; delete) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_delete "$1" ;; status) shift; [ $# -ge 1 ] || { usage; exit 1; }; cmd_status "$1" ;; help|--help|-h|'') usage ;; *) usage; exit 1 ;; esac