Files
zapret-discord-youtube-linux/service.sh
OpenCode Agent 62ec6c5749 fix(core): critical fixes for service menu, autotest, strategy runner, dependencies
- service.sh: split service_remove into internal (no prompts) and interactive
  versions to prevent 'eternal wait' when installing new strategy.
  Install now calls service_remove_internal > /dev/null instead of service_remove.
  Also filter strategy list to general*.sh only to avoid clutter.
  Systemd ExecStart now points to run_strategy.sh <name> consistently.

- run_strategy.sh: add SIGTERM/SIGINT trap cleanup_strategy() that kills nfqws
  and cleans up firewall. Prevents stale nfqws/firewall rules after autotest kill.
  Also handles nfqws exit gracefully with final cleanup.

- autotest.sh: rewritten to test strategies by config name (not wrapper filename).
  Stop now sends SIGTERM to wrapper (which triggers trap cleanup) instead of
  bare kill. Added extra sleep after stop to let trap fire.
  Auto-install ExecStart fixed to run_strategy.sh <strategy>.

- setup.sh: added libmnl-dev and zlib1g-dev to Ubuntu/Debian dependency install
  to prevent build failures (missing libmnl/libmnl.h and zlib.h).

- general*.sh: removed UTF-8 BOM (0xEF 0xBB 0xBF) that caused 'exec format error'
  when running scripts on Linux. All 19 wrappers cleaned.

- ensure_wrappers.sh: always rewrite wrappers to ensure no stale BOM or paths.

Fixes: eternal wait on menu option 1, nfqws build failure, stale processes after test.
2026-05-10 22:47:48 +04:00

580 lines
17 KiB
Bash
Executable File

