mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-02 23:03:56 +08:00
update 2026-10-11 22:01:38
This commit is contained in:
1 parent
ebe57dfba9
commit
6f8f22eee4
199 files changed
+8565
-41418
No files matched your search
Executable
+574
@@ -0,0 +1,574 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
. /lib/functions.sh
|
||||
. /usr/share/honk/lock.sh
|
||||
. /usr/share/honk/lifecycle.sh
|
||||
. /usr/share/honk/log-lines.sh
|
||||
|
||||
CONTROL=/usr/share/honk/control.sh
|
||||
CORE=/usr/bin/honk-core
|
||||
JOBS=/tmp/honk-jobs
|
||||
JOB_RUNNER=/usr/share/honk/job.sh
|
||||
UPDATE=/usr/share/honk/update.sh
|
||||
MAINT=/usr/share/honk/maintenance.sh
|
||||
REDACT=/usr/share/honk/redact-log.sh
|
||||
LOG_LEVEL_FILTER=/usr/share/honk/log-level-filter.sh
|
||||
RESTORE_ROOT=/tmp/honk-maintenance
|
||||
RESTORE_PATH=$RESTORE_ROOT/restore.tar.gz
|
||||
|
||||
emit_result() {
|
||||
ok=$1
|
||||
message=$2
|
||||
[ "$ok" = true ] && ok=1 || ok=0
|
||||
json_init
|
||||
json_add_boolean ok "$ok"
|
||||
json_add_string message "$message"
|
||||
json_dump
|
||||
}
|
||||
|
||||
emit_errors() {
|
||||
ok=$1
|
||||
[ "$ok" = true ] && ok=1 || ok=0
|
||||
json_init
|
||||
json_add_boolean ok "$ok"
|
||||
json_add_array errors
|
||||
for error in $2; do
|
||||
json_add_string "" "$error"
|
||||
done
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
lock_owner_invalid() {
|
||||
[ -d "$HONK_LOCK_DIR" ] || return 1
|
||||
lock_owner=$(cat "$HONK_LOCK_DIR/pid" 2>/dev/null)
|
||||
case "$lock_owner" in ''|*[!0-9]*) return 0 ;; esac
|
||||
return 1
|
||||
}
|
||||
|
||||
status() {
|
||||
local enabled initialized boot_enabled running api_url api_ready core_version doona_version kernel lan_network listen_port errors listen
|
||||
config_load honk
|
||||
config_get_bool enabled main enabled 0
|
||||
config_get_bool initialized main initialized 0
|
||||
config_get_bool boot_enabled main boot_enabled 0
|
||||
config_get listen_port main listen_port 9527
|
||||
config_get lan_network main lan_network lan
|
||||
managed_pid=$(procd_core_pid)
|
||||
if is_managed_core "$managed_pid"; then running=1; else running=0; fi
|
||||
listen=$(sed -n "s/^[[:space:]]*listen:[[:space:]]*'\\([^']*\\)'.*/\\1/p" /etc/honk/system.dae | head -n 1)
|
||||
case "$listen" in
|
||||
''|*[!0-9.:]*) listen="127.0.0.1:$listen_port" ;;
|
||||
esac
|
||||
api_url="http://$listen"
|
||||
if [ "$running" = 1 ] && honk_lifecycle_api_owned "$managed_pid"; then api_ready=1; else api_ready=0; fi
|
||||
core_version=$($CORE --version 2>/dev/null | sed -n '1p')
|
||||
[ -n "$core_version" ] || core_version=unknown
|
||||
doona_version=$(jsonfilter -i /usr/share/honk/provenance.json -e '@.doona.version' 2>/dev/null)
|
||||
[ -n "$doona_version" ] || doona_version=unknown
|
||||
kernel=$(uname -r)
|
||||
errors=""
|
||||
if [ "$enabled" != 1 ]; then errors="$errors disabled"; fi
|
||||
if [ "$initialized" != 1 ]; then errors="$errors not_initialized"; fi
|
||||
if [ "$enabled" = 1 ] && [ "$initialized" = 1 ] && [ "$running" != 1 ]; then errors="$errors service_not_running"; fi
|
||||
if [ "$running" = 1 ] && [ "$api_ready" != 1 ]; then errors="$errors api_unavailable"; fi
|
||||
if [ -d "$HONK_LOCK_DIR" ]; then
|
||||
if lock_owner_invalid || honk_lock_is_stale; then errors="$errors stale_operation_lock maintenance_recovery_required"; else errors="$errors operation_in_progress"; fi
|
||||
fi
|
||||
{ [ -e /etc/.honk-maintenance/journal ] || [ -L /etc/.honk-maintenance/journal ]; } && errors="$errors maintenance_recovery_required"
|
||||
{ [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ]; } && errors="$errors update_recovery_required"
|
||||
json_init
|
||||
json_add_boolean enabled "$enabled"
|
||||
json_add_boolean initialized "$initialized"
|
||||
json_add_boolean boot_enabled "$boot_enabled"
|
||||
json_add_boolean running "$running"
|
||||
json_add_boolean api_ready "$api_ready"
|
||||
json_add_string api_url "$api_url"
|
||||
json_add_string core_version "$core_version"
|
||||
json_add_string doona_version "$doona_version"
|
||||
json_add_string kernel "$kernel"
|
||||
json_add_string lan_network "$lan_network"
|
||||
case "$listen_port" in
|
||||
''|*[!0-9]*) listen_port=0 ;;
|
||||
esac
|
||||
json_add_int listen_port "$listen_port"
|
||||
json_add_array errors
|
||||
for error in $errors; do json_add_string "" "$error"; done
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
|
||||
service_ready_config() {
|
||||
local enabled initialized
|
||||
config_load honk
|
||||
config_get_bool enabled main enabled 0
|
||||
config_get_bool initialized main initialized 0
|
||||
[ "$initialized" = 1 ]
|
||||
}
|
||||
|
||||
start_job() {
|
||||
job_operation=$1
|
||||
job_request=
|
||||
case "$job_operation" in backup|restore|reset|update_check|update_apply) ;; *) emit_result false invalid_operation; return 1 ;; esac
|
||||
if [ "$job_operation" = update_apply ]; then
|
||||
update_gate_result=$("$UPDATE" gate 2>/dev/null) || { emit_result false update_check_failed; return 1; }
|
||||
json_load "$update_gate_result" 2>/dev/null || { emit_result false update_check_failed; return 1; }
|
||||
json_get_var update_apply_enabled apply_enabled
|
||||
if [ "$update_apply_enabled" != 1 ]; then
|
||||
json_get_var update_gate_reason reason
|
||||
case "$update_gate_reason" in rollback_tuple_unavailable|trusted_feed_unavailable|signed_manifest_missing|signed_manifest_expired|no_update) emit_result false "$update_gate_reason" ;; *) emit_result false update_check_failed ;; esac
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e "$JOBS" ] && [ ! -L "$JOBS" ]; then
|
||||
(umask 077; mkdir "$JOBS") || { emit_result false job_storage_unavailable; return 1; }
|
||||
fi
|
||||
[ -d "$JOBS" ] && [ ! -L "$JOBS" ] && [ "$(stat -c '%u:%a' "$JOBS" 2>/dev/null)" = 0:700 ] || { emit_result false job_storage_unavailable; return 1; }
|
||||
job_id=$(cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d '-')
|
||||
case "$job_id" in [0-9a-f][0-9a-f]*) [ "${#job_id}" -eq 32 ] || { emit_result false job_id_unavailable; return 1; } ;; *) emit_result false job_id_unavailable; return 1 ;; esac
|
||||
job_dir=$JOBS/$job_id
|
||||
mkdir "$job_dir" || { emit_result false job_storage_unavailable; return 1; }
|
||||
chmod 0700 "$job_dir" || { rm -rf "$job_dir"; emit_result false job_storage_unavailable; return 1; }
|
||||
|
||||
json_init
|
||||
json_add_string state running
|
||||
json_add_string phase queued
|
||||
json_add_boolean ok 1
|
||||
json_add_string message job_started
|
||||
json_dump > "$job_dir/status" || { rm -rf "$job_dir"; emit_result false job_storage_unavailable; return 1; }
|
||||
chmod 0600 "$job_dir/status" || { rm -rf "$job_dir"; emit_result false job_storage_unavailable; return 1; }
|
||||
nohup "$JOB_RUNNER" "$job_id" "$job_operation" >/dev/null 2>&1 </dev/null &
|
||||
job_pid=$!
|
||||
printf '%s\n' "$job_pid" > "$job_dir/worker.pid" || { rm -rf "$job_dir"; emit_result false job_storage_unavailable; return 1; }
|
||||
chmod 0600 "$job_dir/worker.pid" || { rm -rf "$job_dir"; emit_result false job_storage_unavailable; return 1; }
|
||||
json_init
|
||||
json_add_boolean ok 1
|
||||
json_add_string message job_started
|
||||
json_add_string job_id "$job_id"
|
||||
json_dump
|
||||
}
|
||||
|
||||
job_status() {
|
||||
request=$(cat)
|
||||
json_load "$request" 2>/dev/null || { emit_result false invalid_request; return 1; }
|
||||
json_get_var job_id job_id
|
||||
case "$job_id" in ''|*[!0-9a-f]*) emit_result false invalid_job_id; return 1 ;; esac
|
||||
[ "${#job_id}" -eq 32 ] || { emit_result false invalid_job_id; return 1; }
|
||||
[ -d "$JOBS" ] && [ ! -L "$JOBS" ] && [ "$(stat -c '%u:%a' "$JOBS" 2>/dev/null)" = 0:700 ] || { emit_result false job_not_found; return 1; }
|
||||
job_dir=$JOBS/$job_id
|
||||
[ -d "$job_dir" ] && [ ! -L "$job_dir" ] && [ "$(stat -c '%u:%a' "$job_dir" 2>/dev/null)" = 0:700 ] || { emit_result false job_not_found; return 1; }
|
||||
job_status_file=$JOBS/$job_id/status
|
||||
[ -f "$job_status_file" ] && [ ! -L "$job_status_file" ] && [ "$(stat -c '%u:%a' "$job_status_file" 2>/dev/null)" = 0:600 ] || { emit_result false job_not_found; return 1; }
|
||||
job_pid_file=$job_dir/worker.pid
|
||||
[ -f "$job_pid_file" ] && [ ! -L "$job_pid_file" ] && [ "$(stat -c '%u:%a' "$job_pid_file" 2>/dev/null)" = 0:600 ] || { emit_result false job_not_found; return 1; }
|
||||
job_state=$(jsonfilter -i "$job_status_file" -e '@.state' 2>/dev/null)
|
||||
job_pid=$(cat "$job_pid_file" 2>/dev/null)
|
||||
case "$job_pid" in ''|*[!0-9]*) job_pid= ;; esac
|
||||
if [ "$job_state" = running ] && { [ -z "$job_pid" ] || ! kill -0 "$job_pid" 2>/dev/null; }; then
|
||||
json_init
|
||||
if [ -e /etc/.honk-maintenance/journal ] || [ -L /etc/.honk-maintenance/journal ] || [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ] || [ -d "$HONK_LOCK_DIR" ]; then
|
||||
json_add_string state rollback_required
|
||||
json_add_string phase recovery
|
||||
job_lost_message=maintenance_recovery_required
|
||||
else
|
||||
json_add_string state failed
|
||||
json_add_string phase complete
|
||||
job_lost_message worker_lost
|
||||
fi
|
||||
json_add_boolean ok 0
|
||||
json_add_string message "$job_lost_message"
|
||||
json_dump > "$job_dir/status.new" && chmod 0600 "$job_dir/status.new" && mv -f "$job_dir/status.new" "$job_status_file" || { emit_result false job_status_unavailable; return 1; }
|
||||
fi
|
||||
cat "$job_status_file"
|
||||
}
|
||||
|
||||
logs() {
|
||||
request=$(cat)
|
||||
json_load "$request" 2>/dev/null || { emit_result false invalid_request; return 1; }
|
||||
json_get_var log_source source
|
||||
json_get_var log_level level
|
||||
json_get_var log_limit limit
|
||||
case "$log_source" in service) log_pattern='honk.init|procd.*honk|honk service|honk\[[0-9]+\]' ;; core) log_pattern='honk-core|honk::' ;; maintenance) log_pattern='honk-maintenance|honk maintenance' ;; *) emit_result false invalid_log_source; return 1 ;; esac
|
||||
case "$log_level" in all|debug|info|warn|error) ;; *) emit_result false invalid_log_level; return 1 ;; esac
|
||||
case "$log_limit" in ''|*[!0-9]*) log_limit=200 ;; esac
|
||||
[ "$log_limit" -ge 1 ] && [ "$log_limit" -le 200 ] || { emit_result false invalid_log_limit; return 1; }
|
||||
command -v logread >/dev/null 2>&1 || { emit_result false logs_unavailable; return 1; }
|
||||
[ -x "$REDACT" ] && [ -x "$LOG_LEVEL_FILTER" ] || { emit_result false logs_unavailable; return 1; }
|
||||
log_raw_file=$(mktemp /tmp/honk-log.XXXXXX) || { emit_result false logs_unavailable; return 1; }
|
||||
log_file=$(mktemp /tmp/honk-log.XXXXXX) || { rm -f "$log_raw_file"; emit_result false logs_unavailable; return 1; }
|
||||
chmod 0600 "$log_raw_file" "$log_file" || { rm -f "$log_raw_file" "$log_file"; emit_result false logs_unavailable; return 1; }
|
||||
log_capped=0
|
||||
logread -l 200 -e 'honk' 2>/dev/null | head -c 262144 > "$log_raw_file"
|
||||
log_raw_bytes=$(wc -c < "$log_raw_file" | tr -d ' ')
|
||||
if [ "$log_raw_bytes" -ge 262144 ]; then
|
||||
log_capped=1
|
||||
log_raw_complete=$(mktemp /tmp/honk-log.XXXXXX) || { rm -f "$log_raw_file" "$log_file"; emit_result false logs_unavailable; return 1; }
|
||||
chmod 0600 "$log_raw_complete" || { rm -f "$log_raw_file" "$log_file" "$log_raw_complete"; emit_result false logs_unavailable; return 1; }
|
||||
sed '$d' "$log_raw_file" > "$log_raw_complete"
|
||||
mv -f "$log_raw_complete" "$log_raw_file"
|
||||
fi
|
||||
grep -E "$log_pattern" "$log_raw_file" | "$LOG_LEVEL_FILTER" "$log_level" | "$REDACT" > "$log_file"
|
||||
rm -f "$log_raw_file"
|
||||
line_count=$(wc -l < "$log_file" | tr -d ' ')
|
||||
if [ "$line_count" -gt "$log_limit" ]; then
|
||||
log_capped=1
|
||||
tail -n "$log_limit" "$log_file" > "$log_file.latest" && mv -f "$log_file.latest" "$log_file"
|
||||
fi
|
||||
log_bytes=$(wc -c < "$log_file" | tr -d ' ')
|
||||
if [ "$log_bytes" -gt 32768 ]; then
|
||||
log_capped=1
|
||||
log_capped_file=$(mktemp /tmp/honk-log.XXXXXX) || { rm -f "$log_file"; emit_result false logs_unavailable; return 1; }
|
||||
chmod 0600 "$log_capped_file" || { rm -f "$log_file" "$log_capped_file"; emit_result false logs_unavailable; return 1; }
|
||||
LC_ALL=C awk 'BEGIN { first=1; bytes=0 } { lines[NR]=$0; sizes[NR]=length($0)+1; bytes+=sizes[NR]; while (bytes>32768 && first<=NR) { bytes-=sizes[first]; first++ } } END { for (i=first; i<=NR; i++) print lines[i] }' "$log_file" > "$log_capped_file"
|
||||
mv -f "$log_capped_file" "$log_file"
|
||||
fi
|
||||
json_init
|
||||
json_add_boolean ok 1
|
||||
json_add_string message logs_read
|
||||
[ "$log_capped" = 1 ] && json_add_boolean capped 1 || json_add_boolean capped 0
|
||||
json_add_array lines
|
||||
honk_add_log_lines "$log_file"
|
||||
json_close_array
|
||||
json_dump
|
||||
rm -f "$log_file"
|
||||
}
|
||||
|
||||
add_subscription() {
|
||||
request=$(cat)
|
||||
json_load "$request" 2>/dev/null || { emit_result false invalid_request; return 1; }
|
||||
json_get_var sub_name name
|
||||
json_get_var sub_url url
|
||||
|
||||
case "$sub_name" in
|
||||
''|*[!A-Za-z0-9_-]*) emit_result false subscription_invalid; return 1 ;;
|
||||
esac
|
||||
case "$sub_url" in
|
||||
http://*|https://*) ;;
|
||||
*) emit_result false subscription_invalid; return 1 ;;
|
||||
esac
|
||||
|
||||
cfg=/etc/honk/config.dae
|
||||
[ -f "$cfg" ] || { emit_result false config_missing; return 1; }
|
||||
|
||||
if grep -qE "^[[:space:]]*${sub_name}:[[:space:]]*['\"]" "$cfg"; then
|
||||
emit_result false subscription_invalid_or_conflicting
|
||||
return 1
|
||||
fi
|
||||
|
||||
cp -p "$cfg" "$cfg.bak" 2>/dev/null || true
|
||||
|
||||
if ! awk -v name="$sub_name" -v url="$sub_url" '
|
||||
/^subscription[[:space:]]*\{/ {
|
||||
print
|
||||
printf " %s: \047%s\047 {\n interval: 0s\n route: direct\n }\n", name, url
|
||||
found = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
if (!found) {
|
||||
printf "\nsubscription {\n %s: \047%s\047 {\n interval: 0s\n route: direct\n }\n}\n", name, url
|
||||
}
|
||||
}
|
||||
' "$cfg" > "$cfg.tmp" 2>/dev/null; then
|
||||
rm -f "$cfg.tmp"
|
||||
emit_result false config_write_failed
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "$cfg.tmp" "$cfg" || { rm -f "$cfg.tmp"; emit_result false config_write_failed; return 1; }
|
||||
chmod 0600 "$cfg" 2>/dev/null || true
|
||||
|
||||
if [ -n "$(honk_lifecycle_managed_pid 2>/dev/null)" ]; then
|
||||
/etc/init.d/honk reload >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
emit_result true subscription_added
|
||||
}
|
||||
|
||||
add_nodes() {
|
||||
request=$(cat)
|
||||
json_load "$request" 2>/dev/null || { emit_result false invalid_request; return 1; }
|
||||
json_get_var node_links links
|
||||
|
||||
[ -n "$node_links" ] || { emit_result false invalid_request; return 1; }
|
||||
|
||||
cfg=/etc/honk/config.dae
|
||||
[ -f "$cfg" ] || { emit_result false config_missing; return 1; }
|
||||
|
||||
node_lines=$(printf '%s\n' "$node_links" | awk '
|
||||
BEGIN { n = 0 }
|
||||
{
|
||||
if ($0 !~ /^(vless|vmess|ss|ssr|tuic|trojan|hysteria2|hy2|socks5|http|https):\/\//) next
|
||||
n++
|
||||
printf " node_%03d: \047%s\047\n", n, $0
|
||||
}
|
||||
END { if (n == 0) exit 1 }
|
||||
') || { emit_result false share_links_invalid_or_conflicting; return 1; }
|
||||
|
||||
cp -p "$cfg" "$cfg.bak" 2>/dev/null || true
|
||||
|
||||
if ! awk -v lines="$node_lines" '
|
||||
/^[[:space:]]*node[[:space:]]*\{/ {
|
||||
print
|
||||
printf "%s\n", lines
|
||||
found = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
if (!found) printf "\nnode {\n%s\n}\n", lines
|
||||
}
|
||||
' "$cfg" > "$cfg.tmp" 2>/dev/null; then
|
||||
rm -f "$cfg.tmp"
|
||||
emit_result false config_write_failed
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "$cfg.tmp" "$cfg" || { rm -f "$cfg.tmp"; emit_result false config_write_failed; return 1; }
|
||||
chmod 0600 "$cfg" 2>/dev/null || true
|
||||
|
||||
if [ -n "$(honk_lifecycle_managed_pid 2>/dev/null)" ]; then
|
||||
/etc/init.d/honk reload >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
emit_result true nodes_added
|
||||
}
|
||||
|
||||
extract_dae_block() {
|
||||
# $1 = 块名, $2 = 文件;输出该顶级块(含大括号)。只匹配行首(无缩进)的块,
|
||||
# 避免把 dns 里的 routing/request 子块误当成顶级 routing。
|
||||
awk -v name="$1" '
|
||||
$0 ~ "^" name "[[:space:]]*\\{" {
|
||||
in_block = 1
|
||||
depth = 0
|
||||
}
|
||||
in_block {
|
||||
print
|
||||
depth += gsub(/\{/, "{")
|
||||
depth -= gsub(/\}/, "}")
|
||||
if (depth <= 0 && $0 ~ /\}/) in_block = 0
|
||||
}
|
||||
' "$2"
|
||||
}
|
||||
|
||||
import_dae() {
|
||||
request=$(cat)
|
||||
json_load "$request" 2>/dev/null || { emit_result false invalid_request; return 1; }
|
||||
|
||||
src=/tmp/honk-v2-upload/import.dae
|
||||
[ -f "$src" ] || { emit_result false dae_upload_unavailable; return 1; }
|
||||
|
||||
cfg=/etc/honk/config.dae
|
||||
[ -f "$cfg" ] || { emit_result false config_missing; return 1; }
|
||||
|
||||
# 提取 node 块(过滤 honk-core 不支持的 ssr/snell 协议)
|
||||
node_block=$(awk '
|
||||
$0 ~ /^node[[:space:]]*\{/ {
|
||||
in_block = 1
|
||||
depth = 0
|
||||
print
|
||||
next
|
||||
}
|
||||
in_block {
|
||||
if ($0 ~ /node_[A-Za-z0-9_]+:[[:space:]]*\047(ssr|snell):\/\//) next
|
||||
print
|
||||
depth += gsub(/\{/, "{")
|
||||
depth -= gsub(/\}/, "}")
|
||||
if (depth <= 0 && $0 ~ /\}/) in_block = 0
|
||||
}
|
||||
' "$src")
|
||||
group_block=$(extract_dae_block group "$src")
|
||||
routing_block=$(extract_dae_block routing "$src")
|
||||
dns_block=$(extract_dae_block dns "$src")
|
||||
|
||||
[ -n "$node_block" ] || [ -n "$group_block" ] || [ -n "$routing_block" ] || [ -n "$dns_block" ] || { emit_result false dae_invalid_or_unsupported; return 1; }
|
||||
|
||||
cp -p "$cfg" "$cfg.bak" 2>/dev/null || true
|
||||
|
||||
# 整体替换 node/group/routing/dns 四个业务块,保留 include(system.dae)与 subscription。
|
||||
if ! awk -v node_block="$node_block" -v group_block="$group_block" -v routing_block="$routing_block" -v dns_block="$dns_block" '
|
||||
function skip_block() {
|
||||
depth = 1
|
||||
while (depth > 0 && (getline line) > 0) {
|
||||
depth += gsub(/\{/, "{", line)
|
||||
depth -= gsub(/\}/, "}", line)
|
||||
}
|
||||
}
|
||||
/^[[:space:]]*node[[:space:]]*\{/ {
|
||||
if (node_block != "") { printf "%s\n", node_block; node_done = 1 }
|
||||
skip_block()
|
||||
next
|
||||
}
|
||||
/^[[:space:]]*group[[:space:]]*\{/ {
|
||||
if (group_block != "") { printf "%s\n", group_block; group_done = 1 }
|
||||
skip_block()
|
||||
next
|
||||
}
|
||||
/^[[:space:]]*routing[[:space:]]*\{/ {
|
||||
if (routing_block != "") { printf "%s\n", routing_block; routing_done = 1 }
|
||||
skip_block()
|
||||
next
|
||||
}
|
||||
/^[[:space:]]*dns[[:space:]]*\{/ {
|
||||
if (dns_block != "") { printf "%s\n", dns_block; dns_done = 1 }
|
||||
skip_block()
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
if (node_block != "" && !node_done) printf "\n%s\n", node_block
|
||||
if (group_block != "" && !group_done) printf "\n%s\n", group_block
|
||||
if (routing_block != "" && !routing_done) printf "\n%s\n", routing_block
|
||||
if (dns_block != "" && !dns_done) printf "\n%s\n", dns_block
|
||||
}
|
||||
' "$cfg" > "$cfg.tmp" 2>/dev/null; then
|
||||
rm -f "$cfg.tmp"
|
||||
emit_result false config_write_failed
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "$cfg.tmp" "$cfg" || { rm -f "$cfg.tmp"; emit_result false config_write_failed; return 1; }
|
||||
chmod 0600 "$cfg" 2>/dev/null || true
|
||||
rm -f "$src" 2>/dev/null || true
|
||||
|
||||
if [ -n "$(honk_lifecycle_managed_pid 2>/dev/null)" ]; then
|
||||
/etc/init.d/honk reload >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
emit_result true dae_imported
|
||||
}
|
||||
|
||||
set_enabled() {
|
||||
uci set "honk.main.enabled=$1" && uci commit honk
|
||||
}
|
||||
|
||||
procd_core_pid() {
|
||||
honk_lifecycle_procd_pid
|
||||
}
|
||||
|
||||
is_managed_core() {
|
||||
[ "$1" = "$(honk_lifecycle_managed_pid 2>/dev/null)" ] && [ -n "$1" ]
|
||||
}
|
||||
|
||||
begin_mutation() {
|
||||
if [ -e /etc/.honk-update/journal ] || [ -L /etc/.honk-update/journal ]; then emit_result false update_recovery_required; exit 1; fi
|
||||
honk_lock_acquire || { emit_result false operation_in_progress; exit 1; }
|
||||
trap 'honk_lock_release >/dev/null 2>&1' 0
|
||||
}
|
||||
|
||||
restore_prepare() {
|
||||
# Stage the upload directory for a backup restore. The frontend calls
|
||||
# this before uploading the archive to $RESTORE_PATH.
|
||||
if ! mkdir -p "$RESTORE_ROOT" 2>/dev/null; then
|
||||
emit_result false mkdir_failed
|
||||
return 1
|
||||
fi
|
||||
rm -f "$RESTORE_PATH"
|
||||
emit_result true prepared
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{"status":{},"check":{},"initialize":{"username":"String","password":"String"},"start":{},"stop":{},"restart":{},"settings":{"lan_network":"String","listen_port":"Integer","boot_enabled":"Boolean"},"add_subscription":{"name":"String","url":"String"},"add_nodes":{"links":"String"},"import_dae":{},"repair":{},"backup":{},"restore":{},"restore_prepare":{},"reset":{},"job_status":{"job_id":"String"},"logs":{"source":"String","level":"String","limit":"Integer"},"update_check":{},"update_apply":{}}'
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
status)
|
||||
status
|
||||
;;
|
||||
check)
|
||||
if errors=$("$CONTROL" diagnose 2>/dev/null); then emit_errors true ""; else emit_errors false "$errors"; fi
|
||||
;;
|
||||
initialize)
|
||||
exec "$CONTROL" initialize
|
||||
;;
|
||||
settings)
|
||||
exec "$CONTROL" settings
|
||||
;;
|
||||
add_subscription)
|
||||
add_subscription
|
||||
;;
|
||||
add_nodes)
|
||||
add_nodes
|
||||
;;
|
||||
import_dae)
|
||||
import_dae
|
||||
;;
|
||||
repair)
|
||||
exec "$CONTROL" repair
|
||||
;;
|
||||
backup)
|
||||
start_job backup
|
||||
;;
|
||||
restore)
|
||||
start_job restore
|
||||
;;
|
||||
restore_prepare)
|
||||
restore_prepare
|
||||
;;
|
||||
reset)
|
||||
start_job reset
|
||||
;;
|
||||
job_status)
|
||||
job_status
|
||||
;;
|
||||
logs)
|
||||
logs
|
||||
;;
|
||||
update_check)
|
||||
start_job update_check
|
||||
;;
|
||||
update_apply)
|
||||
start_job update_apply
|
||||
;;
|
||||
start)
|
||||
begin_mutation
|
||||
if ! service_ready_config; then emit_result false not_initialized; exit 1; fi
|
||||
old_enabled=$(uci -q get honk.main.enabled || echo 0)
|
||||
if ! set_enabled 1; then emit_result false settings_write_failed; exit 1; fi
|
||||
managed_pid=$(honk_lifecycle_managed_pid 2>/dev/null)
|
||||
if [ -n "$managed_pid" ]; then
|
||||
if honk_lifecycle_api_owned "$managed_pid"; then emit_result true already_running; else set_enabled "$old_enabled" >/dev/null 2>&1; emit_result false managed_service_unhealthy; exit 1; fi
|
||||
elif honk_lifecycle_start 30; then
|
||||
emit_result true started
|
||||
else
|
||||
honk_lifecycle_stop 30 >/dev/null 2>&1 || :
|
||||
set_enabled "$old_enabled" >/dev/null 2>&1
|
||||
emit_result false start_failed
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
begin_mutation
|
||||
old_enabled=$(uci -q get honk.main.enabled || echo 0)
|
||||
if ! set_enabled 0; then emit_result false settings_write_failed; exit 1; fi
|
||||
if honk_lifecycle_stop 30; then emit_result true stopped; else set_enabled "$old_enabled" >/dev/null 2>&1; emit_result false stop_failed; exit 1; fi
|
||||
;;
|
||||
restart)
|
||||
begin_mutation
|
||||
if ! service_ready_config; then emit_result false not_initialized; exit 1; fi
|
||||
old_enabled=$(uci -q get honk.main.enabled || echo 0)
|
||||
if ! set_enabled 1; then emit_result false settings_write_failed; exit 1; fi
|
||||
if [ -n "$(honk_lifecycle_managed_pid 2>/dev/null)" ]; then
|
||||
:
|
||||
elif pidof honk-core >/dev/null 2>&1; then
|
||||
set_enabled "$old_enabled" >/dev/null 2>&1
|
||||
emit_result false unmanaged_core_running
|
||||
exit 1
|
||||
fi
|
||||
if honk_lifecycle_restart 30; then emit_result true restarted; else set_enabled "$old_enabled" >/dev/null 2>&1; emit_result false restart_failed; exit 1; fi
|
||||
;;
|
||||
*)
|
||||
emit_result false unknown_method
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
# geo-cron.sh enable|disable
|
||||
# Manage the geo auto-update crontab entry. enable reads the schedule from
|
||||
# honk.main.geo_auto_freq (daily|weekly); both actions are idempotent.
|
||||
|
||||
ACTION="$1"
|
||||
case "$ACTION" in
|
||||
enable|disable) ;;
|
||||
*) echo "usage: $0 enable|disable" >&2; exit 64 ;;
|
||||
esac
|
||||
CRONTAB="/etc/crontabs/root"
|
||||
TAG="# luci-app-honk geo-update"
|
||||
SCRIPT="/usr/share/luci-app-honk/update-geo.sh"
|
||||
|
||||
# Drop any previous honk geo entry (tag line + its command line).
|
||||
clean() {
|
||||
[ -f "$CRONTAB" ] || return 0
|
||||
grep -vF -e "$TAG" -e "$SCRIPT" "$CRONTAB" > "$CRONTAB.tmp" 2>/dev/null
|
||||
mv "$CRONTAB.tmp" "$CRONTAB"
|
||||
}
|
||||
|
||||
clean
|
||||
|
||||
if [ "$ACTION" = "enable" ]; then
|
||||
case "$(uci -q get honk.main.geo_auto_freq)" in
|
||||
weekly) sched="17 4 * * 0" ;; # Sunday 04:17
|
||||
*) sched="17 4 * * *" ;; # every day 04:17
|
||||
esac
|
||||
mkdir -p /etc/crontabs
|
||||
{
|
||||
echo "$TAG"
|
||||
echo "$sched $SCRIPT geoip; $SCRIPT geosite"
|
||||
} >> "$CRONTAB"
|
||||
/etc/init.d/cron enable >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
/etc/init.d/cron restart >/dev/null 2>&1
|
||||
exit 0
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# pkg-info.sh <honk|luci-app-honk>
|
||||
# Prints "<installed>\t<latest>" for the named package, either field empty when
|
||||
# unknown. Used by the Maintenance view to decide whether to enable [升级].
|
||||
|
||||
PKG="$1"
|
||||
case "$PKG" in
|
||||
honk|luci-app-honk) ;;
|
||||
*) printf '\t\n'; exit 64 ;;
|
||||
esac
|
||||
|
||||
installed=""
|
||||
latest=""
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
if apk info -e "$PKG" >/dev/null 2>&1; then
|
||||
installed=$(apk list -I "$PKG" 2>/dev/null | awk -v p="$PKG" '
|
||||
$1 ~ "^" p "-" {
|
||||
sub("^" p "-", "", $1);
|
||||
print $1;
|
||||
exit
|
||||
}
|
||||
')
|
||||
fi
|
||||
latest=$(apk list "$PKG" 2>/dev/null | awk -v p="$PKG" '
|
||||
$1 ~ "^" p "-[0-9]" {
|
||||
v = $1; sub("^" p "-", "", v);
|
||||
print v
|
||||
}
|
||||
' | sort -V | tail -1)
|
||||
fi
|
||||
|
||||
printf '%s\t%s\n' "$installed" "$latest"
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# Background apk index refresh for the Maintenance view. Returns at once.
|
||||
|
||||
LOCK=/tmp/luci-app-honk.idx.lock
|
||||
|
||||
if [ -f "$LOCK" ]; then
|
||||
mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0)
|
||||
[ "$(( $(date +%s) - mtime ))" -lt 90 ] && exit 0
|
||||
fi
|
||||
: > "$LOCK"
|
||||
|
||||
(
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
flock -n /tmp/luci-app-honk.apk.lock apk update
|
||||
fi
|
||||
) >/dev/null 2>&1 </dev/null &
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
# update-geo.sh <geoip|geosite>
|
||||
# Fork-and-detach so the LuCI RPC call returns immediately. The maintenance
|
||||
# card polls the .dat file's mtime/size to detect completion. A lock file
|
||||
# in /tmp prevents concurrent runs.
|
||||
|
||||
TYPE="$1"
|
||||
# Default source: Loyalsoldier/v2ray-rules-dat — daily-built, China-optimized,
|
||||
# ships both geoip.dat and geosite.dat. Users can override per-type via
|
||||
# honk.main.geoip_url / geosite_url (empty falls back to the default below).
|
||||
case "$TYPE" in
|
||||
geoip)
|
||||
DEF_URL="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
|
||||
URL="$(uci -q get honk.main.geoip_url)"
|
||||
DEST="/usr/share/v2ray/geoip.dat"
|
||||
;;
|
||||
geosite)
|
||||
DEF_URL="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||
URL="$(uci -q get honk.main.geosite_url)"
|
||||
DEST="/usr/share/v2ray/geosite.dat"
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 <geoip|geosite>" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
[ -n "$URL" ] || URL="$DEF_URL"
|
||||
|
||||
LOCK="/tmp/luci-app-honk.${TYPE}.lock"
|
||||
LOG="/tmp/luci-app-honk.${TYPE}.log"
|
||||
|
||||
schedule_backend_reload() {
|
||||
if /bin/pidof honk-core >/dev/null 2>&1; then
|
||||
(
|
||||
sleep 25
|
||||
if /usr/bin/honk-core reload >/dev/null 2>&1; then
|
||||
echo "$(date '+%F %T') reloaded honk-core"
|
||||
else
|
||||
echo "$(date '+%F %T') honk-core reload failed"
|
||||
fi
|
||||
) </dev/null >>"$LOG" 2>&1 &
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -f "$LOCK" ]; then
|
||||
# Lock is fresh (< 5 min)? Refuse. Stale? Remove and proceed.
|
||||
mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0)
|
||||
age=$(( $(date +%s) - mtime ))
|
||||
if [ "$age" -lt 300 ]; then
|
||||
echo "${TYPE} update already in progress (PID $(cat "$LOCK" 2>/dev/null), age ${age}s)" >&2
|
||||
exit 75
|
||||
fi
|
||||
rm -f "$LOCK"
|
||||
fi
|
||||
|
||||
if ! ( set -C; echo "$$" >"$LOCK" ) 2>/dev/null; then
|
||||
echo "${TYPE} update already in progress" >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
# Spawn detached worker — parent returns immediately to LuCI.
|
||||
(
|
||||
exec >"$LOG" 2>&1
|
||||
trap 'rm -f "$LOCK"' EXIT INT TERM
|
||||
|
||||
rc=0
|
||||
TMP="${DEST}.new"
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
echo "$(date '+%F %T') begin: $URL"
|
||||
|
||||
if ! curl -fsSL --proto '=http,https' --proto-redir '=http,https' --connect-timeout 15 --max-time 240 -o "$TMP" "$URL"; then
|
||||
echo "$(date '+%F %T') download failed"
|
||||
rm -f "$TMP"
|
||||
rc=1
|
||||
else
|
||||
size=$(wc -c < "$TMP" 2>/dev/null || echo 0)
|
||||
magic=$(hexdump -n 1 -e '1/1 "%02x"' "$TMP" 2>/dev/null)
|
||||
if [ "$size" -lt 1024 ]; then
|
||||
echo "$(date '+%F %T') file too small ($size bytes)"
|
||||
rm -f "$TMP"
|
||||
rc=2
|
||||
elif [ "$magic" != "0a" ]; then
|
||||
echo "$(date '+%F %T') invalid geodata format"
|
||||
rm -f "$TMP"
|
||||
rc=3
|
||||
else
|
||||
mv "$TMP" "$DEST"
|
||||
echo "$(date '+%F %T') updated $DEST ($size bytes)"
|
||||
schedule_backend_reload
|
||||
fi
|
||||
fi
|
||||
if [ "$rc" = 0 ]; then echo "$(date '+%F %T') ✓ 完成"; else echo "$(date '+%F %T') ✗ 失败 (rc=$rc)"; fi
|
||||
) </dev/null >/dev/null 2>&1 &
|
||||
|
||||
echo "started in background, see $LOG"
|
||||
exit 0
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# update-pkg.sh <honk|luci-app-honk>
|
||||
# Refresh package indexes and upgrade the named package via apk. Forks to
|
||||
# background; the result is streamed to /tmp/luci-app-honk.pkg.<name>.log.
|
||||
|
||||
PKG="$1"
|
||||
case "$PKG" in
|
||||
honk|luci-app-honk) ;;
|
||||
*)
|
||||
echo "usage: $0 <honk|luci-app-honk>" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
# Run from a /tmp copy so upgrading luci-app-honk (which replaces this script)
|
||||
# cannot corrupt the in-flight upgrade.
|
||||
case "$0" in
|
||||
/tmp/.honk-upd-*) ;;
|
||||
*)
|
||||
_self="/tmp/.honk-upd-$$"
|
||||
cp "$0" "$_self" 2>/dev/null && exec sh "$_self" "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
LOCK="/tmp/luci-app-honk.pkg-${PKG}.lock"
|
||||
LOG="/tmp/luci-app-honk.pkg-${PKG}.log"
|
||||
|
||||
if [ -f "$LOCK" ]; then
|
||||
mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0)
|
||||
age=$(( $(date +%s) - mtime ))
|
||||
if [ "$age" -lt 300 ]; then
|
||||
echo "${PKG} update already in progress (PID $(cat "$LOCK" 2>/dev/null), age ${age}s)" >&2
|
||||
exit 75
|
||||
fi
|
||||
rm -f "$LOCK"
|
||||
fi
|
||||
|
||||
if ! ( set -C; echo "$$" >"$LOCK" ) 2>/dev/null; then
|
||||
echo "${PKG} update already in progress" >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
(
|
||||
exec >"$LOG" 2>&1
|
||||
trap 'rm -f "$LOCK"; [ "${0#/tmp/.honk-upd-}" != "$0" ] && rm -f "$0"' EXIT INT TERM
|
||||
|
||||
echo "$(date '+%F %T') begin upgrade: $PKG"
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
(
|
||||
flock 9
|
||||
apk update 2>&1
|
||||
ver=$(apk list "$PKG" 2>/dev/null | awk -v p="$PKG" '$1 ~ "^" p "-[0-9]" { v=$1; sub("^" p "-", "", v); print v }' | sort -V | tail -1)
|
||||
if [ -n "$ver" ]; then
|
||||
constraint="$PKG=$ver"
|
||||
else
|
||||
echo "result: 软件源中没有找到 $PKG,请检查网络或软件源配置"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- apk add -s $constraint ---"
|
||||
if ! apk add -s "$constraint" 2>&1; then
|
||||
echo "note: apk 预检失败,通常是系统上其它软件包的问题,继续尝试升级"
|
||||
fi
|
||||
echo "--- apk add $constraint ---"
|
||||
apk add "$constraint" 2>&1
|
||||
exit 0
|
||||
) 9>/tmp/luci-app-honk.apk.lock
|
||||
rc=$?
|
||||
if [ "$rc" != 0 ]; then
|
||||
:
|
||||
elif ! apk list --installed 2>/dev/null | grep -q "^${PKG}-"; then
|
||||
echo "result: $PKG is not installed"
|
||||
rc=1
|
||||
elif apk list -u 2>/dev/null | grep -q "^${PKG}-"; then
|
||||
echo "result: $PKG still has a pending upgrade"
|
||||
rc=1
|
||||
else
|
||||
echo "result: $PKG is at the latest available version"
|
||||
rc=0
|
||||
fi
|
||||
else
|
||||
echo "no package manager found"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [ "$rc" = 0 ]; then echo "$(date '+%F %T') ✓ 完成"; else echo "$(date '+%F %T') ✗ 失败 (rc=$rc)"; fi
|
||||
|
||||
if [ "$PKG" = "luci-app-honk" ] && [ "$rc" = "0" ]; then
|
||||
echo "reloading rpcd to pick up new ACL"
|
||||
/etc/init.d/rpcd reload 2>&1
|
||||
fi
|
||||
) </dev/null >/dev/null 2>&1 &
|
||||
|
||||
echo "started in background, see $LOG"
|
||||
exit 0
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"admin/services/honk": {
|
||||
"title": "Honk",
|
||||
"order": 35,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-honk" ]
|
||||
}
|
||||
},
|
||||
"admin/services/honk/overview": {
|
||||
"title": "Overview",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "honk/overview"
|
||||
}
|
||||
},
|
||||
"admin/services/honk/configuration": {
|
||||
"title": "Configuration",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "honk/configuration"
|
||||
}
|
||||
},
|
||||
"admin/services/honk/logs": {
|
||||
"title": "Logs",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "honk/logs"
|
||||
}
|
||||
},
|
||||
"admin/services/honk/maintenance": {
|
||||
"title": "Maintenance",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "honk/maintenance"
|
||||
}
|
||||
},
|
||||
"admin/services/honk/dashboard": {
|
||||
"hidden": true,
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/services/honk/overview"
|
||||
}
|
||||
},
|
||||
"admin/services/honk/settings": {
|
||||
"hidden": true,
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/services/honk/overview"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"luci-app-honk": {
|
||||
"description": "Grant access to Honk controls and maintenance",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"honk": [ "status", "check", "job_status", "logs", "update_check" ],
|
||||
"network": [ "get_proto_handlers" ],
|
||||
"network.interface": [ "dump" ]
|
||||
},
|
||||
"file": {
|
||||
"/tmp/honk-backup.tar.gz": [ "read" ],
|
||||
"/usr/share/v2ray/geoip.dat": [ "read" ],
|
||||
"/usr/share/v2ray/geosite.dat": [ "read" ],
|
||||
"/tmp/luci-app-honk.geoip.log": [ "read" ],
|
||||
"/tmp/luci-app-honk.geosite.log": [ "read" ],
|
||||
"/tmp/luci-app-honk.pkg-honk.log": [ "read" ],
|
||||
"/tmp/luci-app-honk.pkg-luci-app-honk.log": [ "read" ],
|
||||
"/usr/share/luci-app-honk/pkg-info.sh honk": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/pkg-info.sh luci-app-honk": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/refresh-index.sh": [ "exec" ]
|
||||
},
|
||||
"uci": [ "network", "honk" ]
|
||||
},
|
||||
"write": {
|
||||
"cgi-io": [ "upload" ],
|
||||
"ubus": {
|
||||
"honk": [ "initialize", "start", "stop", "restart", "settings", "add_subscription", "add_nodes", "import_dae", "repair", "backup", "restore", "restore_prepare", "reset", "update_apply" ]
|
||||
},
|
||||
"file": {
|
||||
"/tmp/honk-maintenance/restore.tar.gz": [ "write" ],
|
||||
"/tmp/honk-v2-upload/import.dae": [ "write" ],
|
||||
"/usr/share/luci-app-honk/update-geo.sh geoip": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/update-geo.sh geosite": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/geo-cron.sh enable": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/geo-cron.sh disable": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/update-pkg.sh honk": [ "exec" ],
|
||||
"/usr/share/luci-app-honk/update-pkg.sh luci-app-honk": [ "exec" ]
|
||||
},
|
||||
"uci": [ "honk" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user