diff --git a/README.md b/README.md index 6554734..6b8c670 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,8 @@ ln -s /opt/.xray-manager/xray-manager.sh /usr/sbin/xray-manager | :--- | :--- | :--- | :--- | | **table_name** | `string` | `xray` | Имя основной таблицы в `nftables`. | | **tproxy_port** | `int` | `61219` | Порт, на котором Xray/Sing-box слушает TProxy входящие соединения. | -| **tproxy_mark** | `int` | `10` | Метка (`fwmark`), используемая для маршрутизации пакетов в таблицу 110. | +| **tproxy_mark** | `string` | `10` | Метка (`fwmark`), используемая для маршрутизации пакетов в таблицу 110. | +| **client_mark** | `string` | `103` | Внутренняя метка ct mark для идентификации трафика, подлежащего проксированию. | **timeout** | `string` | `1h` | Время жизни динамических записей в сетах (для доменов). | | **ipnet_dir** | `string` | `/tmp/ipnet` | Директория для хранения списков IP-адресов в формате `.lst`. | | **dnsmasq_dir** | `string` | `/tmp/dnsmasq.d` | Директория для `.conf` файлов dnsmasq (автоматически добавляется в `confdir` по пути `/etc/config/dhcp`). | @@ -86,11 +87,11 @@ config xray-manager 'main' option tproxy_port '61219' option tproxy_mark '10' option timeout '1h' + option route_local '1' option ipnet_dir '/tmp/ipnet' option dnsmasq_dir '/tmp/dnsmasq.d' option cache_dir '/tmp/.xray-manager/cache' option active_list '/tmp/.xray-manager/active_files' - option route_local '1' ``` ### Исключения (Whitelist) diff --git a/config/xray-manager b/config/xray-manager index 9954f74..2fcbf94 100644 --- a/config/xray-manager +++ b/config/xray-manager @@ -1,92 +1,78 @@ config xray-manager 'main' - option table_name 'xray' - option tproxy_port '61219' - option tproxy_mark '10' - option timeout '1h' - option ipnet_dir '/tmp/ipnet' - option dnsmasq_dir '/tmp/dnsmasq.d' - option cache_dir '/tmp/.xray-manager/cache' - option active_list '/tmp/.xray-manager/active_files' - option route_local '1' + option table_name 'xray' + option tproxy_port '61219' + option tproxy_mark '10' + option client_mark '103' + option timeout '1h' + option route_local '1' + option ipnet_dir '/tmp/.xray-manager/ipnet' + option dnsmasq_dir '/tmp/.xray-manager/dnsmasq.d' + option cache_dir '/tmp/.xray-manager/cache' + option active_list '/tmp/.xray-manager/active_files' config xray-list 'lnet' - option name 'whitelist' - option direction 'dst' - option exclude '1' - list ip '127.0.0.0/8' - list ip '10.0.0.0/8' - list ip '172.16.0.0/12' - list ip '192.168.0.0/16' - list ip '169.254.0.0/16' - list ip '100.64.0.0/10' - list ip '224.0.0.0/4' - list ip '255.255.255.255/32' - list ip '0.0.0.0/8' - list ip '192.0.0.0/24' - list ip '192.0.2.0/24' - list ip '198.18.0.0/15' - list ip '198.51.100.0/24' - list ip '203.0.113.0/24' + option name 'whitelist' + option direction 'dst' + option exclude '1' + list ip '127.0.0.0/8' + list ip '10.0.0.0/8' + list ip '172.16.0.0/12' + list ip '192.168.0.0/16' + list ip '169.254.0.0/16' + list ip '100.64.0.0/10' + list ip '224.0.0.0/4' + list ip '255.255.255.255/32' + list ip '0.0.0.0/8' + list ip '192.0.0.0/24' + list ip '192.0.2.0/24' + list ip '198.18.0.0/15' + list ip '198.51.100.0/24' + list ip '203.0.113.0/24' config xray-list - option name 'bf2' - option direction 'dst' - option exclude '0' - list domain 'bf2hub.com' - list ip '185.107.96.22' + option name 'proxyserver' + option direction 'dst' + option exclude '1' + list ip '178.173.249.148' config xray-list - option name 'proxyserver' - option direction 'dst' - option exclude '1' - list ip '178.173.249.148' + option name 'local' + option direction 'src' + option exclude '0' + list ip '192.168.1.0/24' config xray-list - option name 'cloudflare' - option direction 'dst' - option exclude '0' - list ip '1.1.1.1' - list ip '1.0.0.1' + option name 'cf-dns' + option direction 'dst' + option exclude '1' + list ip '1.1.1.1' + list ip '1.0.0.1' config xray-list - option name 'local' - option direction 'src' - option exclude '0' - list ip '10.1.0.0/24' - list ip '10.2.0.0/24' + option name 'refilter' + option direction 'dst' + option exclude '0' + list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/community_ips.lst' + list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/discord_ips.lst' + list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/ipsum.lst' config xray-list - option name 'local_exclude' - option direction 'src' - option exclude '1' - list ip '10.1.0.6' + option name 'cdn' + option direction 'dst' + option exclude '0' + list url_ip 'https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/all/all_plain_ipv4.txt' config xray-list - option name 'refilter' - option direction 'dst' - option exclude '0' - list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/community_ips.lst' - list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/discord_ips.lst' - list url_ip 'https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/ipsum.lst' + option name 'telegram' + option direction 'dst' + option exclude '0' + list url_ip 'https://raw.githubusercontent.com/fernvenue/telegram-cidr-list/refs/heads/master/CIDRv4.txt' config xray-list - option name 'cdn' - option direction 'dst' - option exclude '0' - list url_ip 'https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/all/all_plain_ipv4.txt' - -config xray-list - option name 'telegram' - option direction 'dst' - option exclude '0' - list url_ip 'https://raw.githubusercontent.com/fernvenue/telegram-cidr-list/refs/heads/master/CIDRv4.txt' - -config xray-list - option name 'v2ray' - option direction 'dst' - option exclude '0' - list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/spotify' - list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/microsoft' - list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/openai' - \ No newline at end of file + option name 'v2ray' + option direction 'dst' + option exclude '0' + list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/spotify' + list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/microsoft' + list url_domain 'https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/openai' \ No newline at end of file diff --git a/init.d/xray-manager b/init.d/xray-manager index 33a68a8..18ebc03 100755 --- a/init.d/xray-manager +++ b/init.d/xray-manager @@ -7,8 +7,6 @@ extra_command "update" "Download and refresh remote lists" start_service() { xray-manager start - xray-manager update - procd_open_instance procd_set_param command tail -f /dev/null procd_set_param respawn diff --git a/xray-manager.sh b/xray-manager.sh index 80b765b..1f5d9b3 100755 --- a/xray-manager.sh +++ b/xray-manager.sh @@ -10,6 +10,69 @@ _log() { _get_md5() { echo "$1" | md5sum | cut -d' ' -f1; } +_init_vars() { + config_load xray-manager + + local s="@xray-manager[0]" + TABLE=$(uci -q get xray-manager.$s.table_name) + TPROXY_PORT=$(uci -q get xray-manager.$s.tproxy_port) + TPROXY_MARK=$(uci -q get xray-manager.$s.tproxy_mark) + CLIENT_MARK=$(uci -q get xray-manager.$s.client_mark) + IPNET_DIR=$(uci -q get xray-manager.$s.ipnet_dir) + DNSMASQ_DIR=$(uci -q get xray-manager.$s.dnsmasq_dir) + CACHE_DIR=$(uci -q get xray-manager.$s.cache_dir) + ACTIVE_LIST=$(uci -q get xray-manager.$s.active_list) + ROUTE_LOCAL=$(uci -q get xray-manager.$s.route_local) + + local required_vars="TABLE TPROXY_PORT TPROXY_MARK CLIENT_MARK IPNET_DIR DNSMASQ_DIR CACHE_DIR ACTIVE_LIST ROUTE_LOCAL" + + for var in $required_vars; do + eval val=\$$var + if [ -z "$val" ]; then + _log "CRITICAL: Variable '$var' is not set in UCI config!" "err" "1" + return 1 + fi + done + + mkdir -p "$IPNET_DIR" "$DNSMASQ_DIR" "$CACHE_DIR" "${ACTIVE_LIST%/*}" + return 0 +} + +_check_marks() { + local m_dec=$(printf "%d" "$CLIENT_MARK" 2>/dev/null) + local m_hex=$(printf "%x" "$m_dec" 2>/dev/null) + local m_pattern="($m_dec|0x0*$m_hex)" + + if nft list ruleset 2>/dev/null | sed "/table ip $TABLE/,/^}/d" | grep -iqE "mark.*$m_pattern"; then + _log "Mark 0x$m_hex is busy in another table!" "err" "1" + return 1 + fi + return 0 +} + +_check_dnsmasq() { + local current_dns_dir=$(uci -q get dhcp.@dnsmasq[0].confdir | awk '{print $1}') + + if [ -z "$current_dns_dir" ]; then + _log "dnsmasq.confdir is empty. Setting to $DNSMASQ_DIR" "info" "1" + uci add_list dhcp.@dnsmasq[0].confdir="$DNSMASQ_DIR" + uci commit dhcp + DNS_CHANGES=1 + elif [ "$current_dns_dir" != "$DNSMASQ_DIR" ]; then + _log "dnsmasq.confdir mismatch: $current_dns_dir" "err" "1" + _log "Expected $DNSMASQ_DIR. Check your DHCP config." "err" "1" + return 1 + fi + return 0 +} + +_load_config_full() { + _init_vars || return 1 + _check_marks || return 1 + _check_dnsmasq || return 1 + return 0 +} + _nft_init() { [ -z "$TABLE" ] && return 1 nft add table ip "$TABLE" 2>/dev/null @@ -18,7 +81,6 @@ _nft_init() { nft flush chain ip "$TABLE" prerouting nft add rule ip "$TABLE" prerouting fib daddr type local accept - [ -z "$TPROXY_MARK" ] && TPROXY_MARK="10" local mark_hex=$(printf '0x%x' "$TPROXY_MARK") ip route show table 110 | grep -q "local default" || ip route add local default dev lo table 110 @@ -33,35 +95,47 @@ _nft_init() { fi } +_nft_batch_insert() { + local table="$1" sname="$2" file="$3" + { + printf "add element ip %s %s { " "$table" "$sname" + awk '/^[[:space:]]*#/ || /^[[:space:]]*$/ { next } + { val = ($1 ~ /\//) ? $1 : $1 "/32" + printf "%s%s", (count++ ? ", " : ""), val }' "$file" + echo " }" + } | nft -f - +} + _nft_apply_data() { local sname="$1" file="$2" exc="$3" dir="$4" type="$5" changed="$6" [ -z "$TABLE" ] && return 1 - local has_output=0 - nft list chain ip "$TABLE" output >/dev/null 2>&1 && has_output=1 - + local nft_sname="s_$sname" local flags="interval" [ "$type" = "dom" ] || [ "$type" = "udom" ] && flags="interval,timeout" - nft add set ip "$TABLE" "$sname" { type ipv4_addr \; flags $flags \; auto-merge \; } 2>/dev/null + nft add set ip "$TABLE" "$nft_sname" { type ipv4_addr \; flags $flags \; auto-merge \; } 2>/dev/null - if [ "$changed" = "1" ] && [ -s "$file" ] && { [ "$type" = "ip" ] || [ "$type" = "uip" ]; }; then - _log "Set '$sname' data refreshed in NFT" - nft flush set ip "$TABLE" "$sname" - awk '{print "add element ip '$TABLE' '$sname' { " $1 " }"}' "$file" | nft -f - + if [ "$changed" = "1" ] && [ -s "$file" ] && [[ "$type" == *"ip"* ]]; then + _log "Syncing '$nft_sname' to NFT..." + nft flush set ip "$TABLE" "$nft_sname" + _nft_batch_insert "$TABLE" "$nft_sname" "$file" fi + local has_out; nft list chain ip "$TABLE" output >/dev/null 2>&1 && has_out=1 + if [ "$exc" = "1" ]; then - local selector="ip daddr"; [ "$dir" = "src" ] && selector="ip saddr" - nft insert rule ip "$TABLE" prerouting $selector "@$sname" accept - [ "$has_output" = "1" ] && nft insert rule ip "$TABLE" output $selector "@$sname" accept + local sel="ip daddr"; [ "$dir" = "src" ] && sel="ip saddr" + nft insert rule ip "$TABLE" prerouting "$sel" "@$nft_sname" accept 2>/dev/null elif [ "$dir" = "src" ]; then - nft add rule ip "$TABLE" prerouting ip saddr "@$sname" ct mark set 0x66 - [ "$has_output" = "1" ] && nft add rule ip "$TABLE" output ip saddr "@$sname" ct mark set 0x66 + nft add rule ip "$TABLE" prerouting ip saddr "@$nft_sname" ct mark set "$CLIENT_MARK" 2>/dev/null else for proto in tcp udp; do - nft add rule ip "$TABLE" prerouting ct mark 0x66 ip daddr "@$sname" meta l4proto $proto tproxy to 127.0.0.1:"$TPROXY_PORT" meta mark set "$TPROXY_MARK" accept - [ "$has_output" = "1" ] && nft add rule ip "$TABLE" output ip daddr "@$sname" meta l4proto $proto ct mark set 0x66 meta mark set "$TPROXY_MARK" accept + nft add rule ip "$TABLE" prerouting ct mark "$CLIENT_MARK" ip daddr "@$nft_sname" \ + meta l4proto "$proto" tproxy to 127.0.0.1:"$TPROXY_PORT" meta mark set "$TPROXY_MARK" accept 2>/dev/null + [ "$has_out" = "1" ] && \ + nft add rule ip "$TABLE" output ip daddr "@$nft_sname" \ + meta l4proto "$proto" ct mark set "$CLIENT_MARK" meta mark set "$TPROXY_MARK" accept 2>/dev/null done fi } @@ -69,7 +143,6 @@ _nft_apply_data() { _process_item() { local id="$1" val="$2" type="$3" dir="$4" exc="$5" fullpath hfile changed=0 - [ -z "$DNSMASQ_DIR" ] || [ -z "$IPNET_DIR" ] && return 1 [ "$type" = "dom" ] || [ "$type" = "udom" ] && fullpath="$DNSMASQ_DIR/$id.lst" || fullpath="$IPNET_DIR/$id.lst" hfile="$CACHE_DIR/$id.hash" @@ -80,9 +153,9 @@ _process_item() { local old_hash=$(cat "$hfile" 2>/dev/null) if [ "$SKIP_URL" = "1" ] && { [ "$type" = "uip" ] || [ "$type" = "udom" ]; }; then - [ "$new_hash" != "$old_hash" ] && _log "$id: Configuration changed. Run 'update' to sync!" "warn" "1" - [ -f "$fullpath" ] && { _nft_apply_data "$id" "$fullpath" "$exc" "$dir" "$type" "0"; return; } - _log "$id: File not found. Run 'update' to download." "warn" "1"; return + [ "$new_hash" != "$old_hash" ] && _log "$id: Config changed. Run update." "warn" "1" + [ -f "$fullpath" ] && _nft_apply_data "$id" "$fullpath" "$exc" "$dir" "$type" "0" + return fi if [ "$new_hash" != "$old_hash" ] || [ ! -f "$fullpath" ]; then @@ -90,22 +163,32 @@ _process_item() { case "$type" in ip) echo "$val" | tr ' ' '\n' | sed 's/\r//g; /^#/d; /^[[:space:]]*$/d; /\//! s|$|/32|' | sort -u > "$fullpath" ;; dom) - echo "$val" | tr ' ' '\n' | tr '[:upper:]' '[:lower:]' | grep -oE '([a-z0-9-]+\.)+[a-z]{2,}' | sort -u | \ - sed "s|^|nftset=/|; s|\$|/4#ip#$TABLE#$id|" > "$fullpath" + echo "$val" | tr ' ' '\n' | awk -v t="$TABLE" -v i="s_$id" ' + /^[[:space:]]*#/ || /^[[:space:]]*$/ { next } + { gsub(/\r/, ""); print "nftset=/" tolower($1) "/4#ip#" t "#" i }' | sort -u > "$fullpath" DNS_CHANGES=1 ;; uip|udom) _log "Processing $id..." "info" - local tmp_all="/tmp/dl_all_$id"; : > "$tmp_all" + local tmp_all="$CACHE_DIR/dl_all_$id"; : > "$tmp_all" + local dl_ok=0 for url in $val; do - local tmp_part="/tmp/dl_part" - curl -sSfL --connect-timeout 10 "$url" -o "$tmp_part" && { cat "$tmp_part" >> "$tmp_all"; echo "" >> "$tmp_all"; rm -f "$tmp_part"; } + curl -sSfL --connect-timeout 10 "$url" >> "$tmp_all" && dl_ok=1 + echo "" >> "$tmp_all" done - if [ -s "$tmp_all" ]; then - local out="/tmp/out_$id" - [ "$type" = "uip" ] && sed 's/\r//g; /^#/d; /^[[:space:]]*$/d; /\//! s|$|/32|' "$tmp_all" | sort -u > "$out" || \ - { tr '[:upper:]' '[:lower:]' < "$tmp_all" | grep -oE '([a-z0-9-]+\.)+[a-z]{2,}' | sort -u | sed "s|^|nftset=/|; s|\$|/4#ip#$TABLE#$id|" > "$out"; DNS_CHANGES=1; } - mv "$out" "$fullpath"; rm -f "$tmp_all"; changed=1 - fi ;; + if [ "$dl_ok" = "1" ] && [ -s "$tmp_all" ]; then + local out="$CACHE_DIR/out_$id" + if [ "$type" = "uip" ]; then + sed 's/\r//g; /^#/d; /^[[:space:]]*$/d' "$tmp_all" | \ + awk '{print ($1 ~ /\//) ? $1 : $1"/32"}' | sort -u > "$out" + else + tr '[:upper:]' '[:lower:]' < "$tmp_all" | \ + grep -oE '([a-z0-9-]+\.)+[a-z]{2,}' | sort -u | \ + sed "s|^|nftset=/|; s|\$|/4#ip#$TABLE#s_$id|" > "$out" + DNS_CHANGES=1 + fi + mv "$out" "$fullpath"; changed=1 + fi + rm -f "$tmp_all" ;; esac echo "$new_hash" > "$hfile" fi @@ -116,15 +199,19 @@ _process_section() { local s="$1" name dir exc ips doms uips udoms config_get name "$s" name; config_get dir "$s" direction "dst"; config_get exc "$s" exclude "0" [ -z "$name" ] && return + + name=${name//[^a-zA-Z0-9_]/} + _append_list() { CURRENT_VAL="${CURRENT_VAL}${CURRENT_VAL:+ }$1"; } CURRENT_VAL=""; config_list_foreach "$s" ip _append_list; ips="$CURRENT_VAL" CURRENT_VAL=""; config_list_foreach "$s" domain _append_list; doms="$CURRENT_VAL" CURRENT_VAL=""; config_list_foreach "$s" url_ip _append_list; uips="$CURRENT_VAL" CURRENT_VAL=""; config_list_foreach "$s" url_domain _append_list; udoms="$CURRENT_VAL" - [ -n "$(echo "$ips" | xargs)" ] && _process_item "${name}_ip" "$ips" "ip" "$dir" "$exc" - [ -n "$(echo "$doms" | xargs)" ] && _process_item "${name}_dom" "$doms" "dom" "$dir" "$exc" - [ -n "$(echo "$uips" | xargs)" ] && _process_item "${name}_uip" "$uips" "uip" "$dir" "$exc" - [ -n "$(echo "$udoms" | xargs)" ] && _process_item "${name}_udom" "$udoms" "udom" "$dir" "$exc" + + [ -n "$ips" ] && _process_item "${name}_ip" "$ips" "ip" "$dir" "$exc" + [ -n "$doms" ] && _process_item "${name}_dom" "$doms" "dom" "$dir" "$exc" + [ -n "$uips" ] && _process_item "${name}_uip" "$uips" "uip" "$dir" "$exc" + [ -n "$udoms" ] && _process_item "${name}_udom" "$udoms" "udom" "$dir" "$exc" } _cleanup_orphans() { @@ -133,39 +220,39 @@ _cleanup_orphans() { [ -e "$f" ] || continue if ! grep -Fxq "$f" "$ACTIVE_LIST" 2>/dev/null; then local base="${f##*/}" - [ "$(dirname "$f")" = "$DNSMASQ_DIR" ] && DNS_CHANGES=1 - rm -f "$f" "$CACHE_DIR/${base%.*}.hash" + local sname="${base%.*}" + _log "Orphan detected: $sname. Cleaning up..." "info" + nft delete set ip "$TABLE" "s_$sname" 2>/dev/null + [[ "$f" == "$DNSMASQ_DIR"* ]] && DNS_CHANGES=1 + rm -f "$f" "$CACHE_DIR/$sname.hash" fi done rm -f "$ACTIVE_LIST" } -_load_config() { - config_load xray-manager - TABLE=$(uci -q get xray-manager.@xray-manager[0].table_name) - TPROXY_PORT=$(uci -q get xray-manager.@xray-manager[0].tproxy_port) - TPROXY_MARK=$(uci -q get xray-manager.@xray-manager[0].tproxy_mark) - IPNET_DIR=$(uci -q get xray-manager.@xray-manager[0].ipnet_dir) - DNSMASQ_DIR=$(uci -q get xray-manager.@xray-manager[0].dnsmasq_dir) - CACHE_DIR="/tmp/xray-manager.cache"; ACTIVE_LIST="/tmp/xray_active_files" - ROUTE_LOCAL=$(uci -q get xray-manager.@xray-manager[0].route_local) - mkdir -p "$IPNET_DIR" "$DNSMASQ_DIR" "$CACHE_DIR" - if ! uci show dhcp.@dnsmasq[0].confdir 2>/dev/null | grep -q "'$DNSMASQ_DIR'"; then - uci add_list dhcp.@dnsmasq[0].confdir="$DNSMASQ_DIR"; uci commit dhcp; DNS_CHANGES=1 - fi -} - _run() { - DNS_CHANGES=0; _load_config || return 1; : > "$ACTIVE_LIST"; _nft_init || return 1 + DNS_CHANGES=0 + + if [ "$FULL_LOAD" = "1" ]; then + _load_config_full || return 1 + else + _init_vars || return 1 + fi + + : > "$ACTIVE_LIST" + _nft_init || return 1 + local lnet_val=$(uci -q get xray-manager.@lnet[0].ip) [ -n "$lnet_val" ] && _process_item "lnet" "$lnet_val" "ip" "dst" "1" + config_foreach _process_section "xray-list" _cleanup_orphans + [ "$DNS_CHANGES" = "1" ] && /etc/init.d/dnsmasq restart } _stop() { - _load_config || exit 1 + _load_config_full || exit 1 nft delete table ip "$TABLE" 2>/dev/null ip rule del fwmark "$TPROXY_MARK" table 110 2>/dev/null ip route del local default dev lo table 110 2>/dev/null @@ -174,9 +261,16 @@ _stop() { } case "$1" in - start|reload) export SKIP_URL=1; _run ;; - update) export SKIP_URL=0; _run ;; - stop) _stop ;; - restart) _stop; export SKIP_URL=1; _run ;; - *) echo "Usage: $0 {start|reload|update|stop|restart}" ;; + start) + export SKIP_URL=0 FULL_LOAD=1; _run ;; + restart) + _stop; export SKIP_URL=0 FULL_LOAD=1; _run ;; + reload) + export SKIP_URL=1 FULL_LOAD=0; _run ;; + update) + export SKIP_URL=0 FULL_LOAD=0; _run ;; + stop) + _stop ;; + *) + echo "Usage: $0 {start|reload|update|stop|restart}" ;; esac \ No newline at end of file