#!/bin/bash
# service.sh - Zapret Service Manager for Linux
# Analogous to service.bat on Windows
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/functions.sh"
# ============================================
# External commands (called from general*.sh)
# ============================================
if [[ "$1" == "status_zapret" ]]; then
check_nfqws >/dev/null 2>&1 || true
tcp_enable_timestamps
exit 0
fi
if [[ "$1" == "check_updates" ]]; then
if [[ "${2:-}" != "soft" ]]; then
bash "$0" check_updates soft >&1 &
exit 0
else
check_for_updates soft
exit 0
fi
fi
if [[ "$1" == "load_game_filter" ]]; then
get_game_filter
exit 0
fi
if [[ "$1" == "load_user_lists" ]]; then
load_user_lists
exit 0
fi
# ============================================
# MENU
# ============================================
show_menu() {
local ipset_status game_filter_status check_updates_status current_strategy
ipset_status=$(ipset_switch_status)
game_filter_status=$(game_switch_status)
check_updates_status=$(check_updates_switch_status)
current_strategy=$(get_strategy_name)
echo ""
echo " ZAPRET SERVICE MANAGER v$LOCAL_VERSION"
echo " $current_strategy"
echo " ----------------------------------------"
echo ""
echo " :: SERVICE"
echo " 1. Install Service"
echo " 2. Remove Services"
echo " 3. Check Status"
echo ""
echo " :: SETTINGS"
echo " 4. Game Filter [$game_filter_status]"
echo " 5. IPSet Filter [$ipset_status]"
echo " 6. Auto-Update Check [$check_updates_status]"
echo ""
echo " :: UPDATES"
echo " 7. Update IPSet List"
echo " 8. Update Hosts File"
echo " 9. Check for Updates"
echo ""
echo " :: TOOLS"
echo " 10. Run Diagnostics"
echo " 11. Run Tests"
echo ""
echo " ----------------------------------------"
echo " 0. Exit"
echo ""
}
# ============================================
# STATUS HELPERS
# ============================================
get_strategy_name() {
if [[ -f "$SCRIPT_DIR/.service/installed_strategy" ]]; then
local strat
strat=$(cat "$SCRIPT_DIR/.service/installed_strategy")
echo "Current strategy: $strat"
else
echo "No service installed"
fi
}
ipset_switch_status() {
if [[ -f "$SCRIPT_DIR/.service/ipset_filter_any" ]]; then
echo "any"
elif [[ -f "$SCRIPT_DIR/.service/ipset_filter_loaded" ]]; then
echo "loaded"
else
echo "none"
fi
}
game_switch_status() {
if [[ -f "$SCRIPT_DIR/.service/game_filter_enabled" ]]; then
echo "enabled"
else
echo "disabled"
fi
}
check_updates_switch_status() {
if [[ -f "$SCRIPT_DIR/.service/check_updates_enabled" ]]; then
echo "enabled"
else
echo "disabled"
fi
}
# ============================================
# SERVICE INSTALL
# ============================================
service_install() {
echo ""
echo "[*] Pick one of the strategies:"
echo ""
local files=()
local i=1
while IFS= read -r -d '' f; do
local name
name=$(basename "$f")
# Only list strategy wrappers (general*.sh), skip service/utility scripts
[[ "$name" == general*.sh ]] || continue
files+=("$f")
local desc
desc=$(describe_strategy "$f")
printf " %2d. %-35s %s\n" "$i" "$name" "$desc"
((i++))
done < <(find "$SCRIPT_DIR" -maxdepth 1 -type f -name 'general*.sh' -print0 | sort -z)
echo ""
read -rp " Input file index (number): " choice
if [[ -z "$choice" ]]; then
echo "The choice is empty, exiting..."
return
fi
local selected="${files[$((choice-1))]}"
if [[ -z "$selected" ]]; then
echo "Invalid choice, exiting..."
return
fi
local strategy_name
strategy_name=$(basename "$selected" .sh)
echo ""
echo "[*] Installing strategy: $strategy_name"
# Stop any existing service (internal — no prompts)
service_remove_internal > /dev/null 2>&1 || true
# Write strategy marker
mkdir -p "$SCRIPT_DIR/.service"
echo "$strategy_name" > "$SCRIPT_DIR/.service/installed_strategy"
# Create systemd service file
local service_path="$SYSTEMD_DIR/zapret.service"
cat > "$service_path" <<EOF
[Unit]
Description=Zapret DPI bypass (strategy: $strategy_name)
After=network.target
[Service]
Type=simple
ExecStart=$SCRIPT_DIR/run_strategy.sh $strategy_name
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Install and enable
if command -v systemctl >/dev/null 2>&1; then
cp "$service_path" /etc/systemd/system/zapret.service
systemctl daemon-reload
systemctl enable zapret.service
systemctl start zapret.service
echo ""
print_green "[OK] Service installed and started."
echo " Strategy: $strategy_name"
echo " systemd: /etc/systemd/system/zapret.service"
else
echo ""
print_yellow "[!] systemctl not found. Service file created but not installed automatically."
echo " To start manually: $SCRIPT_DIR/run_strategy.sh $strategy_name"
fi
read -rp "Press Enter to continue..."
}
# ============================================
# SERVICE REMOVE (internal — no prompts)
# ============================================
service_remove_internal() {
# Stop systemd service
if command -v systemctl >/dev/null 2>&1; then
systemctl stop zapret.service 2>/dev/null || true
systemctl disable zapret.service 2>/dev/null || true
rm -f /etc/systemd/system/zapret.service
systemctl daemon-reload
fi
# Kill nfqws
pkill -f "nfqws.*qnum=$NFQUEUE_NUM" 2>/dev/null || true
sleep 1
# Cleanup firewall
cleanup_firewall
# Remove markers
rm -f "$SCRIPT_DIR/.service/installed_strategy"
}
# ============================================
# SERVICE REMOVE (interactive)
# ============================================
service_remove() {
echo ""
echo "[*] Removing services..."
service_remove_internal
echo ""
print_green "[OK] Services removed."
read -rp "Press Enter to continue..."
}
# ============================================
# SERVICE STATUS
# ============================================
service_status() {
echo ""
echo "[*] Service Status"
echo ""
# Check systemd
if command -v systemctl >/dev/null 2>&1; then
if systemctl is-active zapret.service >/dev/null 2>&1; then
print_green "zapret.service is RUNNING."
else
print_red "zapret.service is NOT running."
fi
fi
# Check nfqws process
if pgrep -f "nfqws.*qnum=$NFQUEUE_NUM" >/dev/null 2>&1; then
print_green "nfqws process is RUNNING."
else
print_red "nfqws process is NOT running."
fi
# Check iptables/nftables rules
if nft list table ip $NFT_TABLE >/dev/null 2>&1; then
print_green "nftables rules are LOADED."
elif iptables -t mangle -L -n 2>/dev/null | grep -q "NFQUEUE"; then
print_green "iptables rules are LOADED."
else
print_red "No firewall rules found."
fi
# Show strategy
local strat
strat=$(get_strategy_name)
echo ""
echo "Strategy: $strat"
echo ""
read -rp "Press Enter to continue..."
}
# ============================================
# GAME FILTER SWITCH
# ============================================
game_switch() {
echo ""
if [[ -f "$SCRIPT_DIR/.service/game_filter_enabled" ]]; then
rm -f "$SCRIPT_DIR/.service/game_filter_enabled"
echo "[*] Game Filter: DISABLED"
echo " (Restart strategy to apply)"
else
touch "$SCRIPT_DIR/.service/game_filter_enabled"
echo "[*] Game Filter: ENABLED"
echo " (Restart strategy to apply)"
fi
read -rp "Press Enter to continue..."
}
# ============================================
# IPSET FILTER SWITCH
# ============================================
ipset_switch() {
echo ""
echo "[*] IPSet Filter options:"
echo " 1. none - no IPs checked"
echo " 2. loaded - check against ipset-all.txt"
echo " 3. any - any IP goes through filter"
echo ""
read -rp " Select option (1-3): " choice
rm -f "$SCRIPT_DIR/.service/ipset_filter_none"
rm -f "$SCRIPT_DIR/.service/ipset_filter_loaded"
rm -f "$SCRIPT_DIR/.service/ipset_filter_any"
case "$choice" in
1) touch "$SCRIPT_DIR/.service/ipset_filter_none" ;;
2) touch "$SCRIPT_DIR/.service/ipset_filter_loaded" ;;
3) touch "$SCRIPT_DIR/.service/ipset_filter_any" ;;
*) echo "Invalid choice"; return ;;
esac
echo "[*] IPSet filter updated. (Restart strategy to apply)"
read -rp "Press Enter to continue..."
}
# ============================================
# AUTO-UPDATE SWITCH
# ============================================
check_updates_switch() {
echo ""
if [[ -f "$SCRIPT_DIR/.service/check_updates_enabled" ]]; then
rm -f "$SCRIPT_DIR/.service/check_updates_enabled"
echo "[*] Auto-Update Check: DISABLED"
else
touch "$SCRIPT_DIR/.service/check_updates_enabled"
echo "[*] Auto-Update Check: ENABLED"
fi
read -rp "Press Enter to continue..."
}
# ============================================
# UPDATE IPSET LIST
# ============================================
ipset_update() {
echo ""
echo "[*] Updating IPSet list..."
local url="https://raw.githubusercontent.com/Flowseal/zapret-discord-youtube/main/.service/ipset-all.txt"
if curl -sfL "$url" -o "$LISTS_DIR/ipset-all.txt.new" 2>/dev/null; then
mv "$LISTS_DIR/ipset-all.txt.new" "$LISTS_DIR/ipset-all.txt"
print_green "[OK] ipset-all.txt updated."
else
print_red "[!] Failed to update ipset-all.txt"
echo " (URL may be unavailable)"
fi
read -rp "Press Enter to continue..."
}
# ============================================
# UPDATE HOSTS FILE
# ============================================
hosts_update() {
echo ""
echo "[*] Hosts File Manager"
echo ""
local hosts_file="$SCRIPT_DIR/.service/hosts"
if [[ ! -f "$hosts_file" ]]; then
print_red "[!] Local hosts file not found: $hosts_file"
read -rp "Press Enter to continue..."
return
fi
echo "[*] Local hosts entries (for GitHub, Telegram, Discord):"
echo ""
grep -v '^#' "$hosts_file" | grep -v '^$' | head -n 20
local total
total=$(grep -v '^#' "$hosts_file" | grep -v '^$' | wc -l)
echo ""
echo "Total entries: $total"
echo ""
read -rp "Append these entries to /etc/hosts? [y/N]: " ans
if [[ "$ans" == [yY]* ]]; then
# Backup /etc/hosts
sudo cp /etc/hosts "/etc/hosts.backup.$(date +%s)"
echo "" | sudo tee -a /etc/hosts > /dev/null
echo "# --- zapret-discord-youtube-linux hosts ---" | sudo tee -a /etc/hosts > /dev/null
grep -v '^#' "$hosts_file" | grep -v '^$' | sudo tee -a /etc/hosts > /dev/null
echo "# --- end zapret hosts ---" | sudo tee -a /etc/hosts > /dev/null
print_green "[OK] Entries added to /etc/hosts"
echo " Backup saved as /etc/hosts.backup.*"
else
echo "[*] Skipped. To add manually:"
echo " sudo nano /etc/hosts"
echo " Then copy from: $hosts_file"
fi
read -rp "Press Enter to continue..."
}
# ============================================
# CHECK FOR UPDATES
# ============================================
check_for_updates() {
local mode="${1:-interactive}"
local github_version_url="https://raw.githubusercontent.com/Flowseal/zapret-discord-youtube/main/.service/version.txt"
local github_release_url="https://github.com/Flowseal/zapret-discord-youtube/releases/latest"
local github_version
github_version=$(curl -sfL "$github_version_url" 2>/dev/null | head -n1 | tr -d '[:space:]')
if [[ -z "$github_version" ]]; then
echo "Warning: failed to fetch the latest version."
if [[ "$mode" == "interactive" ]]; then
read -rp "Press Enter to continue..."
fi
return
fi
if [[ "$LOCAL_VERSION" == "$github_version" ]]; then
echo "Latest version installed: $LOCAL_VERSION"
if [[ "$mode" == "interactive" ]]; then
read -rp "Press Enter to continue..."
fi
return
fi
echo ""
echo "New version available: $github_version"
echo "Your version: $LOCAL_VERSION"
echo "Release page: $github_release_url"
echo ""
if [[ "$mode" == "interactive" ]]; then
read -rp "Open download page? [Y/n]: " ans
if [[ "${ans:-Y}" == [yY]* ]]; then
if [[ -n "${DISPLAY:-}" ]] && command -v xdg-open >/dev/null 2>&1; then
xdg-open "$github_release_url"
elif [[ -n "${DISPLAY:-}" ]] && command -v gnome-open >/dev/null 2>&1; then
gnome-open "$github_release_url"
else
echo "Please open: $github_release_url"
fi
fi
read -rp "Press Enter to continue..."
fi
}
# ============================================
# DIAGNOSTICS
# ============================================
service_diagnostics() {
echo ""
echo "[*] Running Diagnostics"
echo ""
# Check root/sudo
if [[ "$EUID" -ne 0 ]]; then
print_yellow "[!] Not running as root. Some checks may fail."
fi
# Check nfqws
if [[ -x "$BIN_DIR/nfqws" ]]; then
print_green "[OK] nfqws binary found"
else
print_red "[FAIL] nfqws binary NOT found"
echo " Run: ./install_nfqws.sh"
fi
# Check iptables/nftables
if command -v iptables >/dev/null 2>&1; then
print_green "[OK] iptables found"
else
print_red "[FAIL] iptables NOT found"
fi
if command -v nft >/dev/null 2>&1; then
print_green "[OK] nftables (nft) found"
else
print_yellow "[WARN] nftables (nft) NOT found"
fi
# Check kernel modules
if lsmod | grep -q "nfnetlink_queue"; then
print_green "[OK] nfnetlink_queue module loaded"
else
print_yellow "[WARN] nfnetlink_queue module not loaded"
echo " Try: sudo modprobe nfnetlink_queue"
fi
# Check Secure DNS (systemd-resolved)
if command -v resolvectl >/dev/null 2>&1; then
echo ""
echo "[*] DNS settings:"
resolvectl status 2>/dev/null | head -n 20 || true
fi
# Check for conflicting processes
echo ""
echo "[*] Checking for conflicting processes..."
if pgrep -f "goodbye-dpi\|byedpi\|zapret-win" >/dev/null 2>&1; then
print_yellow "[!] Another DPI bypass tool may be running"
else
print_green "[OK] No conflicting processes found"
fi
# Offer to clear Discord cache
echo ""
read -rp "Clear Discord cache? [y/N]: " ans
if [[ "$ans" == [yY]* ]]; then
local discord_cache="$HOME/.config/discord/Cache"
if [[ -d "$discord_cache" ]]; then
rm -rf "$discord_cache"/* 2>/dev/null || true
print_green "[OK] Discord cache cleared."
else
print_yellow "[!] Discord cache directory not found."
fi
fi
echo ""
read -rp "Press Enter to continue..."
}
# ============================================
# RUN TESTS
# ============================================
run_tests() {
echo ""
echo "[*] Running Tests"
echo ""
echo " 1. Standard tests (check common sites)"
echo " 2. DPI checkers (Cloudflare, Amazon, etc.)"
echo ""
read -rp " Select option (1-2): " choice
case "$choice" in
1) bash "$UTILS_DIR/test_zapret.sh" standard ;;
2) bash "$UTILS_DIR/test_zapret.sh" dpi ;;
*) echo "Invalid choice" ;;
esac
read -rp "Press Enter to continue..."
}
# ============================================
# MAIN LOOP
# ============================================
main() {
# Require root for service management
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
print_red "[!] This script must be run as root (or with sudo)"
echo " sudo $0"
exit 1
fi
while true; do
clear 2>/dev/null || true
show_menu
read -rp " Select option (0-11): " menu_choice
case "$menu_choice" in
1) service_install ;;
2) service_remove ;;
3) service_status ;;
4) game_switch ;;
5) ipset_switch ;;
6) check_updates_switch ;;
7) ipset_update ;;
8) hosts_update ;;
9) check_for_updates ;;
10) service_diagnostics ;;
11) run_tests ;;
0) echo "Goodbye!"; exit 0 ;;
*) echo "Invalid option" ; sleep 1 ;;
esac
done
}
main