#!/bin/bash set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' BINARY_PATH="${BINARY_PATH:-/usr/local/bin/sing-box}" RELEASE_BASE_URL="${RELEASE_BASE_URL:-https://s3.cloudyun.top/downloads/singbox}" SERVICE_CANDIDATES=("${SERVICE_NAME:-}" "singbox" "sing-box") echo -e "${GREEN}sing-box binary update script${NC}" if [[ ${EUID} -ne 0 ]]; then echo -e "${RED}This script must be run as root${NC}" exit 1 fi OS="$(uname -s)" if [[ "$OS" != "Linux" ]]; then echo -e "${RED}This update script currently supports Linux only. Current OS: ${OS}${NC}" exit 1 fi ARCH="$(uname -m)" case "$ARCH" in x86_64) BINARY_ARCH="amd64" ;; aarch64|arm64) BINARY_ARCH="arm64" ;; armv7l|armv7) BINARY_ARCH="armv7" ;; *) echo -e "${RED}Unsupported architecture: ${ARCH}${NC}" exit 1 ;; esac DOWNLOAD_TARGET="${DOWNLOAD_TARGET:-linux-${BINARY_ARCH}}" DOWNLOAD_URL="${DOWNLOAD_URL:-${RELEASE_BASE_URL}/sing-box-${DOWNLOAD_TARGET}}" TMP_BINARY="$(mktemp)" BACKUP_BINARY="$(mktemp)" cleanup() { rm -f "${TMP_BINARY}" "${BACKUP_BINARY}" } trap cleanup EXIT detect_service_name() { local candidate for candidate in "${SERVICE_CANDIDATES[@]}"; do if [[ -z "${candidate}" ]]; then continue fi if systemctl list-unit-files --type=service --no-legend 2>/dev/null | awk '{print $1}' | grep -Fxq "${candidate}.service"; then printf '%s\n' "${candidate}" return 0 fi done return 1 } download_binary() { echo -e "${YELLOW}Downloading sing-box binary...${NC}" echo -e "${YELLOW}Target: ${DOWNLOAD_TARGET}${NC}" echo -e "${YELLOW}URL: ${DOWNLOAD_URL}${NC}" if command -v curl >/dev/null 2>&1; then curl -fL "${DOWNLOAD_URL}" -o "${TMP_BINARY}" elif command -v wget >/dev/null 2>&1; then wget -O "${TMP_BINARY}" "${DOWNLOAD_URL}" else echo -e "${RED}Neither curl nor wget is installed.${NC}" exit 1 fi chmod 0755 "${TMP_BINARY}" } validate_binary() { if ! "${TMP_BINARY}" version >/dev/null 2>&1; then echo -e "${RED}Downloaded file is not a valid sing-box binary.${NC}" exit 1 fi } current_version() { if [[ -x "${BINARY_PATH}" ]]; then "${BINARY_PATH}" version 2>/dev/null | head -n 1 || true fi } new_version() { "${TMP_BINARY}" version 2>/dev/null | head -n 1 || true } rollback() { echo -e "${RED}Update failed, rolling back to previous binary...${NC}" install -m 0755 "${BACKUP_BINARY}" "${BINARY_PATH}" if [[ -n "${SERVICE_NAME_DETECTED:-}" ]]; then systemctl restart "${SERVICE_NAME_DETECTED}" || true fi } download_binary validate_binary OLD_VERSION="$(current_version)" NEW_VERSION="$(new_version)" if [[ -n "${OLD_VERSION}" ]]; then echo -e "${YELLOW}Current version: ${OLD_VERSION}${NC}" else echo -e "${YELLOW}Current version: not installed or unreadable${NC}" fi echo -e "${YELLOW}New version: ${NEW_VERSION}${NC}" if [[ -x "${BINARY_PATH}" ]]; then cp -f "${BINARY_PATH}" "${BACKUP_BINARY}" else : > "${BACKUP_BINARY}" fi install -m 0755 "${TMP_BINARY}" "${BINARY_PATH}" SERVICE_NAME_DETECTED="$(detect_service_name || true)" if [[ -n "${SERVICE_NAME_DETECTED}" ]]; then echo -e "${YELLOW}Restarting service: ${SERVICE_NAME_DETECTED}${NC}" if ! systemctl restart "${SERVICE_NAME_DETECTED}"; then if [[ -s "${BACKUP_BINARY}" ]]; then rollback fi exit 1 fi echo -e "${GREEN}Service restarted successfully.${NC}" else echo -e "${YELLOW}No systemd service detected. Binary updated only.${NC}" fi echo -e "${GREEN}sing-box has been updated successfully.${NC}" echo -e "${GREEN}Binary: ${BINARY_PATH}${NC}" echo -e "${GREEN}Version: $(current_version)${NC}"