secubox-openwrt/package/secubox/secubox-vortex-dns/files/usr/sbin/vortexctl
CyberMind-FR 592e46bde8 fix(vortex-dns): Fix shell glob syntax error in zone list
Remove 2>/dev/null from for-loop glob pattern which causes syntax
error in BusyBox ash shell. The [ -f "$zf" ] check handles the
case when no zone files exist.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-08 09:11:05 +01:00

847 lines
23 KiB
Bash

#!/bin/sh
# SecuBox Vortex DNS Controller
# Meshed multi-dynamic subdomain delegation system
#
# Copyright (C) 2026 CyberMind.fr
CONFIG="vortex-dns"
LIB_DIR="/usr/lib/vortex-dns"
STATE_DIR="/var/lib/vortex-dns"
LOG_TAG="vortex-dns"
# Source libraries
[ -f "$LIB_DIR/master.sh" ] && . "$LIB_DIR/master.sh"
[ -f "$LIB_DIR/slave.sh" ] && . "$LIB_DIR/slave.sh"
[ -f "$LIB_DIR/gossip.sh" ] && . "$LIB_DIR/gossip.sh"
usage() {
cat <<'EOF'
Usage: vortexctl <command> [options]
Master Commands:
master init <wildcard_domain> Initialize as master node
master delegate <node> <zone> Delegate subzone to slave node
master revoke <zone> Revoke zone delegation
master list-slaves List delegated zones
Slave Commands:
slave join <master_ip> <token> Join as slave to master
slave leave Leave master hierarchy
slave status Show slave status
Mesh Commands:
mesh status Show mesh DNS status
mesh sync Force sync with peers
mesh publish <service> <domain> Publish service to mesh
mesh unpublish <service> Remove from mesh
Submaster Commands:
submaster promote Promote to submaster role
submaster demote Demote to regular slave
Zone Commands:
zone list List managed zones
zone dump <domain> Dump zone from external DNS
zone import <domain> Import zone and become authoritative
zone export <domain> Export zone file to stdout
zone reload [domain] Reload zone(s) in dnsmasq
Secondary DNS:
secondary list List secondary DNS providers
secondary add <provider> <domain> Configure provider as secondary
secondary remove <provider> <domain> Remove secondary configuration
General:
status Show overall status
daemon Run sync daemon
Configuration: /etc/config/vortex-dns
EOF
}
log_info() { logger -t "$LOG_TAG" "$*"; echo "[INFO] $*"; }
log_warn() { logger -t "$LOG_TAG" -p warning "$*"; echo "[WARN] $*" >&2; }
log_error() { logger -t "$LOG_TAG" -p err "$*"; echo "[ERROR] $*" >&2; }
uci_get() { uci -q get "${CONFIG}.$1"; }
uci_set() { uci set "${CONFIG}.$1=$2"; }
load_config() {
enabled=$(uci_get main.enabled)
mode=$(uci_get main.mode)
sync_interval=$(uci_get main.sync_interval)
master_enabled=$(uci_get master.enabled)
wildcard_domain=$(uci_get master.wildcard_domain)
dns_provider=$(uci_get master.dns_provider)
slave_enabled=$(uci_get slave.enabled)
parent_master=$(uci_get slave.parent_master)
delegated_zone=$(uci_get slave.delegated_zone)
gossip_enabled=$(uci_get mesh.gossip_enabled)
first_peek=$(uci_get mesh.first_peek)
auto_register=$(uci_get mesh.auto_register)
[ -z "$sync_interval" ] && sync_interval=300
mkdir -p "$STATE_DIR"
}
# =============================================================================
# STATUS
# =============================================================================
cmd_status() {
load_config
echo "=== Vortex DNS Status ==="
echo ""
echo "Enabled: $([ "$enabled" = "1" ] && echo "Yes" || echo "No")"
echo "Mode: ${mode:-standalone}"
echo "Sync Interval: ${sync_interval}s"
echo ""
if [ "$master_enabled" = "1" ]; then
echo "=== Master Node ==="
echo "Wildcard Domain: ${wildcard_domain:-not set}"
echo "DNS Provider: ${dns_provider:-not set}"
# Count delegated zones
local zones=$(uci show "$CONFIG" 2>/dev/null | grep -c "=delegation")
echo "Delegated Zones: $zones"
echo ""
fi
if [ "$slave_enabled" = "1" ]; then
echo "=== Slave Node ==="
echo "Parent Master: ${parent_master:-not set}"
echo "Delegated Zone: ${delegated_zone:-not set}"
echo ""
fi
echo "=== Mesh Status ==="
echo "Gossip: $([ "$gossip_enabled" = "1" ] && echo "Enabled" || echo "Disabled")"
echo "First Peek: $([ "$first_peek" = "1" ] && echo "Enabled" || echo "Disabled")"
echo "Auto Register: $([ "$auto_register" = "1" ] && echo "Enabled" || echo "Disabled")"
# Count mesh peers
if command -v secubox-p2p >/dev/null 2>&1; then
local peers=$(secubox-p2p peers 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
echo "Mesh Peers: $peers"
fi
# Count published services
if [ -f "$STATE_DIR/published.json" ]; then
local services=$(jsonfilter -i "$STATE_DIR/published.json" -e '@[*]' 2>/dev/null | wc -l)
echo "Published Services: $services"
fi
}
# =============================================================================
# MASTER COMMANDS
# =============================================================================
cmd_master_init() {
local domain="$1"
[ -z "$domain" ] && {
echo "Usage: vortexctl master init <wildcard_domain>"
echo "Example: vortexctl master init secubox.io"
exit 1
}
load_config
log_info "Initializing as master for *.$domain"
# Get DNS provider from dns-provider config
local provider=$(uci -q get dns-provider.main.provider)
[ -z "$provider" ] && {
log_error "DNS provider not configured. Run: uci set dns-provider.main.provider=<ovh|gandi|cloudflare>"
exit 1
}
# Configure master mode
uci_set master.enabled 1
uci_set master.wildcard_domain "$domain"
uci_set master.dns_provider "$provider"
uci_set main.mode "master"
uci commit "$CONFIG"
# Create wildcard DNS record pointing to this node
local wan_ip=$(curl -s -4 ifconfig.me 2>/dev/null || wget -q -O - ifconfig.me 2>/dev/null)
if [ -n "$wan_ip" ]; then
log_info "Creating wildcard A record: *.$domain -> $wan_ip"
dnsctl add A "*" "$wan_ip" 300 2>/dev/null || log_warn "Failed to create wildcard record"
fi
# Generate master token for slave enrollment
local master_token=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 32)
echo "$master_token" > "$STATE_DIR/master_token"
chmod 600 "$STATE_DIR/master_token"
log_info "Master initialized for *.$domain"
echo ""
echo "Slave enrollment token: $master_token"
echo "Slaves can join with: vortexctl slave join $(hostname -I | awk '{print $1}') $master_token"
}
cmd_master_delegate() {
local node="$1"
local zone="$2"
[ -z "$node" ] || [ -z "$zone" ] && {
echo "Usage: vortexctl master delegate <node_ip> <subzone>"
echo "Example: vortexctl master delegate 192.168.1.100 node1"
exit 1
}
load_config
[ "$master_enabled" != "1" ] && {
log_error "Not configured as master. Run: vortexctl master init <domain>"
exit 1
}
local fqdn="${zone}.${wildcard_domain}"
log_info "Delegating $fqdn to $node"
# Create NS record for delegation
dnsctl add NS "$zone" "ns1.${fqdn}" 2>/dev/null
dnsctl add A "ns1.${zone}" "$node" 2>/dev/null
# Store delegation in UCI
local section="delegation_$(echo "$zone" | tr '.-' '__')"
uci set "${CONFIG}.${section}=delegation"
uci set "${CONFIG}.${section}.zone=$zone"
uci set "${CONFIG}.${section}.node=$node"
uci set "${CONFIG}.${section}.fqdn=$fqdn"
uci set "${CONFIG}.${section}.created=$(date -Iseconds)"
uci commit "$CONFIG"
# Notify slave via mesh
if [ "$gossip_enabled" = "1" ]; then
secubox-p2p publish "dns-delegation" "0" "zone=$fqdn,node=$node" 2>/dev/null
fi
log_info "Delegated $fqdn to $node"
}
cmd_master_list_slaves() {
load_config
echo "=== Delegated Zones ==="
echo ""
uci show "$CONFIG" 2>/dev/null | grep "=delegation" | while read -r line; do
local section=$(echo "$line" | cut -d= -f1 | cut -d. -f2)
local zone=$(uci_get "${section}.zone")
local node=$(uci_get "${section}.node")
local fqdn=$(uci_get "${section}.fqdn")
local created=$(uci_get "${section}.created")
echo "Zone: $zone"
echo " FQDN: $fqdn"
echo " Node: $node"
echo " Created: $created"
echo ""
done
}
# =============================================================================
# SLAVE COMMANDS
# =============================================================================
cmd_slave_join() {
local master_ip="$1"
local token="$2"
[ -z "$master_ip" ] || [ -z "$token" ] && {
echo "Usage: vortexctl slave join <master_ip> <enrollment_token>"
exit 1
}
load_config
log_info "Joining master at $master_ip..."
# Verify token with master (via mesh API)
local verify=$(wget -q -O - --timeout=10 "http://${master_ip}:7331/api/vortex/verify?token=$token" 2>/dev/null)
if [ -z "$verify" ]; then
log_warn "Could not verify with master, proceeding anyway..."
fi
# Configure slave mode
uci_set slave.enabled 1
uci_set slave.parent_master "$master_ip"
uci_set slave.sync_key "$token"
uci_set main.mode "slave"
# Request zone assignment from master
local hostname=$(uci -q get system.@system[0].hostname || hostname)
local my_ip=$(hostname -I | awk '{print $1}')
# Ask master to delegate a zone for us
local zone_response=$(wget -q -O - --timeout=10 \
"http://${master_ip}:7331/api/vortex/request-zone?hostname=$hostname&ip=$my_ip&token=$token" 2>/dev/null)
if [ -n "$zone_response" ]; then
local assigned_zone=$(echo "$zone_response" | jsonfilter -e '@.zone' 2>/dev/null)
if [ -n "$assigned_zone" ]; then
uci_set slave.delegated_zone "$assigned_zone"
log_info "Assigned zone: $assigned_zone"
fi
fi
uci commit "$CONFIG"
log_info "Joined master at $master_ip"
cmd_status
}
cmd_slave_status() {
load_config
[ "$slave_enabled" != "1" ] && {
echo "Not configured as slave"
exit 0
}
echo "=== Slave Status ==="
echo "Parent Master: $parent_master"
echo "Delegated Zone: ${delegated_zone:-pending}"
# Check connectivity to master
if wget -q -O /dev/null --timeout=3 "http://${parent_master}:7331/api/status" 2>/dev/null; then
echo "Master Connectivity: OK"
else
echo "Master Connectivity: FAILED"
fi
}
# =============================================================================
# MESH COMMANDS
# =============================================================================
cmd_mesh_sync() {
load_config
log_info "Syncing with mesh peers..."
# Get all exposure entries from peers
if command -v secubox-p2p >/dev/null 2>&1; then
secubox-p2p sync 2>/dev/null
fi
# Sync DNS records based on mesh catalog
if [ -f "/tmp/secubox-p2p-services.json" ]; then
jsonfilter -i /tmp/secubox-p2p-services.json -e '@[*]' 2>/dev/null | while read -r svc; do
local name=$(echo "$svc" | jsonfilter -e '@.name' 2>/dev/null)
local domain=$(echo "$svc" | jsonfilter -e '@.domain' 2>/dev/null)
local ip=$(echo "$svc" | jsonfilter -e '@.ip' 2>/dev/null)
if [ -n "$domain" ] && [ -n "$ip" ]; then
log_info "Mesh sync: $domain -> $ip"
# Update local DNS cache/records as needed
fi
done
fi
date -Iseconds > "$STATE_DIR/last_sync"
log_info "Mesh sync complete"
}
cmd_mesh_publish() {
local service="$1"
local domain="$2"
[ -z "$service" ] || [ -z "$domain" ] && {
echo "Usage: vortexctl mesh publish <service> <domain>"
exit 1
}
load_config
log_info "Publishing $service as $domain to mesh..."
# Use secubox-p2p to publish
if command -v secubox-p2p >/dev/null 2>&1; then
secubox-p2p publish "$service" "0" "domain=$domain" 2>/dev/null
fi
# Store locally
mkdir -p "$STATE_DIR"
local entry="{\"service\":\"$service\",\"domain\":\"$domain\",\"published\":\"$(date -Iseconds)\"}"
if [ -f "$STATE_DIR/published.json" ]; then
# Append to existing
sed -i "s/\]$/,$entry]/" "$STATE_DIR/published.json"
else
echo "[$entry]" > "$STATE_DIR/published.json"
fi
log_info "Published $service as $domain"
}
cmd_mesh_status() {
load_config
echo "=== Mesh DNS Status ==="
echo ""
echo "Gossip: $([ "$gossip_enabled" = "1" ] && echo "Enabled" || echo "Disabled")"
echo "First Peek: $([ "$first_peek" = "1" ] && echo "Enabled" || echo "Disabled")"
echo "Auto Register: $([ "$auto_register" = "1" ] && echo "Enabled" || echo "Disabled")"
echo ""
if [ -f "$STATE_DIR/last_sync" ]; then
echo "Last Sync: $(cat "$STATE_DIR/last_sync")"
fi
echo ""
echo "=== Published Services ==="
if [ -f "$STATE_DIR/published.json" ]; then
jsonfilter -i "$STATE_DIR/published.json" -e '@[*]' 2>/dev/null | while read -r pub; do
local svc=$(echo "$pub" | jsonfilter -e '@.service' 2>/dev/null)
local dom=$(echo "$pub" | jsonfilter -e '@.domain' 2>/dev/null)
echo " $svc -> $dom"
done
else
echo " (none)"
fi
}
# =============================================================================
# ZONE COMMANDS
# =============================================================================
ZONE_DIR="/srv/dns/zones"
DNSMASQ_VORTEX_DIR="/etc/dnsmasq.d/vortex"
cmd_zone_list() {
echo "=== Managed Zones ==="
echo ""
mkdir -p "$ZONE_DIR"
# List zones from UCI
uci show "$CONFIG" 2>/dev/null | grep "=zone" | while read -r line; do
local section=$(echo "$line" | cut -d= -f1 | cut -d. -f2)
local domain=$(uci_get "${section}.domain")
local enabled=$(uci_get "${section}.enabled")
local file=$(uci_get "${section}.file")
[ -z "$domain" ] && continue
local status="disabled"
[ "$enabled" = "1" ] && status="enabled"
local records=0
[ -f "$file" ] && records=$(grep -c "^[^;$]" "$file" 2>/dev/null || echo 0)
printf "%-25s %3d records [%s]\n" "$domain" "$records" "$status"
done
# Also list zone files not in UCI
for zf in "$ZONE_DIR"/*.zone; do
[ -f "$zf" ] || continue
local domain=$(basename "$zf" .zone)
if ! uci show "$CONFIG" 2>/dev/null | grep -q "domain='$domain'"; then
local records=$(grep -c "^[^;$]" "$zf" 2>/dev/null || echo 0)
printf "%-25s %3d records [not configured]\n" "$domain" "$records"
fi
done
}
cmd_zone_dump() {
local domain="$1"
[ -z "$domain" ] && {
echo "Usage: vortexctl zone dump <domain>"
echo "Example: vortexctl zone dump maegia.tv"
exit 1
}
mkdir -p "$ZONE_DIR"
local output="$ZONE_DIR/${domain}.zone"
local serial=$(date +%Y%m%d)01
log_info "Dumping zone $domain..."
# Get SOA info
local soa=$(dig +short "$domain" SOA 2>/dev/null | head -1)
local primary_ns=$(echo "$soa" | awk '{print $1}')
local hostmaster=$(echo "$soa" | awk '{print $2}')
[ -z "$hostmaster" ] && hostmaster="hostmaster.${domain}."
[ -z "$primary_ns" ] && primary_ns="ns1.${domain}."
# Start zone file
cat > "$output" << EOF
\$ORIGIN ${domain}.
\$TTL 3600
; Zone file for ${domain}
; Generated by vortexctl on $(date -Iseconds)
; Source: External DNS query
@ IN SOA ${primary_ns} ${hostmaster} (
${serial} ; serial
10800 ; refresh (3 hours)
3600 ; retry (1 hour)
604800 ; expire (1 week)
10800 ) ; minimum (3 hours)
; NS records
EOF
# Get NS records
dig +short "$domain" NS 2>/dev/null | while read -r ns; do
printf "@ IN NS %s\n" "$ns" >> "$output"
done
echo "" >> "$output"
echo "; MX records" >> "$output"
# Get MX records
dig +short "$domain" MX 2>/dev/null | while read -r mx; do
local prio=$(echo "$mx" | awk '{print $1}')
local server=$(echo "$mx" | awk '{print $2}')
printf "@ IN MX %s %s\n" "$prio" "$server" >> "$output"
done
echo "" >> "$output"
echo "; TXT records" >> "$output"
# Get TXT records
dig +short "$domain" TXT 2>/dev/null | while read -r txt; do
printf "@ IN TXT %s\n" "$txt" >> "$output"
done
echo "" >> "$output"
echo "; A records" >> "$output"
# Get root A record
local root_ip=$(dig +short "$domain" A 2>/dev/null | head -1)
[ -n "$root_ip" ] && printf "@ IN A %s\n" "$root_ip" >> "$output"
# Get subdomains from HAProxy vhosts
local subs=$(uci show haproxy 2>/dev/null | grep -oE "[a-z0-9_-]+\\.${domain}" | sed "s/\\.${domain}//" | sort -u)
echo "" >> "$output"
echo "; Subdomains (from HAProxy vhosts)" >> "$output"
for sub in $subs; do
[ "$sub" = "@" ] && continue
local ip=$(dig +short "${sub}.${domain}" A 2>/dev/null | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$" | head -1)
if [ -n "$ip" ]; then
printf "%-20s IN A %s\n" "$sub" "$ip" >> "$output"
fi
done
local count=$(grep -c "^[^;$]" "$output" 2>/dev/null || echo 0)
log_info "Zone dumped to $output ($count records)"
echo ""
echo "Zone file: $output"
}
cmd_zone_import() {
local domain="$1"
[ -z "$domain" ] && {
echo "Usage: vortexctl zone import <domain>"
exit 1
}
# First dump the zone
cmd_zone_dump "$domain"
local zone_file="$ZONE_DIR/${domain}.zone"
[ ! -f "$zone_file" ] && {
log_error "Zone file not created"
exit 1
}
log_info "Configuring $domain as authoritative zone..."
# Create UCI config
local section="zone_$(echo "$domain" | tr '.-' '__')"
uci set "${CONFIG}.${section}=zone"
uci set "${CONFIG}.${section}.domain=$domain"
uci set "${CONFIG}.${section}.file=$zone_file"
uci set "${CONFIG}.${section}.enabled=1"
uci set "${CONFIG}.${section}.authoritative=1"
uci commit "$CONFIG"
# Create dnsmasq config
mkdir -p "$DNSMASQ_VORTEX_DIR"
local wan_if=$(uci -q get network.wan.device || echo "eth0")
cat > "${DNSMASQ_VORTEX_DIR}/${domain}.conf" << EOF
# Authoritative zone: ${domain}
# Generated by vortexctl
auth-zone=${domain}
auth-server=ns1.${domain},${wan_if}
EOF
# Reload dnsmasq
/etc/init.d/dnsmasq reload 2>/dev/null
log_info "Zone $domain imported and configured"
echo ""
echo "Zone file: $zone_file"
echo "Dnsmasq config: ${DNSMASQ_VORTEX_DIR}/${domain}.conf"
echo ""
echo "To test: dig @127.0.0.1 ${domain}"
}
cmd_zone_export() {
local domain="$1"
[ -z "$domain" ] && {
echo "Usage: vortexctl zone export <domain>"
exit 1
}
local zone_file="$ZONE_DIR/${domain}.zone"
[ ! -f "$zone_file" ] && {
log_error "Zone file not found: $zone_file"
echo "Run 'vortexctl zone dump $domain' first"
exit 1
}
cat "$zone_file"
}
cmd_zone_reload() {
local domain="$1"
if [ -n "$domain" ]; then
log_info "Reloading zone $domain..."
# Bump serial
local zone_file="$ZONE_DIR/${domain}.zone"
if [ -f "$zone_file" ]; then
local new_serial=$(date +%Y%m%d%H)
sed -i "s/[0-9]\{10\} ; serial/${new_serial} ; serial/" "$zone_file"
log_info "Updated serial to $new_serial"
fi
else
log_info "Reloading all zones..."
fi
/etc/init.d/dnsmasq reload 2>/dev/null
log_info "Dnsmasq reloaded"
}
# =============================================================================
# SECONDARY DNS COMMANDS
# =============================================================================
cmd_secondary_list() {
echo "=== Secondary DNS Providers ==="
echo ""
uci show "$CONFIG" 2>/dev/null | grep "=secondary" | while read -r line; do
local section=$(echo "$line" | cut -d= -f1 | cut -d. -f2)
local provider=$(uci_get "${section}.provider")
local enabled=$(uci_get "${section}.enabled")
local status="disabled"
[ "$enabled" = "1" ] && status="enabled"
printf "%-15s [%s]\n" "$provider" "$status"
# List zones for this provider
uci show "$CONFIG" 2>/dev/null | grep "=zone" | while read -r zline; do
local zsection=$(echo "$zline" | cut -d= -f1 | cut -d. -f2)
local domain=$(uci_get "${zsection}.domain")
local secondaries=$(uci_get "${zsection}.secondary")
if echo "$secondaries" | grep -q "$provider"; then
echo " - $domain"
fi
done
done
}
cmd_secondary_add() {
local provider="$1"
local domain="$2"
[ -z "$provider" ] || [ -z "$domain" ] && {
echo "Usage: vortexctl secondary add <provider> <domain>"
echo "Providers: ovh, gandi"
exit 1
}
log_info "Configuring $provider as secondary for $domain..."
# Check if zone exists
local zone_section="zone_$(echo "$domain" | tr '.-' '__')"
local zone_domain=$(uci_get "${zone_section}.domain")
[ -z "$zone_domain" ] && {
log_error "Zone $domain not configured. Run 'vortexctl zone import $domain' first"
exit 1
}
# Add secondary to zone
uci add_list "${CONFIG}.${zone_section}.secondary=$provider"
uci commit "$CONFIG"
# Configure secondary provider section if not exists
local sec_section="secondary_${provider}"
local sec_exists=$(uci_get "${sec_section}.provider")
if [ -z "$sec_exists" ]; then
uci set "${CONFIG}.${sec_section}=secondary"
uci set "${CONFIG}.${sec_section}.provider=$provider"
uci set "${CONFIG}.${sec_section}.enabled=1"
uci commit "$CONFIG"
fi
# Provider-specific setup
case "$provider" in
ovh)
log_info "OVH secondary DNS setup:"
echo ""
echo "1. Add OVH DNS servers to your zone's NS records"
echo "2. Configure OVH as secondary via their API or web panel"
echo "3. OVH will poll this server for zone transfers"
echo ""
echo "OVH secondary DNS servers:"
echo " dns100.ovh.net"
echo " dns101.ovh.net"
echo " dns102.ovh.net"
echo " dns103.ovh.net"
# Add auth-sec-servers to dnsmasq config
local dnsmasq_conf="${DNSMASQ_VORTEX_DIR}/${domain}.conf"
if [ -f "$dnsmasq_conf" ]; then
if ! grep -q "auth-sec-servers" "$dnsmasq_conf"; then
echo "auth-sec-servers=dns100.ovh.net,dns101.ovh.net,dns102.ovh.net,dns103.ovh.net" >> "$dnsmasq_conf"
/etc/init.d/dnsmasq reload 2>/dev/null
fi
fi
;;
gandi)
log_info "Gandi secondary DNS setup:"
echo ""
echo "Gandi uses their LiveDNS as secondary."
echo "Configure via Gandi API or web panel."
;;
*)
log_warn "Unknown provider: $provider"
;;
esac
log_info "Secondary $provider configured for $domain"
}
cmd_secondary_remove() {
local provider="$1"
local domain="$2"
[ -z "$provider" ] || [ -z "$domain" ] && {
echo "Usage: vortexctl secondary remove <provider> <domain>"
exit 1
}
local zone_section="zone_$(echo "$domain" | tr '.-' '__')"
uci del_list "${CONFIG}.${zone_section}.secondary=$provider" 2>/dev/null
uci commit "$CONFIG"
log_info "Removed $provider as secondary for $domain"
}
# =============================================================================
# DAEMON
# =============================================================================
cmd_daemon() {
load_config
[ "$enabled" = "1" ] || {
log_error "Vortex DNS is disabled"
exit 1
}
log_info "Starting Vortex DNS daemon (interval: ${sync_interval}s)..."
while true; do
cmd_mesh_sync 2>&1 | while read -r line; do
logger -t "$LOG_TAG" "$line"
done
sleep "$sync_interval"
done
}
# =============================================================================
# MAIN
# =============================================================================
case "$1" in
status)
cmd_status
;;
master)
case "$2" in
init) cmd_master_init "$3" ;;
delegate) cmd_master_delegate "$3" "$4" ;;
revoke) log_info "Not implemented yet" ;;
list-slaves) cmd_master_list_slaves ;;
*) echo "Unknown master command: $2"; usage; exit 1 ;;
esac
;;
slave)
case "$2" in
join) cmd_slave_join "$3" "$4" ;;
leave) log_info "Not implemented yet" ;;
status) cmd_slave_status ;;
*) echo "Unknown slave command: $2"; usage; exit 1 ;;
esac
;;
mesh)
case "$2" in
status) cmd_mesh_status ;;
sync) cmd_mesh_sync ;;
publish) cmd_mesh_publish "$3" "$4" ;;
unpublish) log_info "Not implemented yet" ;;
*) echo "Unknown mesh command: $2"; usage; exit 1 ;;
esac
;;
submaster)
log_info "Submaster commands not implemented yet"
;;
zone)
case "$2" in
list) cmd_zone_list ;;
dump) cmd_zone_dump "$3" ;;
import) cmd_zone_import "$3" ;;
export) cmd_zone_export "$3" ;;
reload) cmd_zone_reload "$3" ;;
*) echo "Unknown zone command: $2"; usage; exit 1 ;;
esac
;;
secondary)
case "$2" in
list) cmd_secondary_list ;;
add) cmd_secondary_add "$3" "$4" ;;
remove) cmd_secondary_remove "$3" "$4" ;;
*) echo "Unknown secondary command: $2"; usage; exit 1 ;;
esac
;;
daemon)
cmd_daemon
;;
-h|--help|help)
usage
;;
*)
usage
exit 1
;;
esac