#!/bin/sh [ -f "/etc/openwrt_release" ] || { printf "\033[1;31m[!] Ошибка: Эта система не OpenWrt.\033[0m\n"; exit 1; } . /etc/openwrt_release # --------------------------------------------------------------- # Зафиксированная версия: ставится только обычный (не compressed) # архив linux-.tar.gz из релиза shtorm-7/sing-box-extended. # --------------------------------------------------------------- TAG="v1.14.0-extended-2.7.1" VER="${TAG#v}" # Свой Gitea: сюда загружены те же файлы, что и в релизе GitHub # (Releases -> вложения). Пока адрес не заменён - шаг пропускается. GITEA_BASE="https://git.ownsrv.ru/yanistyle/sing-box-extended/releases/download/${TAG}" GITHUB_BASE="https://github.com/shtorm-7/sing-box-extended/releases/download/${TAG}" PROXY_PREFIX="https://ghproxy.net/" DEST_FILE="/usr/bin/sing-box" REAL_BIN="/usr/libexec/sing-box-core" VERSION_CACHE="/etc/sing-box-version.cache" WORK_DIR="/tmp/sing-box-install" DNS_RESTORE=0 R="\033[1;31m" G="\033[1;32m" Y="\033[1;33m" C="\033[1;36m" N="\033[0m" restore_dns() { if [ "$DNS_RESTORE" = "1" ]; then printf "${C}[*] Восстанавливаю настройки DNS (Ignore resolv = 1)...${N}\n" uci set dhcp.@dnsmasq[0].noresolv='1' uci commit dhcp /etc/init.d/dnsmasq restart >/dev/null 2>&1 DNS_RESTORE=0 fi } trap 'printf "\n${R}[!] Установка прервана.${N}\n"; restore_dns; rm -rf "$WORK_DIR"; [ "$SERVICE_STOPPED" = "1" ] && /etc/init.d/"$SERVICE_NAME" start >/dev/null 2>&1; [ "$ZB_STOPPED" = "1" ] && /etc/init.d/zeroblock start >/dev/null 2>&1; exit 1' INT TERM fail() { printf "${R}[!] Ошибка: %s${N}\n" "$1" restore_dns rm -rf "$WORK_DIR" [ "$SERVICE_STOPPED" = "1" ] && /etc/init.d/"$SERVICE_NAME" start >/dev/null 2>&1 [ "$ZB_STOPPED" = "1" ] && /etc/init.d/zeroblock start >/dev/null 2>&1 exit 1 } if command -v curl >/dev/null 2>&1; then DOWNLOAD="curl -fsSL --insecure --connect-timeout 15 -o" elif command -v wget >/dev/null 2>&1; then DOWNLOAD="wget -q --no-check-certificate --timeout=15 -O" elif command -v uclient-fetch >/dev/null 2>&1; then DOWNLOAD="uclient-fetch -q --no-check-certificate --timeout=15 -O" else fail "Не найден curl, wget или uclient-fetch." fi # Архитектура -> суффикс файла и sha256 обычного архива этой версии case "$DISTRIB_ARCH" in aarch64*) ARCH_SUFFIX="arm64" EXPECTED_SHA="968aa00ae3c8bd2d593c506626449629a2fae744d6848ce8dc331aa8deb49a1d" ;; arm_cortex-a7* | arm_cortex-a15*) ARCH_SUFFIX="armv7" EXPECTED_SHA="e6ff11e5091d24b8a63fcee0359720747abb42b6ba7f43f0be40f105aa4817ab" ;; arm_*) ARCH_SUFFIX="armv6" EXPECTED_SHA="6c2d43c9e642c97986bc830399251228471b03b3d0c928621aa43e709d5c30ee" ;; x86_64) ARCH_SUFFIX="amd64" EXPECTED_SHA="6bae7a6f43f55e229741737b31ea7490757ae20e7b2fddc93674b4d8694951f9" ;; i386*) ARCH_SUFFIX="386" EXPECTED_SHA="bb45b69c8397cbc0776137c2c7c659117cee14478a63a5445cf1c0ccaaf77d74" ;; mipsel_*) ARCH_SUFFIX="mipsle-softfloat" EXPECTED_SHA="bcf1acc4bf5e60a9cd734acc1258dd8d80962abbfe395711c70f5b543edccc1a" ;; mips64el_*) ARCH_SUFFIX="mips64le" EXPECTED_SHA="0f2ccb1afa1ef5c07105b618eba9b8990b6d5998cebd5e7e5324ee214cc0ca19" ;; mips64_*) ARCH_SUFFIX="mips64" EXPECTED_SHA="9efaf0160aba9a930f011552dfd8b120775373cbdb025a3bd8c6c8d5ef5c7ea7" ;; mips_*) ARCH_SUFFIX="mips-softfloat" EXPECTED_SHA="c73b0b0e30bee1ed250fa7e4ae17b498b1d737189abb21f7e7f7b302f7c11414" ;; riscv64*) ARCH_SUFFIX="riscv64" EXPECTED_SHA="1bf8967d9dda812b8e5adc47e13eb220f5786a06a7dfac7165f274058c834a18" ;; *) fail "Архитектура $DISTRIB_ARCH не поддерживается." ;; esac if [ -f "/etc/init.d/podkop" ]; then SERVICE_NAME="podkop" else SERVICE_NAME="sing-box" fi FILE_NAME="sing-box-${VER}-linux-${ARCH_SUFFIX}.tar.gz" case "$GITEA_BASE" in *example.com*) GITEA_BASE="" ;; esac CURRENT_VER=$("$DEST_FILE" version 2>/dev/null | head -n 1 | awk '{print $NF}') printf "\n${C}========================================${N}\n" printf "${C} OpenWrt sing-box extended installer${N}\n" printf "${C}========================================${N}\n" printf " Версия ОС: ${Y}%s${N}\n" "${DISTRIB_RELEASE:-неизвестно}" printf " Архитектура: ${Y}%s${N} -> ${Y}%s${N}\n" "$DISTRIB_ARCH" "$ARCH_SUFFIX" printf " Сервис: ${Y}%s${N}\n" "$SERVICE_NAME" printf " Устанавливаю: ${Y}%s${N} (обычная версия)\n\n" "$TAG" if [ "$CURRENT_VER" = "$VER" ]; then printf "${Y}[!] Эта версия уже установлена. Выполняю переустановку.${N}\n" fi # Свободное место: обычный архив ~30 МБ + распакованный бинарник ~65 МБ REQ_KB=65000 TMP_FREE=$(df -Pk /tmp 2>/dev/null | awk 'NR==2 {print $4}') ROOT_FREE=$(df -Pk /root 2>/dev/null | awk 'NR==2 {print $4}') [ -z "$TMP_FREE" ] && TMP_FREE=0 [ -z "$ROOT_FREE" ] && ROOT_FREE=0 if [ "$TMP_FREE" -ge "$REQ_KB" ]; then WORK_DIR="/tmp/sing-box-install" elif [ "$ROOT_FREE" -ge "$REQ_KB" ]; then WORK_DIR="/root/sing-box-install" printf "${Y}[!] Мало места в оперативной памяти (/tmp). Использую диск (/root) для скачивания.${N}\n" else WORK_DIR="/tmp/sing-box-install" printf "${R}[!] Внимание: критически мало места! Возможна ошибка при скачивании или распаковке.${N}\n" fi rm -rf "$WORK_DIR" && mkdir -p "$WORK_DIR" cd "$WORK_DIR" || fail "Не удалось перейти в $WORK_DIR." # Пробует по очереди: свой Gitea -> GitHub -> зеркало ghproxy try_download() { for url in "${GITEA_BASE:+$GITEA_BASE/$FILE_NAME}" "$GITHUB_BASE/$FILE_NAME" "${PROXY_PREFIX}${GITHUB_BASE}/$FILE_NAME"; do [ -z "$url" ] && continue printf "${C}[*] Скачиваю: %s${N}\n" "$url" rm -f "$FILE_NAME" if $DOWNLOAD "$FILE_NAME" "$url" && [ -s "$FILE_NAME" ]; then return 0 fi printf "${Y}[!] Не удалось, пробую следующий источник...${N}\n" done return 1 } if ! try_download; then OLD_NORESOLV=$(uci get dhcp.@dnsmasq[0].noresolv 2>/dev/null) if [ "$OLD_NORESOLV" = "1" ]; then printf "${Y}[!] Источники недоступны. Пробую временно пустить DNS-трафик через провайдера...${N}\n" uci set dhcp.@dnsmasq[0].noresolv='0' uci commit dhcp /etc/init.d/dnsmasq restart >/dev/null 2>&1 DNS_RESTORE=1 sleep 3 try_download || fail "Не удалось скачать $FILE_NAME ни из одного источника." else fail "Не удалось скачать $FILE_NAME ни из одного источника." fi fi # Проверка целостности (архив мог прийти через зеркало / по --insecure) if command -v sha256sum >/dev/null 2>&1; then ACTUAL_SHA=$(sha256sum "$FILE_NAME" | awk '{print $1}') [ "$ACTUAL_SHA" = "$EXPECTED_SHA" ] || fail "Контрольная сумма не совпадает (получено $ACTUAL_SHA)." printf "${G}[+] Контрольная сумма совпала.${N}\n" else printf "${Y}[!] sha256sum не найден, проверка целостности пропущена.${N}\n" fi stop_service() { [ "$SERVICE_STOPPED" = "1" ] && return if [ -f "/etc/init.d/zeroblock" ]; then printf "${C}[*] Останавливаю Zero-Block...${N}\n" /etc/init.d/zeroblock stop >/dev/null 2>&1 ZB_STOPPED=1 fi printf "${C}[*] Останавливаю %s...${N}\n" "$SERVICE_NAME" /etc/init.d/"$SERVICE_NAME" stop >/dev/null 2>&1 killall sing-box >/dev/null 2>&1 SERVICE_STOPPED=1 sleep 2 } printf "${C}[*] Распаковываю архив...${N}\n" tar -xzf "$FILE_NAME" || fail "Ошибка распаковки архива." BIN_PATH=$(find . -type f -name sing-box | head -n 1) [ -z "$BIN_PATH" ] && fail "Бинарник не найден внутри архива." stop_service printf "${C}[*] Устанавливаю...${N}\n" # Убираем следы прежней сжатой версии (враппер + реальный бинарник + кэш версии) rm -f "$REAL_BIN" "$VERSION_CACHE" DEST_DIR=$(dirname "$DEST_FILE") STAGE_FILE="$DEST_DIR/.sing-box.tmp.$$" cp "$BIN_PATH" "$STAGE_FILE" || fail "Недостаточно места на целевом диске ($DEST_DIR)." chmod +x "$STAGE_FILE" mv -f "$STAGE_FILE" "$DEST_FILE" || { rm -f "$STAGE_FILE"; fail "Сбой замены файла."; } NEW_VER=$("$DEST_FILE" version 2>/dev/null | head -n 1 | awk '{print $NF}') [ "$NEW_VER" != "$VER" ] && fail "Версия после установки ($NEW_VER) не совпадает с ожидаемой ($VER)." cd / rm -rf "$WORK_DIR" restore_dns printf "${C}[*] Запускаю сервис...${N}\n" /etc/init.d/"$SERVICE_NAME" start >/dev/null 2>&1 || printf "${Y}[!] Не удалось запустить службу. Проверьте логи.${N}\n" if [ "$ZB_STOPPED" = "1" ]; then printf "${C}[*] Запускаю Zero-Block...${N}\n" /etc/init.d/zeroblock start >/dev/null 2>&1 || printf "${Y}[!] Не удалось запустить Zero-Block.${N}\n" fi printf "\n${G}[+] Установка завершена:${N} ${Y}${CURRENT_VER:-н/д}${N} -> ${Y}${NEW_VER}${N}\n"