- Parse ndpid-apps.json array format [{name: "TLS.YouTube", ...}]
- Use jq contains() instead of test() regex (ONIGURUMA not available on OpenWrt)
- Filter streaming services: YouTube, Netflix, Spotify, AppleiTunes, etc.
- Aggregate streams by app name (combine TLS.YouTube + QUIC.YouTube)
- Estimate quality based on data volume (SD/HD/FHD)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
580 lines
18 KiB
Bash
Executable File
580 lines
18 KiB
Bash
Executable File
#!/bin/sh
|
|
# RPCD backend for Media Flow
|
|
# Provides ubus interface: luci.media-flow
|
|
# Supports both nDPId (local DPI) and netifyd as data sources
|
|
# nDPId provides local application detection without cloud subscription
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
HISTORY_FILE="/tmp/media-flow-history.json"
|
|
STATS_DIR="/tmp/media-flow-stats"
|
|
NDPID_FLOWS="/tmp/ndpid-flows.json"
|
|
NDPID_APPS="/tmp/ndpid-apps.json"
|
|
MEDIA_CACHE="/tmp/media-flow-ndpid-cache.json"
|
|
|
|
# Streaming patterns for filtering
|
|
STREAMING_PATTERN="YouTube|Netflix|Disney|Amazon|Twitch|Hbo|Hulu|Vimeo|Peacock|Paramount|Spotify|Apple.*Music|Deezer|SoundCloud|Tidal|Zoom|Teams|Meet|Discord|Skype|Webex"
|
|
|
|
# Initialize storage
|
|
init_storage() {
|
|
mkdir -p "$STATS_DIR"
|
|
[ ! -f "$HISTORY_FILE" ] && echo '[]' > "$HISTORY_FILE"
|
|
}
|
|
|
|
# Detect available DPI source
|
|
get_dpi_source() {
|
|
# Prefer nDPId if running
|
|
# Check for ndpid process and either direct flows file or compat layer output
|
|
if pgrep ndpid >/dev/null 2>&1; then
|
|
# nDPId running - check for data files
|
|
if [ -f "$NDPID_FLOWS" ] || [ -f "$MEDIA_CACHE" ] || [ -f /var/run/netifyd/status.json ]; then
|
|
echo "ndpid"
|
|
return
|
|
fi
|
|
# nDPId running but no data yet
|
|
echo "ndpid"
|
|
elif [ -f /var/run/netifyd/status.json ] && pgrep netifyd >/dev/null 2>&1; then
|
|
echo "netifyd"
|
|
else
|
|
echo "none"
|
|
fi
|
|
}
|
|
|
|
# Get nDPId flow data
|
|
get_ndpid_data() {
|
|
if [ -f "$NDPID_FLOWS" ]; then
|
|
cat "$NDPID_FLOWS"
|
|
else
|
|
echo '[]'
|
|
fi
|
|
}
|
|
|
|
# Get nDPId apps data
|
|
get_ndpid_apps() {
|
|
if [ -f "$NDPID_APPS" ]; then
|
|
cat "$NDPID_APPS"
|
|
else
|
|
echo '[]'
|
|
fi
|
|
}
|
|
|
|
# Get netifyd status data
|
|
get_netifyd_data() {
|
|
if [ -f /var/run/netifyd/status.json ]; then
|
|
cat /var/run/netifyd/status.json
|
|
else
|
|
echo '{}'
|
|
fi
|
|
}
|
|
|
|
# Build network stats from netifyd status.json
|
|
build_network_stats_json() {
|
|
netifyd_data="$1"
|
|
|
|
# Extract interface stats from netifyd
|
|
echo "$netifyd_data" | jq -c '{
|
|
interfaces: (if .stats then
|
|
[.stats | to_entries[] | {
|
|
name: .key,
|
|
rx_bytes: (.value.ip_bytes // 0),
|
|
tx_bytes: (.value.wire_bytes // 0),
|
|
rx_packets: (.value.ip // 0),
|
|
tx_packets: (.value.raw // 0),
|
|
tcp: (.value.tcp // 0),
|
|
udp: (.value.udp // 0),
|
|
icmp: (.value.icmp // 0)
|
|
}]
|
|
else [] end),
|
|
flows_active: (.flows_active // 0),
|
|
flow_count: (.flow_count // 0),
|
|
uptime: (.uptime // 0),
|
|
agent_version: (.agent_version // 0),
|
|
cpu_system: (.cpu_system // 0),
|
|
cpu_user: (.cpu_user // 0),
|
|
memrss_kb: (.memrss_kb // 0)
|
|
}' 2>/dev/null || echo '{"interfaces":[],"flows_active":0,"flow_count":0,"uptime":0,"agent_version":0}'
|
|
}
|
|
|
|
case "$1" in
|
|
list)
|
|
cat <<-'EOF'
|
|
{
|
|
"status": {},
|
|
"get_active_streams": {},
|
|
"get_network_stats": {},
|
|
"get_stream_history": {"hours": 24},
|
|
"get_stats_by_service": {},
|
|
"get_stats_by_client": {},
|
|
"get_service_details": {"service": "string"},
|
|
"set_alert": {"service": "string", "threshold_hours": 4, "action": "notify"},
|
|
"delete_alert": {"alert_id": "string"},
|
|
"list_alerts": {},
|
|
"clear_history": {},
|
|
"get_settings": {},
|
|
"set_settings": {"enabled": 1, "history_retention": 7, "refresh_interval": 5},
|
|
"start_ndpid": {},
|
|
"stop_ndpid": {},
|
|
"start_netifyd": {},
|
|
"stop_netifyd": {}
|
|
}
|
|
EOF
|
|
;;
|
|
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
init_storage
|
|
|
|
# Check nDPId status
|
|
ndpid_running=0
|
|
ndpid_version="unknown"
|
|
ndpid_flows=0
|
|
pgrep ndpid > /dev/null 2>&1 && ndpid_running=1
|
|
if [ "$ndpid_running" = "1" ]; then
|
|
ndpid_version=$(ndpid -v 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+' | head -1)
|
|
[ -z "$ndpid_version" ] && ndpid_version="unknown"
|
|
# Check for flows from compat layer status.json
|
|
if [ -f /var/run/netifyd/status.json ] && [ -s /var/run/netifyd/status.json ]; then
|
|
# Read flow_count using jsonfilter (busybox-friendly)
|
|
ndpid_flows=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flow_count' 2>/dev/null)
|
|
[ -z "$ndpid_flows" ] && ndpid_flows=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flows_active' 2>/dev/null)
|
|
[ -z "$ndpid_flows" ] && ndpid_flows=0
|
|
elif [ -f "$NDPID_FLOWS" ] && [ -s "$NDPID_FLOWS" ]; then
|
|
ndpid_flows=$(jsonfilter -i "$NDPID_FLOWS" -e '@[*]' 2>/dev/null | wc -l)
|
|
[ -z "$ndpid_flows" ] && ndpid_flows=0
|
|
fi
|
|
fi
|
|
|
|
# Check netifyd status
|
|
netifyd_running=0
|
|
pgrep netifyd > /dev/null 2>&1 && netifyd_running=1
|
|
|
|
netifyd_data=$(get_netifyd_data)
|
|
netifyd_flows=0
|
|
netifyd_version="unknown"
|
|
|
|
if [ "$netifyd_running" = "1" ] && [ -f /var/run/netifyd/status.json ] && [ -s /var/run/netifyd/status.json ]; then
|
|
netifyd_flows=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flows_active' 2>/dev/null)
|
|
[ -z "$netifyd_flows" ] && netifyd_flows=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flow_count' 2>/dev/null)
|
|
[ -z "$netifyd_flows" ] && netifyd_flows=0
|
|
netifyd_version=$(jsonfilter -i /var/run/netifyd/status.json -e '@.agent_version' 2>/dev/null)
|
|
[ -z "$netifyd_version" ] && netifyd_version="unknown"
|
|
fi
|
|
|
|
# Determine active DPI source
|
|
dpi_source=$(get_dpi_source)
|
|
|
|
# Use flow count from active source
|
|
if [ "$dpi_source" = "ndpid" ]; then
|
|
flow_count=$ndpid_flows
|
|
else
|
|
flow_count=$netifyd_flows
|
|
fi
|
|
|
|
history_count=0
|
|
if [ -f "$HISTORY_FILE" ]; then
|
|
history_count=$(jq 'length' "$HISTORY_FILE" 2>/dev/null)
|
|
[ -z "$history_count" ] && history_count=0
|
|
fi
|
|
|
|
# Check if nDPId cache has active streams
|
|
active_streams=0
|
|
if [ -f "$MEDIA_CACHE" ]; then
|
|
active_streams=$(jq 'length' "$MEDIA_CACHE" 2>/dev/null)
|
|
[ -z "$active_streams" ] && active_streams=0
|
|
fi
|
|
|
|
enabled=$(uci -q get media_flow.global.enabled 2>/dev/null || echo "1")
|
|
refresh=$(uci -q get media_flow.global.refresh_interval 2>/dev/null || echo "5")
|
|
|
|
cat <<-EOF
|
|
{
|
|
"enabled": $enabled,
|
|
"module": "media-flow",
|
|
"version": "0.6.0",
|
|
"dpi_source": "$dpi_source",
|
|
"ndpid_running": $ndpid_running,
|
|
"ndpid_version": "$ndpid_version",
|
|
"ndpid_flows": $ndpid_flows,
|
|
"netifyd_running": $netifyd_running,
|
|
"netifyd_version": "$netifyd_version",
|
|
"netifyd_flows": $netifyd_flows,
|
|
"active_flows": $flow_count,
|
|
"active_streams": $active_streams,
|
|
"history_entries": $history_count,
|
|
"refresh_interval": $refresh
|
|
}
|
|
EOF
|
|
;;
|
|
|
|
get_active_streams)
|
|
init_storage
|
|
|
|
dpi_source=$(get_dpi_source)
|
|
streams="[]"
|
|
flow_count=0
|
|
|
|
if [ "$dpi_source" = "ndpid" ]; then
|
|
# Get active streams - try cache first, then parse apps file directly
|
|
if [ -f "$MEDIA_CACHE" ] && [ -s "$MEDIA_CACHE" ]; then
|
|
streams=$(cat "$MEDIA_CACHE" 2>/dev/null || echo "[]")
|
|
elif [ -f "$NDPID_APPS" ] && [ -s "$NDPID_APPS" ]; then
|
|
# Parse ndpid-apps.json directly and filter streaming services
|
|
# Format is array: [{"name": "TLS.YouTube", "category": "Media", "flows": 33, "bytes": 123456}, ...]
|
|
if command -v jq >/dev/null 2>&1; then
|
|
# jq without regex (ONIGURUMA not available on OpenWrt)
|
|
# Filter streaming services by checking if name contains known patterns
|
|
streams=$(jq -c '
|
|
[.[] | select(
|
|
(.name | ascii_downcase | contains("youtube")) or
|
|
(.name | ascii_downcase | contains("netflix")) or
|
|
(.name | ascii_downcase | contains("disney")) or
|
|
(.name | ascii_downcase | contains("amazon")) or
|
|
(.name | ascii_downcase | contains("twitch")) or
|
|
(.name | ascii_downcase | contains("hbo")) or
|
|
(.name | ascii_downcase | contains("hulu")) or
|
|
(.name | ascii_downcase | contains("vimeo")) or
|
|
(.name | ascii_downcase | contains("peacock")) or
|
|
(.name | ascii_downcase | contains("paramount")) or
|
|
(.name | ascii_downcase | contains("spotify")) or
|
|
(.name | ascii_downcase | contains("applemusic")) or
|
|
(.name | ascii_downcase | contains("appleitunes")) or
|
|
(.name | ascii_downcase | contains("deezer")) or
|
|
(.name | ascii_downcase | contains("soundcloud")) or
|
|
(.name | ascii_downcase | contains("tidal")) or
|
|
(.name | ascii_downcase | contains("zoom")) or
|
|
(.name | ascii_downcase | contains("teams")) or
|
|
(.name | ascii_downcase | contains("meet")) or
|
|
(.name | ascii_downcase | contains("discord")) or
|
|
(.name | ascii_downcase | contains("skype")) or
|
|
(.name | ascii_downcase | contains("webex"))
|
|
)] |
|
|
map({
|
|
app: (if (.name | contains(".")) then (.name | split(".") | .[1:] | join(".")) else .name end),
|
|
category: (.category // "Media"),
|
|
flows: (.flows // 0),
|
|
bytes: (.bytes // 0)
|
|
}) |
|
|
group_by(.app) |
|
|
map({
|
|
app: .[0].app,
|
|
category: .[0].category,
|
|
flows: (map(.flows) | add),
|
|
bytes_rx: (map(.bytes) | add),
|
|
bytes_tx: 0,
|
|
bandwidth: ((map(.bytes) | add) / 125 | floor),
|
|
quality: (if ((map(.bytes) | add) > 100000000) then "FHD" elif ((map(.bytes) | add) > 10000000) then "HD" else "SD" end),
|
|
client: "LAN"
|
|
})
|
|
' "$NDPID_APPS" 2>/dev/null) || streams="[]"
|
|
else
|
|
# Fallback without jq - grep names from JSON array
|
|
streams="["
|
|
first=1
|
|
for name in $(jq -r '.[].name' "$NDPID_APPS" 2>/dev/null | grep -iE "$STREAMING_PATTERN"); do
|
|
app_name=$(echo "$name" | sed 's/^[^.]*\.//')
|
|
[ "$first" = "0" ] && streams="$streams,"
|
|
first=0
|
|
streams="$streams{\"app\":\"$app_name\",\"category\":\"Media\",\"flows\":1,\"bandwidth\":0,\"quality\":\"HD\",\"client\":\"LAN\"}"
|
|
done
|
|
streams="$streams]"
|
|
fi
|
|
fi
|
|
# Get flow count - prefer compat layer status.json
|
|
if [ -f /var/run/netifyd/status.json ] && [ -s /var/run/netifyd/status.json ]; then
|
|
flow_count=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flow_count' 2>/dev/null)
|
|
[ -z "$flow_count" ] && flow_count=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flows_active' 2>/dev/null)
|
|
[ -z "$flow_count" ] && flow_count=0
|
|
elif [ -f "$NDPID_FLOWS" ] && [ -s "$NDPID_FLOWS" ]; then
|
|
flow_count=$(jsonfilter -i "$NDPID_FLOWS" -e '@[*]' 2>/dev/null | wc -l)
|
|
[ -z "$flow_count" ] && flow_count=0
|
|
fi
|
|
note="Streams detected via nDPId local DPI"
|
|
elif [ "$dpi_source" = "netifyd" ]; then
|
|
if [ -f /var/run/netifyd/status.json ] && [ -s /var/run/netifyd/status.json ]; then
|
|
flow_count=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flows_active' 2>/dev/null)
|
|
[ -z "$flow_count" ] && flow_count=$(jsonfilter -i /var/run/netifyd/status.json -e '@.flow_count' 2>/dev/null)
|
|
[ -z "$flow_count" ] && flow_count=0
|
|
fi
|
|
note="Application detection requires netifyd cloud subscription"
|
|
else
|
|
note="No DPI engine available"
|
|
fi
|
|
|
|
cat <<-EOF
|
|
{
|
|
"streams": $streams,
|
|
"dpi_source": "$dpi_source",
|
|
"note": "$note",
|
|
"flow_count": $flow_count
|
|
}
|
|
EOF
|
|
;;
|
|
|
|
get_network_stats)
|
|
init_storage
|
|
|
|
netifyd_data=$(get_netifyd_data)
|
|
stats=$(build_network_stats_json "$netifyd_data")
|
|
|
|
echo "{\"stats\": $stats}"
|
|
;;
|
|
|
|
get_stream_history)
|
|
read -r input
|
|
hours=$(echo "$input" | jq -r '.hours // 24' 2>/dev/null)
|
|
[ -z "$hours" ] || [ "$hours" = "null" ] && hours=24
|
|
|
|
init_storage
|
|
|
|
history="[]"
|
|
if [ -f "$HISTORY_FILE" ]; then
|
|
history=$(jq -c '.' "$HISTORY_FILE" 2>/dev/null || echo "[]")
|
|
fi
|
|
|
|
cat <<-EOF
|
|
{"history": $history, "hours_requested": $hours}
|
|
EOF
|
|
;;
|
|
|
|
get_stats_by_service)
|
|
init_storage
|
|
|
|
services="{}"
|
|
if [ -f "$HISTORY_FILE" ] && [ -s "$HISTORY_FILE" ]; then
|
|
services=$(jq -c '
|
|
group_by(.app) |
|
|
map({
|
|
key: .[0].app,
|
|
value: {
|
|
sessions: length,
|
|
total_bandwidth_kbps: (map(.bandwidth) | add // 0),
|
|
total_duration_seconds: (map(.duration) | add // 0),
|
|
category: .[0].category
|
|
}
|
|
}) |
|
|
from_entries
|
|
' "$HISTORY_FILE" 2>/dev/null) || services="{}"
|
|
fi
|
|
|
|
cat <<-EOF
|
|
{"services": $services}
|
|
EOF
|
|
;;
|
|
|
|
get_stats_by_client)
|
|
init_storage
|
|
|
|
clients="{}"
|
|
if [ -f "$HISTORY_FILE" ] && [ -s "$HISTORY_FILE" ]; then
|
|
clients=$(jq -c '
|
|
group_by(.client) |
|
|
map({
|
|
key: .[0].client,
|
|
value: {
|
|
sessions: length,
|
|
total_bandwidth_kbps: (map(.bandwidth) | add // 0),
|
|
total_duration_seconds: (map(.duration) | add // 0),
|
|
top_service: (group_by(.app) | max_by(length) | .[0].app // "unknown")
|
|
}
|
|
}) |
|
|
from_entries
|
|
' "$HISTORY_FILE" 2>/dev/null) || clients="{}"
|
|
fi
|
|
|
|
cat <<-EOF
|
|
{"clients": $clients}
|
|
EOF
|
|
;;
|
|
|
|
get_service_details)
|
|
read -r input
|
|
service=$(echo "$input" | jq -r '.service // ""' 2>/dev/null)
|
|
|
|
init_storage
|
|
|
|
result='{}'
|
|
if [ -n "$service" ] && [ -f "$HISTORY_FILE" ] && [ -s "$HISTORY_FILE" ]; then
|
|
result=$(jq -c --arg svc "$service" '
|
|
[.[] | select(.app == $svc)] |
|
|
{
|
|
service: $svc,
|
|
total_sessions: length,
|
|
avg_bandwidth_kbps: (if length > 0 then (map(.bandwidth) | add / length | floor) else 0 end),
|
|
total_duration_seconds: (map(.duration) | add // 0),
|
|
category: (.[0].category // "unknown"),
|
|
typical_quality: (.[0].quality // "unknown"),
|
|
recent_sessions: (.[-10:] | map({
|
|
timestamp: .timestamp,
|
|
client: .client,
|
|
bandwidth_kbps: .bandwidth,
|
|
duration_seconds: .duration,
|
|
quality: .quality
|
|
}))
|
|
}
|
|
' "$HISTORY_FILE" 2>/dev/null) || result='{"service":"'$service'","total_sessions":0,"avg_bandwidth_kbps":0,"total_duration_seconds":0,"category":"unknown","typical_quality":"unknown","recent_sessions":[]}'
|
|
else
|
|
result='{"service":"'$service'","total_sessions":0,"avg_bandwidth_kbps":0,"total_duration_seconds":0,"category":"unknown","typical_quality":"unknown","recent_sessions":[]}'
|
|
fi
|
|
|
|
echo "$result"
|
|
;;
|
|
|
|
set_alert)
|
|
read -r input
|
|
service=$(echo "$input" | jq -r '.service // ""' 2>/dev/null)
|
|
threshold_hours=$(echo "$input" | jq -r '.threshold_hours // 4' 2>/dev/null)
|
|
action=$(echo "$input" | jq -r '.action // "notify"' 2>/dev/null)
|
|
|
|
if [ -z "$service" ]; then
|
|
echo '{"success": false, "message": "Service name required"}'
|
|
exit 0
|
|
fi
|
|
|
|
alert_id="alert_$(echo "$service" | tr -d ' ' | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9_')"
|
|
|
|
uci -q delete "media_flow.${alert_id}" 2>/dev/null
|
|
uci set "media_flow.${alert_id}=alert"
|
|
uci set "media_flow.${alert_id}.service=${service}"
|
|
uci set "media_flow.${alert_id}.threshold_hours=${threshold_hours}"
|
|
uci set "media_flow.${alert_id}.action=${action}"
|
|
uci set "media_flow.${alert_id}.enabled=1"
|
|
uci commit media_flow
|
|
|
|
cat <<-EOF
|
|
{"success": true, "message": "Alert configured for $service", "alert_id": "$alert_id"}
|
|
EOF
|
|
;;
|
|
|
|
delete_alert)
|
|
read -r input
|
|
alert_id=$(echo "$input" | jq -r '.alert_id // ""' 2>/dev/null)
|
|
|
|
if [ -z "$alert_id" ]; then
|
|
echo '{"success": false, "message": "Alert ID required"}'
|
|
exit 0
|
|
fi
|
|
|
|
if uci -q get "media_flow.${alert_id}" >/dev/null 2>&1; then
|
|
uci delete "media_flow.${alert_id}"
|
|
uci commit media_flow
|
|
echo '{"success": true, "message": "Alert deleted"}'
|
|
else
|
|
echo '{"success": false, "message": "Alert not found"}'
|
|
fi
|
|
;;
|
|
|
|
list_alerts)
|
|
alerts="[]"
|
|
|
|
# Build alerts array from UCI
|
|
alerts=$(uci show media_flow 2>/dev/null | grep "=alert$" | while read -r line; do
|
|
section=$(echo "$line" | cut -d. -f2 | cut -d= -f1)
|
|
svc=$(uci -q get "media_flow.${section}.service")
|
|
threshold=$(uci -q get "media_flow.${section}.threshold_hours")
|
|
act=$(uci -q get "media_flow.${section}.action")
|
|
en=$(uci -q get "media_flow.${section}.enabled")
|
|
[ -z "$en" ] && en="1"
|
|
|
|
cat <<-ALERT
|
|
{"id":"$section","service":"$svc","threshold_hours":$threshold,"action":"$act","enabled":$en}
|
|
ALERT
|
|
done | jq -s '.' 2>/dev/null) || alerts="[]"
|
|
|
|
[ -z "$alerts" ] || [ "$alerts" = "null" ] && alerts="[]"
|
|
|
|
cat <<-EOF
|
|
{"alerts": $alerts}
|
|
EOF
|
|
;;
|
|
|
|
clear_history)
|
|
echo '[]' > "$HISTORY_FILE"
|
|
echo '{"success": true, "message": "History cleared"}'
|
|
;;
|
|
|
|
get_settings)
|
|
enabled=$(uci -q get media_flow.global.enabled 2>/dev/null || echo "1")
|
|
retention=$(uci -q get media_flow.global.history_retention 2>/dev/null || echo "7")
|
|
refresh=$(uci -q get media_flow.global.refresh_interval 2>/dev/null || echo "5")
|
|
|
|
cat <<-EOF
|
|
{
|
|
"enabled": $enabled,
|
|
"history_retention": $retention,
|
|
"refresh_interval": $refresh
|
|
}
|
|
EOF
|
|
;;
|
|
|
|
set_settings)
|
|
read -r input
|
|
enabled=$(echo "$input" | jq -r '.enabled // 1' 2>/dev/null)
|
|
retention=$(echo "$input" | jq -r '.history_retention // 7' 2>/dev/null)
|
|
refresh=$(echo "$input" | jq -r '.refresh_interval // 5' 2>/dev/null)
|
|
|
|
uci set media_flow.global.enabled="$enabled"
|
|
uci set media_flow.global.history_retention="$retention"
|
|
uci set media_flow.global.refresh_interval="$refresh"
|
|
uci commit media_flow
|
|
|
|
echo '{"success": true, "message": "Settings saved"}'
|
|
;;
|
|
|
|
start_ndpid)
|
|
# Start nDPId and compatibility layer
|
|
result_ndpid=0
|
|
result_compat=0
|
|
msg=""
|
|
|
|
if [ -x /etc/init.d/ndpid ]; then
|
|
/etc/init.d/ndpid start 2>/dev/null && result_ndpid=1
|
|
/etc/init.d/ndpid enable 2>/dev/null
|
|
fi
|
|
|
|
if [ -x /etc/init.d/ndpid-compat ]; then
|
|
sleep 2
|
|
/etc/init.d/ndpid-compat start 2>/dev/null && result_compat=1
|
|
/etc/init.d/ndpid-compat enable 2>/dev/null
|
|
fi
|
|
|
|
if [ "$result_ndpid" = "1" ]; then
|
|
msg="nDPId started"
|
|
[ "$result_compat" = "1" ] && msg="$msg with compatibility layer"
|
|
echo "{\"success\": true, \"message\": \"$msg\"}"
|
|
else
|
|
echo '{"success": false, "message": "nDPId not installed or failed to start"}'
|
|
fi
|
|
;;
|
|
|
|
stop_ndpid)
|
|
/etc/init.d/ndpid-compat stop 2>/dev/null
|
|
/etc/init.d/ndpid stop 2>/dev/null
|
|
echo '{"success": true, "message": "nDPId stopped"}'
|
|
;;
|
|
|
|
start_netifyd)
|
|
if [ -x /etc/init.d/netifyd ]; then
|
|
/etc/init.d/netifyd start 2>/dev/null
|
|
/etc/init.d/netifyd enable 2>/dev/null
|
|
echo '{"success": true, "message": "Netifyd started"}'
|
|
else
|
|
echo '{"success": false, "message": "Netifyd not installed"}'
|
|
fi
|
|
;;
|
|
|
|
stop_netifyd)
|
|
/etc/init.d/netifyd stop 2>/dev/null
|
|
echo '{"success": true, "message": "Netifyd stopped"}'
|
|
;;
|
|
|
|
*)
|
|
cat <<-EOF
|
|
{"error": -32601, "message": "Method not found: $2"}
|
|
EOF
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|