diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..80f9c3a --- /dev/null +++ b/install.sh @@ -0,0 +1,233 @@ +#!/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://gitea.example.com/USER/REPO/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"