#!/usr/bin/env bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH #================================================= # System Required: CentOS/Debian/Ubuntu # Description: Snell Server 管理脚本 # Author: 翠花 # WebSite: https://aapls.com #================================================= sh_ver="1.9.6" snell_v2_version="2.0.6" snell_v3_version="3.0.1" snell_v4_version="4.1.1" snell_v5_version="5.0.1" snell_v6_version="6.0.0b4" script_dir=$(cd "$(dirname "$0")"; pwd) script_path=$(echo -e "${script_dir}"|awk -F "$0" '{print $1}') snell_dir="/etc/snell/" snell_bin="/usr/local/bin/snell-server" snell_conf="/etc/snell/config.conf" snell_version_file="/etc/snell/ver.txt" sysctl_conf="/etc/sysctl.d/99-snell.conf" Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && Font_color_suffix="\033[0m" && Yellow_font_prefix="\033[0;33m" Info="${Green_font_prefix}[信息]${Font_color_suffix}" Error="${Red_font_prefix}[警告]${Font_color_suffix}" Tip="${Yellow_font_prefix}[提示]${Font_color_suffix}" # 检查是否为 Root 用户 checkRoot(){ [[ $EUID != 0 ]] && echo -e "${Error} 当前非ROOT账号(或没有ROOT权限),无法继续操作,请更换ROOT账号或使用 ${Green_background_prefix}sudo su${Font_color_suffix} 命令获取临时ROOT权限(执行后可能会提示输入当前账号的密码)。" && exit 1 } # 检查系统类型 checkSys(){ if [[ -f /etc/redhat-release ]]; then release="centos" elif cat /etc/issue | grep -q -E -i "debian"; then release="debian" elif cat /etc/issue | grep -q -E -i "ubuntu"; then release="ubuntu" elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat"; then release="centos" elif cat /proc/version | grep -q -E -i "debian"; then release="debian" elif cat /proc/version | grep -q -E -i "ubuntu"; then release="ubuntu" elif cat /proc/version | grep -q -E -i "centos|red hat|redhat"; then release="centos" fi } # 检查依赖 checkDependencies(){ local deps=("wget" "unzip" "ss") for cmd in "${deps[@]}"; do if ! command -v "$cmd" &> /dev/null; then echo -e "${Error} 缺少依赖: $cmd,正在尝试安装..." if [[ -f /etc/debian_version ]]; then apt-get update && apt-get install -y "$cmd" elif [[ -f /etc/redhat-release ]]; then yum install -y "$cmd" else echo -e "${Error} 不支持的系统,无法自动安装 $cmd" exit 1 fi fi done echo -e "${Info} 依赖检查完成" } # 安装依赖 installDependencies(){ if [[ ${release} == "centos" ]]; then yum update yum install gzip wget curl unzip -y else apt-get update apt-get install gzip wget curl unzip -y fi sysctl -w net.core.rmem_max=26214400 sysctl -w net.core.rmem_default=26214400 \cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo -e "${Info} 依赖安装完成" } # 检查系统架构 sysArch() { uname=$(uname -m) if [[ "$uname" == "i686" ]] || [[ "$uname" == "i386" ]]; then arch="i386" elif [[ "$uname" == *"armv7"* ]] || [[ "$uname" == "armv6l" ]]; then arch="armv7l" elif [[ "$uname" == *"armv8"* ]] || [[ "$uname" == "aarch64" ]]; then arch="aarch64" else arch="amd64" fi } # 开启 TCP Fast Open enableTCPFastOpen() { kernel=$(uname -r | awk -F . '{print $1}') if [ "$kernel" -ge 3 ]; then echo 3 >/proc/sys/net/ipv4/tcp_fastopen # 创建或覆盖 Snell 专用的 sysctl 配置文件 cat > "$sysctl_conf" << EOF # Snell Server 网络优化配置 # 由 Snell 管理脚本自动生成 fs.file-max = 51200 net.core.rmem_max = 67108864 net.core.wmem_max = 67108864 net.core.rmem_default = 65536 net.core.wmem_default = 65536 net.core.netdev_max_backlog = 4096 net.core.somaxconn = 4096 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 0 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.ip_local_port_range = 10000 65000 net.ipv4.tcp_max_syn_backlog = 4096 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.tcp_fastopen = 3 net.ipv4.tcp_rmem = 4096 87380 67108864 net.ipv4.tcp_wmem = 4096 65536 67108864 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_ecn = 1 # BBR 拥塞控制 net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr EOF # 应用配置 sysctl --system >/dev/null 2>&1 echo -e "${Info} TCP Fast Open 和网络优化配置已启用!" else echo -e "${Error} 系统内核版本过低,无法支持 TCP Fast Open!" fi } # 检查 Snell 是否安装 checkInstalledStatus(){ [[ ! -e ${snell_bin} ]] && echo -e "${Error} Snell Server 没有安装,请检查!" && exit 1 } # 检查 Snell 运行状态 checkStatus(){ if systemctl is-active snell-server.service &> /dev/null; then status="running" else status="stopped" fi } # 版本号比较函数(优先正式版) compareVersions(){ local version1="$1" local version2="$2" # 移除版本号前缀 v version1=$(echo "$version1" | sed 's/^v//') version2=$(echo "$version2" | sed 's/^v//') # 如果版本号完全相同 if [[ "$version1" == "$version2" ]]; then return 1 # 相等 fi # 提取基础版本号(去除测试版后缀) local base_version1=$(echo "$version1" | sed 's/[a-z].*//') local base_version2=$(echo "$version2" | sed 's/[a-z].*//') # 检查是否为测试版 local is_beta1=false local is_beta2=false [[ "$version1" =~ [a-z] ]] && is_beta1=true [[ "$version2" =~ [a-z] ]] && is_beta2=true # 如果基础版本号相同 if [[ "$base_version1" == "$base_version2" ]]; then # 优先选择正式版 if [[ "$is_beta1" == true && "$is_beta2" == false ]]; then return 2 # version1 < version2(正式版优先) elif [[ "$is_beta1" == false && "$is_beta2" == true ]]; then return 0 # version1 > version2(正式版优先) fi # 如果都是测试版或都是正式版,使用字母序比较 if [[ "$version1" < "$version2" ]]; then return 2 else return 0 fi fi # 基础版本号不同时,使用 sort -V 进行版本号比较 if printf '%s\n' "$base_version1" "$base_version2" | sort -V | head -1 | grep -q "^$base_version1$"; then return 2 # version1 < version2 else return 0 # version1 > version2 fi } # 验证版本 URL 是否有效 validateVersionUrl(){ local version="$1" getSnellDownloadUrl "$version" # 使用 HEAD 请求检查 URL 是否有效 if curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then return 0 # URL 有效 else return 1 # URL 无效 fi } # 检查版本更新 checkVersionUpdate(){ local show_info=${1:-false} # 是否显示详细信息,默认为静默 update_available=false current_installed_version="" latest_available_version="" best_version="" if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}') # 除 Snell v6 版外的旧版本已停更,不再检查在线更新 if [[ "$current_ver" != "6" ]]; then update_available=false return 0 fi if [[ -e ${snell_version_file} ]]; then installed_version=$(cat ${snell_version_file} | sed 's/^v//') # 尝试从二进制程序获取真实版本 (容错处理) if command -v timeout >/dev/null 2>&1 && [[ -x ${snell_bin} ]]; then bin_version=$(timeout 1 ${snell_bin} --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1) [[ -z "$bin_version" ]] && bin_version=$(timeout 1 ${snell_bin} -v 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1) [[ -z "$bin_version" ]] && bin_version=$(timeout 1 ${snell_bin} --help 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1) if [[ -n "$bin_version" && "$bin_version" != "$installed_version" ]]; then installed_version="$bin_version" echo "v${installed_version}" > "${snell_version_file}" # 修复不同步的问题 fi fi current_installed_version="$installed_version" # 根据当前版本确定对应的脚本版本和网页版本 case "$current_ver" in "4") script_version=${snell_v4_version} web_version=$(getLatestVersionFromWeb "v4") ;; "5") script_version=${snell_v5_version} web_version=$(getLatestVersionFromWeb "v5") ;; "6") script_version=${snell_v6_version} web_version=$(getLatestVersionFromWeb "v6") ;; *) script_version="" web_version="" ;; esac # 优先使用脚本内置版本,除非网页版本更新 best_version="$installed_version" version_source="已安装" # 首先比较脚本内置版本 if [[ -n "$script_version" ]]; then compareVersions "$best_version" "$script_version" case $? in 2) # best_version < script_version # 验证脚本内置版本的 URL 是否有效 if validateVersionUrl "$script_version"; then best_version="$script_version" version_source="脚本内置" else [[ "$show_info" == true ]] && echo -e "${Tip} 脚本内置版本 v${script_version} 的下载链接无效,跳过" fi ;; esac fi # 然后比较网页版本,只有当网页版本比当前最佳版本更新时才采用 if [[ -n "$web_version" ]]; then compareVersions "$best_version" "$web_version" case $? in 2) # best_version < web_version # 验证网页版本的 URL 是否有效 if validateVersionUrl "$web_version"; then best_version="$web_version" version_source="官方网页" else [[ "$show_info" == true ]] && echo -e "${Tip} 网页版本 Snell v${web_version} 的下载链接无效,使用脚本内置版本" fi ;; 1|0) # best_version >= web_version # 脚本内置版本优先,无需显示 ;; esac fi latest_available_version="$best_version" # 如果最佳版本与当前安装版本不同,则有更新可用 compareVersions "$installed_version" "$best_version" if [[ $? -eq 2 ]]; then update_available=true if [[ "$show_info" == true ]]; then echo -e "${Info} 发现更新:当前版本 Snell v${installed_version} -> 最新版本 Snell v${best_version}" echo -e "${Info} 更新版本来源:${version_source}" fi fi fi fi } # 获取 Snell 下载链接 getSnellDownloadUrl(){ sysArch local version=$1 snell_url="https://dl.nssurge.com/snell/snell-server-v${version}-linux-${arch}.zip" } # 下载并安装 Snell v2 downloadSnellV2() { downloadSnellFromGitHub "${snell_v2_version}" "Snell v2 GitHub 备份源版" || downloadSnellFromGitee "${snell_v2_version}" "Snell v2 Gitee 备份源版" } # 下载并安装 Snell v3 downloadSnellV3() { downloadSnellFromGitHub "${snell_v3_version}" "Snell v3 GitHub 备份源版" || downloadSnellFromGitee "${snell_v3_version}" "Snell v3 Gitee 备份源版" } # 通用下载并安装 Snell 函数(第三方备份源) downloadSnellFromBackup(){ local version=$1 local version_type=$2 local backup_url=$3 echo -e "${Info} 试图请求 ${Yellow_font_prefix}${version_type}${Font_color_suffix} Snell Server ……" wget --no-check-certificate -N "${backup_url}" if [[ ! -e "snell-server-v${version}-linux-${arch}.zip" ]]; then echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载失败!" return 1 fi unzip -o "snell-server-v${version}-linux-${arch}.zip" if [[ ! -e "snell-server" ]]; then echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 解压失败!" return 1 fi rm -rf "snell-server-v${version}-linux-${arch}.zip" chmod +x snell-server mv -f snell-server "${snell_bin}" echo "v${version}" > "${snell_version_file}" echo -e "${Info} Snell Server 主程序下载安装完毕!" return 0 } downloadSnellFromGitHub(){ local version=$1 local version_type=$2 local backup_url="https://raw.githubusercontent.com/xOS/Others/master/snell/v${version}/snell-server-v${version}-linux-${arch}.zip" downloadSnellFromBackup "$version" "$version_type" "$backup_url" } downloadSnellFromGitee(){ local version=$1 local version_type=$2 local backup_url="https://gitee.com/ten/Others/raw/master/snell/v${version}/snell-server-v${version}-linux-${arch}.zip" downloadSnellFromBackup "$version" "$version_type" "$backup_url" } # 下载并安装 Snell v4 downloadSnellV4(){ downloadSnell "${snell_v4_version}" "Snell v4 官网源版" || downloadSnellFromGitHub "${snell_v4_version}" "Snell v4 GitHub 备份源版" || downloadSnellFromGitee "${snell_v4_version}" "Snell v4 Gitee 备份源版" } # 下载并安装 Snell v5 downloadSnellV5(){ downloadSnell "${snell_v5_version}" "Snell v5 官网源版" || downloadSnellFromGitHub "${snell_v5_version}" "Snell v5 GitHub 备份源版" || downloadSnellFromGitee "${snell_v5_version}" "Snell v5 Gitee 备份源版" } # 通用下载并安装 Snell 函数(带回退机制) downloadSnell(){ local version=$1 local version_type=$2 local allow_fallback=${3:-false} local fallback_version=$4 echo -e "${Info} 试图请求 ${Yellow_font_prefix}${version_type}${Font_color_suffix} Snell Server ……" getSnellDownloadUrl "${version}" # 首先检查 URL 是否有效 if ! curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载链接无效 (404)!" # 如果允许回退且提供了回退版本 if [[ "$allow_fallback" == true && -n "$fallback_version" ]]; then echo -e "${Info} 尝试回退到已安装版本 v${fallback_version}..." getSnellDownloadUrl "${fallback_version}" if curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then version="$fallback_version" echo -e "${Info} 回退成功,使用版本 v${version}" else echo -e "${Error} 回退版本也无法下载!" return 1 fi else return 1 fi fi wget --no-check-certificate -N "${snell_url}" if [[ ! -e "snell-server-v${version}-linux-${arch}.zip" ]]; then echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载失败!" return 1 && exit 1 else unzip -o "snell-server-v${version}-linux-${arch}.zip" fi if [[ ! -e "snell-server" ]]; then echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 解压失败!" return 1 && exit 1 else rm -rf "snell-server-v${version}-linux-${arch}.zip" chmod +x snell-server mv -f snell-server "${snell_bin}" echo "v${version}" > ${snell_version_file} echo -e "${Info} Snell Server 主程序下载安装完毕!" return 0 fi } # 安装 Snell installSnell() { if [[ ! -e "${snell_dir}" ]]; then mkdir "${snell_dir}" else [[ -e "${snell_bin}" ]] && rm -rf "${snell_bin}" fi echo -e "选择安装版本${Yellow_font_prefix}[2-6]${Font_color_suffix} ================================== ${Green_font_prefix} 2.${Font_color_suffix} v2 ${Green_font_prefix} 3.${Font_color_suffix} v3 ${Green_font_prefix} 4.${Font_color_suffix} v4 ${Green_font_prefix} 5.${Font_color_suffix} v5 ${Green_font_prefix} 6.${Font_color_suffix} v6 ==================================" read -e -p "(默认:4.v4):" ver [[ -z "${ver}" ]] && ver="4" if [[ ${ver} == "2" ]]; then installSnellV2 elif [[ ${ver} == "3" ]]; then installSnellV3 elif [[ ${ver} == "4" ]]; then installSnellV4 elif [[ ${ver} == "5" ]]; then installSnellV5 elif [[ ${ver} == "6" ]]; then installSnellV6 else installSnellV4 fi } # 配置服务 setupService(){ echo ' [Unit] Description=Snell Service After=network.target [Service] LimitNOFILE=32767 Type=simple User=root Restart=on-failure RestartSec=5s ExecStart=/usr/local/bin/snell-server -c /etc/snell/config.conf [Install] WantedBy=multi-user.target' > /etc/systemd/system/snell-server.service systemctl daemon-reload systemctl enable snell-server echo -e "${Info} Snell Server 服务配置完成!" } # 针对 Snell v6 检查密钥长度 checkPskForV6(){ if [[ ${#psk} -lt 16 ]] || [[ ${#psk} -gt 255 ]]; then echo -e "${Error} 检测到当前密钥 (${psk}) 长度不符合 16-255 位要求,Snell v6 要求升级密钥!" echo -e "请选择处理方式:" echo -e " 1. 自动生成安全的随机长密钥(推荐)" echo -e " 2. 手动输入新的长密钥" read -e -p "(默认: 1):" psk_choice [[ -z "${psk_choice}" ]] && psk_choice="1" if [[ "${psk_choice}" == "2" ]]; then while true; do read -e -p "请输入新的密钥(16-255位): " new_psk if [[ ${#new_psk} -ge 16 ]] && [[ ${#new_psk} -le 255 ]]; then psk=$new_psk break else echo -e "${Error} 密钥长度必须在 16 到 255 位之间!" fi done else psk=$(tr -dc A-Za-z0-9 0 && $0 !~ /^[[:space:]]*$/) { print $0 } } }' "${snell_conf}") if [[ -n "${custom_configs}" ]]; then config_content+=$'\n\n# Custom Configs\n'"${custom_configs}" fi fi echo "${config_content}" > "${snell_conf}" } # 读取配置文件 readConfig(){ [[ ! -e ${snell_conf} ]] && echo -e "${Error} Snell Server 配置文件不存在!" && exit 1 listen_val=$(grep -E '^listen\s*=' ${snell_conf} | sed -E 's/^listen\s*=\s*//' | xargs) port=$(echo "$listen_val" | awk -F',' '{print $1}' | awk -F':' '{print $NF}' | xargs) ipv6=$(cat ${snell_conf}|grep 'ipv6 = '|awk -F 'ipv6 = ' '{print $NF}') psk=$(cat ${snell_conf}|grep 'psk = '|awk -F 'psk = ' '{print $NF}') obfs=$(cat ${snell_conf}|grep 'obfs = '|awk -F 'obfs = ' '{print $NF}') host=$(cat ${snell_conf}|grep 'obfs-host = '|awk -F 'obfs-host = ' '{print $NF}') tfo=$(cat ${snell_conf}|grep 'tfo = '|awk -F 'tfo = ' '{print $NF}') dns=$(cat ${snell_conf}|grep 'dns = '|awk -F 'dns = ' '{print $NF}') ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}') dns_ip_pref=$(cat ${snell_conf}|grep 'dns-ip-preference = '|awk -F 'dns-ip-preference = ' '{print $NF}') [[ -z "$dns_ip_pref" && "$ver" == "6" ]] && dns_ip_pref="default" mode=$(cat ${snell_conf}|grep 'mode = '|awk -F 'mode = ' '{print $NF}') [[ -z "$mode" && "$ver" == "6" ]] && mode="default" egress_interface=$(cat ${snell_conf}|grep 'egress-interface = '|awk -F 'egress-interface = ' '{print $NF}') } # 设置端口 setPort(){ local orig_port="$port" while true; do echo -e "${Tip} 本步骤不涉及系统防火墙端口操作,请手动放行相应端口!" echo -e "请输入 Snell Server 端口${Yellow_font_prefix}[1-65535]${Font_color_suffix}" local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}: 2345):" [[ -n "$port" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${port} | ${Green_font_prefix}默认${Font_color_suffix}: 2345):" echo -e -n "${p_prompt}" read -e input_port [[ -z "${input_port}" ]] && input_port="2345" port=$input_port if [[ "${ver}" == "6" || "$current_installed_ver" == "6" ]]; then listen_val="0.0.0.0:${port},[::]:${port}" else listen_val="::0:${port}" fi if [[ $port =~ ^[0-9]+$ ]] && [[ $port -ge 1 && $port -le 65535 ]]; then if [[ "$port" != "$orig_port" ]] && ss -tuln | grep -q ":$port "; then echo -e "${Error} 端口 $port 已被占用,请选择其他端口。" else echo && echo "==============================" echo -e "端口 : ${Red_background_prefix} ${port} ${Font_color_suffix}" echo "==============================" && echo break fi else echo "输入错误, 请输入正确的端口号。" sleep 2s setPort fi done } # 设置 IPv6 setIpv6(){ echo -e "是否开启 IPv6 解析? ================================== ${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭 ==================================" local current_opt="2" [[ "$ipv6" == "true" ]] && current_opt="1" local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:2.关闭):" [[ -n "$ipv6" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${ipv6} | ${Green_font_prefix}默认${Font_color_suffix}:2.关闭):" echo -e -n "${p_prompt}" read -e input_ipv6 [[ -z "${input_ipv6}" ]] && input_ipv6="2" if [[ ${input_ipv6} == "1" ]]; then ipv6=true else ipv6=false fi echo && echo "==================================" echo -e "IPv6 解析 开启状态:${Red_background_prefix} ${ipv6} ${Font_color_suffix}" echo "==================================" && echo } # 设置密钥 setPSK(){ echo "请输入 Snell Server 密钥 [0-9][a-z][A-Z] " local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}: 随机生成):" [[ -n "$psk" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${psk} | ${Green_font_prefix}默认${Font_color_suffix}: 随机生成):" if [[ "$ver" == "6" || "$current_installed_ver" == "6" ]]; then echo -e "${Tip} 当前目标协议为 Snell v6,密钥长度要求在 16-255 位之间" while true; do echo -e -n "${p_prompt}" read -e input_psk if [[ -z "${input_psk}" ]]; then psk=$(tr -dc A-Za-z0-9 /dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${old_ver}" > ${snell_version_file} echo -e "${Info} 已回滚到 Snell v${old_ver} 版本" fi systemctl start snell-server return 1 } # 修改配置 setConfig(){ checkInstalledStatus local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}') [[ -z "$current_installed_ver" ]] && current_installed_ver=$ver echo && echo -e "请输入要操作配置项的序号,然后回车 ==============================" echo -e " ${Green_font_prefix}1.${Font_color_suffix} 修改 端口" echo -e " ${Green_font_prefix}2.${Font_color_suffix} 修改 密钥" if [[ "$current_installed_ver" != "6" ]]; then echo -e " ${Green_font_prefix}3.${Font_color_suffix} 配置 OBFS" echo -e " ${Green_font_prefix}4.${Font_color_suffix} 配置 OBFS 域名" echo -e " ${Green_font_prefix}5.${Font_color_suffix} 开关 IPv6 解析" fi echo -e " ${Green_font_prefix}6.${Font_color_suffix} 开关 TCP Fast Open" echo -e " ${Green_font_prefix}7.${Font_color_suffix} 配置 DNS" echo -e " ${Green_font_prefix}8.${Font_color_suffix} 配置 Snell 协议版本" if [[ "$current_installed_ver" == "6" ]]; then echo -e " ${Green_font_prefix}9.${Font_color_suffix} 配置 DNS IP 偏好" echo -e "${Green_font_prefix}10.${Font_color_suffix} 配置 混淆模式" fi echo -e "==============================" echo -e "${Green_font_prefix}11.${Font_color_suffix} 修改 全部配置" && echo read -e -p "(默认: 取消):" modify [[ -z "${modify}" ]] && echo "已取消..." && exit 1 if [[ "${modify}" == "1" ]]; then readConfig setPort writeConfig restartSnell elif [[ "${modify}" == "2" ]]; then readConfig setPSK writeConfig restartSnell elif [[ "${modify}" == "3" ]]; then readConfig setObfs # 在 setObfs 中已处理 host writeConfig restartSnell elif [[ "${modify}" == "4" ]]; then readConfig if [[ ${obfs} == "off" ]]; then echo -e "${Error} OBFS 当前为 off,无法修改 OBFS 域名。" else setHost writeConfig restartSnell fi elif [[ "${modify}" == "5" ]]; then readConfig setIpv6 writeConfig restartSnell elif [[ "${modify}" == "6" ]]; then readConfig setTFO writeConfig restartSnell elif [[ "${modify}" == "7" ]]; then readConfig setDNS writeConfig restartSnell elif [[ "${modify}" == "8" ]]; then readConfig local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}') [[ -z "$current_installed_ver" ]] && current_installed_ver=$ver setVer if [[ "$ver" != "$current_installed_ver" ]]; then if [[ "$ver" -gt "$current_installed_ver" ]]; then echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 升级到 Snell v${ver}" echo -e "确定要升级吗?(y/N)" else echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 降级到 Snell v${ver}" echo -e "确定要降级吗?(y/N)" fi read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} == [Yy] ]]; then if [[ "$ver" == "6" ]]; then setDNSIPPref setMode checkPskForV6 else setObfs fi switchBinary "$current_installed_ver" "$ver" if [[ $? -ne 0 ]]; then ver=$current_installed_ver else writeConfig systemctl restart snell-server for ((i=0; i<10; i++)); do sleep 1 checkStatus if [[ "$status" == "running" ]]; then break fi done if [[ "$status" != "running" ]]; then echo -e "${Error} 协议切换后服务启动失败,以下为错误日志:" journalctl -u snell-server -n 20 --no-pager echo -e "${Error} 正在回滚到原版本..." backup_file=$(ls -t "${snell_bin}".v${current_installed_ver}.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${current_installed_ver}" > ${snell_version_file} ver=$current_installed_ver writeConfig systemctl start snell-server echo -e "${Info} 已回滚到 Snell v${current_installed_ver} 版本并恢复配置" fi else echo -e "${Info} Snell Server 重启完毕!" fi sleep 3s startMenu return fi else echo -e "${Info} 已取消切换,保持原配置" ver=$current_installed_ver fi else echo -e "${Info} 版本未发生改变" fi writeConfig restartSnell elif [[ "${modify}" == "9" ]]; then readConfig if [[ "$current_installed_ver" != "6" ]]; then echo -e "${Error} 当前版本不是 Snell v6,不支持 DNS IP 偏好配置!" sleep 2s; setConfig; return fi setDNSIPPref writeConfig restartSnell elif [[ "${modify}" == "10" ]]; then readConfig if [[ "$current_installed_ver" != "6" ]]; then echo -e "${Error} 当前版本不是 Snell v6,不支持 混淆模式配置!" sleep 2s; setConfig; return fi setMode writeConfig restartSnell elif [[ "${modify}" == "11" ]]; then readConfig local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}') [[ -z "$current_installed_ver" ]] && current_installed_ver=$ver setVer if [[ "$ver" != "$current_installed_ver" ]]; then if [[ "$ver" -gt "$current_installed_ver" ]]; then echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 升级到 Snell v${ver}" echo -e "确定要升级吗?(y/N)" else echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 降级到 Snell v${ver}" echo -e "确定要降级吗?(y/N)" fi read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} != [Yy] ]]; then echo -e "${Info} 已取消切换,保持原版本" ver=$current_installed_ver fi fi setPort setPSK if [[ "$ver" != "6" ]]; then setIpv6 fi setTFO setDNS if [[ "$ver" == "6" ]]; then setDNSIPPref setMode checkPskForV6 else setObfs fi if [[ "$ver" != "$current_installed_ver" ]]; then switchBinary "$current_installed_ver" "$ver" if [[ $? -ne 0 ]]; then ver=$current_installed_ver writeConfig restartSnell else writeConfig systemctl restart snell-server for ((i=0; i<10; i++)); do sleep 1 checkStatus if [[ "$status" == "running" ]]; then break fi done if [[ "$status" != "running" ]]; then echo -e "${Error} 协议切换后服务启动失败,以下为错误日志:" journalctl -u snell-server -n 20 --no-pager echo -e "${Error} 正在回滚到原版本..." backup_file=$(ls -t "${snell_bin}".v${current_installed_ver}.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${current_installed_ver}" > ${snell_version_file} ver=$current_installed_ver writeConfig systemctl start snell-server echo -e "${Info} 已回滚到 v${current_installed_ver} 版本并恢复配置" fi else echo -e "${Info} Snell Server 重启完毕!" fi sleep 3s startMenu return fi else writeConfig restartSnell fi else echo -e "${Error} 请输入正确数字${Yellow_font_prefix}[1-11]${Font_color_suffix}" sleep 2s setConfig fi sleep 3s startMenu } # 安装 Snell v2 installSnellV2(){ checkRoot [[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装!" && exit 1 echo -e "${Info} 开始设置 配置..." setPort setPSK setObfs setIpv6 setTFO echo -e "${Info} 开始安装/配置 依赖..." checkDependencies installDependencies echo -e "${Info} 开始下载/安装..." downloadSnellV2 echo -e "${Info} 开始安装 服务脚本..." setupService echo -e "${Info} 开始写入 配置文件..." writeConfig echo -e "${Info} 所有步骤 安装完毕,开始启动..." startSnell echo -e "${Info} 启动完成,查看配置..." viewConfig } # 安装 Snell v3 installSnellV3(){ checkRoot [[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装!" && exit 1 echo -e "${Info} 开始设置 配置..." setPort setPSK setObfs setIpv6 setTFO echo -e "${Info} 开始安装/配置 依赖..." checkDependencies installDependencies echo -e "${Info} 开始下载/安装..." downloadSnellV3 echo -e "${Info} 开始安装 服务脚本..." setupService echo -e "${Info} 开始写入 配置文件..." writeConfig echo -e "${Info} 所有步骤 安装完毕,开始启动..." startSnell echo -e "${Info} 启动完成,查看配置..." viewConfig } # 安装 Snell v4 installSnellV4(){ checkRoot [[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1 echo -e "${Info} 开始设置 配置..." setPort setPSK setObfs setIpv6 setTFO setDNS echo -e "${Info} 开始安装/配置 依赖..." checkDependencies installDependencies echo -e "${Info} 开始下载/安装..." downloadSnellV4 echo -e "${Info} 开始安装 服务脚本..." setupService echo -e "${Info} 开始写入 配置文件..." writeConfig echo -e "${Info} 所有步骤 安装完毕,开始启动..." startSnell echo -e "${Info} 启动完成,查看配置..." viewConfig } # 安装 Snell v5 installSnellV5(){ checkRoot [[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1 echo -e "${Info} 开始设置 配置..." setPort setPSK setObfs setIpv6 setTFO setDNS echo -e "${Info} 开始安装/配置 依赖..." checkDependencies installDependencies echo -e "${Info} 开始下载/安装..." downloadSnellV5 echo -e "${Info} 开始安装 服务脚本..." setupService echo -e "${Info} 开始写入 配置文件..." writeConfig echo -e "${Info} 所有步骤 安装完毕,开始启动..." startSnell echo -e "${Info} 启动完成,查看配置..." viewConfig } # 下载并安装 Snell v6(官方源) downloadSnellV6(){ downloadSnell "${snell_v6_version}" "Snell v6 官网源版" } # 安装 Snell v6 installSnellV6(){ checkRoot [[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1 echo -e "${Info} 开始设置 配置..." setPort setPSK setTFO setDNS setDNSIPPref setMode echo -e "${Info} 开始安装/配置 依赖..." checkDependencies installDependencies echo -e "${Info} 开始下载/安装..." downloadSnellV6 echo -e "${Info} 开始安装 服务脚本..." setupService echo -e "${Info} 开始写入 配置文件..." writeConfig echo -e "${Info} 所有步骤 安装完毕,开始启动..." startSnell echo -e "${Info} 启动完成,查看配置..." viewConfig } # 启动 Snell startSnell(){ checkInstalledStatus checkStatus if [[ "$status" == "running" ]]; then echo -e "${Info} Snell Server 已在运行!" else echo -e "${Info} 正在启动 Snell Server..." systemctl start snell-server # 等待启动完成,最多等待10秒 local timeout=10 local elapsed=0 while [[ $elapsed -lt $timeout ]]; do sleep 1 checkStatus if [[ "$status" == "running" ]]; then echo -e "${Info} Snell Server 启动成功!" return 0 fi ((elapsed++)) done # 如果5秒后仍未启动,检查状态并报错 checkStatus if [[ "$status" == "running" ]]; then echo -e "${Info} Snell Server 启动成功!" else echo -e "${Error} Snell Server 启动失败!" echo -e "${Error} 请使用 'systemctl status snell-server' 查看详细错误信息" journalctl -u snell-server -n 20 --no-pager exit 1 fi fi } # 停止 Snell stopSnell(){ checkInstalledStatus checkStatus [[ !"$status" == "running" ]] && echo -e "${Error} Snell Server 没有运行,请检查!" && exit 1 systemctl stop snell-server echo -e "${Info} Snell Server 停止成功!" sleep 3s startMenu } # 重启 Snell restartSnell(){ checkInstalledStatus systemctl restart snell-server echo -e "${Info} Snell Server 重启完毕!" sleep 3s startMenu } # 更新 Snell(占位,待实现) updateSnell(){ checkInstalledStatus echo -e "${Info} Snell Server 更新完毕!" sleep 3s startMenu } # Snell v2 更新到 Snell v3 updateV2toV3(){ checkInstalledStatus readConfig # 检查当前版本是否为 Snell v2 if [[ "$ver" != "2" ]]; then echo -e "${Error} 当前版本不是 Snell v2,无法使用此功能!当前版本:Snell v${ver}" sleep 3s startMenu return 1 fi echo -e "${Info} 即将将 Snell Server 从 Snell v2 更新到 Snell v3 版本" echo -e "确定要更新吗?(y/N)" read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} != [Yy] ]]; then echo -e "${Info} 已取消更新" sleep 2s startMenu return 0 fi echo -e "${Info} 开始更新 Snell v2 到 Snell v3..." # 停止服务 echo -e "${Info} 停止 Snell Server 服务..." systemctl stop snell-server # 备份当前二进制文件 if [[ -e "${snell_bin}" ]]; then echo -e "${Info} 备份当前程序文件..." cp "${snell_bin}" "${snell_bin}.v2.backup.$(date +%Y%m%d_%H%M%S)" fi echo -e "${Info} 开始下载 Snell v3 版本..." downloadSnellV3 if [[ $? -eq 0 ]]; then # 更新配置文件中的版本号 echo -e "${Info} 更新配置文件版本号..." sed -i "s/version = 2/version = 3/g" "${snell_conf}" # 重新加载 systemd 并启动服务 echo -e "${Info} 重启 Snell Server 服务..." systemctl daemon-reload systemctl start snell-server # 检查服务状态 sleep 2 checkStatus if [[ "$status" == "running" ]]; then echo -e "${Info} Snell v2 到 Snell v3 更新成功!" echo -e "${Info} 当前版本:Snell v3" else echo -e "${Error} 服务启动失败,正在回滚..." # 回滚到 Snell v2 backup_file=$(ls -t "${snell_bin}".v2.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" sed -i "s/version = 3/version = 2/g" "${snell_conf}" systemctl start snell-server echo -e "${Info} 已回滚到 Snell v2 版本" fi fi else echo -e "${Error} Snell v3 下载失败,保持 Snell v2 版本" systemctl start snell-server fi sleep 3s startMenu } # Snell v3 更新到 Snell v4 updateV3toV4(){ checkInstalledStatus readConfig # 检查当前版本是否为 Snell v3 if [[ "$ver" != "3" ]]; then echo -e "${Error} 当前版本不是 Snell v3,无法使用此功能!当前版本:Snell v${ver}" sleep 3s startMenu return 1 fi echo -e "${Info} 即将将 Snell Server 从 Snell v3 更新到 Snell v4 版本" echo -e "确定要更新吗?(y/N)" read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} != [Yy] ]]; then echo -e "${Info} 已取消更新" sleep 2s startMenu return 0 fi echo -e "${Info} 开始更新 Snell v3 到 Snell v4..." # 停止服务 echo -e "${Info} 停止 Snell Server 服务..." systemctl stop snell-server # 备份当前二进制文件 if [[ -e "${snell_bin}" ]]; then echo -e "${Info} 备份当前程序文件..." cp "${snell_bin}" "${snell_bin}.v3.backup.$(date +%Y%m%d_%H%M%S)" fi # 获取最新的 Snell v4 版本号 web_v4_version=$(getLatestVersionFromWeb "v4") script_v4_version="${snell_v4_version}" target_v4_version="" if [[ -n "$web_v4_version" ]] && validateVersionUrl "$web_v4_version"; then target_v4_version="$web_v4_version" echo -e "${Info} 使用网页获取的 Snell v4 版本: v${target_v4_version}" elif [[ -n "$script_v4_version" ]] && validateVersionUrl "$script_v4_version"; then target_v4_version="$script_v4_version" echo -e "${Info} 使用脚本内置的 Snell v4 版本: v${target_v4_version}" fi if [[ -z "$target_v4_version" ]]; then echo -e "${Error} 无法找到有效的 Snell v4 版本进行更新" systemctl start snell-server sleep 3s startMenu return 1 fi echo -e "${Info} 开始下载 Snell v4 版本..." downloadSnell "${target_v4_version}" "Snell v4 版本" true "3.0.1" if [[ $? -eq 0 ]]; then actual_version=$(cat ${snell_version_file} | sed 's/^v//') if [[ "$actual_version" =~ ^4\. ]]; then echo -e "${Info} 更新配置文件版本号..." sed -i "s/version = 3/version = 4/g" "${snell_conf}" else echo -e "${Tip} 注意:由于 Snell v4 下载链接问题,已回退重新安装 Snell v${actual_version} 版本" fi # 重新加载 systemd 并启动服务 echo -e "${Info} 重启 Snell Server 服务..." systemctl daemon-reload systemctl start snell-server # 检查服务状态 sleep 2 checkStatus if [[ "$status" == "running" ]]; then if [[ "$actual_version" =~ ^4\. ]]; then echo -e "${Info} Snell v3 到 Snell v4 更新成功!" echo -e "${Info} 当前版本:Snell v${actual_version}" fi else echo -e "${Error} 服务启动失败,正在回滚..." # 回滚到 Snell v3 backup_file=$(ls -t "${snell_bin}".v3.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v3.0.1" > ${snell_version_file} sed -i "s/version = 4/version = 3/g" "${snell_conf}" systemctl start snell-server echo -e "${Info} 已回滚到 Snell v3 版本" fi fi else echo -e "${Error} Snell v4 下载失败,保持 Snell v3 版本" systemctl start snell-server fi sleep 3s startMenu } # Snell v4 更新到 Snell v5 updateV4toV5(){ checkInstalledStatus readConfig # 检查当前版本是否为 Snell v4 if [[ "$ver" != "4" ]]; then echo -e "${Error} 当前版本不是 Snell v4,无法使用此功能!当前版本:Snell v${ver}" sleep 3s startMenu return 1 fi echo -e "${Info} 即将将 Snell Server 从 Snell v4 更新到 Snell v5 版本" echo -e "确定要更新吗?(y/N)" read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} != [Yy] ]]; then echo -e "${Info} 已取消更新" sleep 2s startMenu return 0 fi echo -e "${Info} 开始更新 Snell v4 到 Snell v5..." # 停止服务 echo -e "${Info} 停止 Snell Server 服务..." systemctl stop snell-server # 备份当前二进制文件 if [[ -e "${snell_bin}" ]]; then echo -e "${Info} 备份当前程序文件..." cp "${snell_bin}" "${snell_bin}.v4.backup.$(date +%Y%m%d_%H%M%S)" fi # 获取当前安装的 Snell v4 版本作为回退版本 current_v4_version=$(cat ${snell_version_file} | sed 's/^v//') # 获取最新的 Snell v5 版本号(优先使用网页版本,然后是脚本内置版本) web_v5_version=$(getLatestVersionFromWeb "v5") script_v5_version="${snell_v5_version}" # 选择最新的 Snell v5 版本 target_v5_version="" if [[ -n "$web_v5_version" ]]; then if validateVersionUrl "$web_v5_version"; then target_v5_version="$web_v5_version" echo -e "${Info} 使用网页获取的 Snell v5 版本: v${target_v5_version}" fi fi # 如果网页版本无效,尝试脚本内置版本 if [[ -z "$target_v5_version" && -n "$script_v5_version" ]]; then if validateVersionUrl "$script_v5_version"; then target_v5_version="$script_v5_version" echo -e "${Info} 使用脚本内置的 Snell v5 版本: v${target_v5_version}" fi fi # 如果都无效,取消更新 if [[ -z "$target_v5_version" ]]; then echo -e "${Error} 无法找到有效的 Snell v5 版本进行更新" systemctl start snell-server sleep 3s startMenu return 1 fi # 下载并安装 Snell v5,启用回退机制 echo -e "${Info} 开始下载 Snell v5 版本..." downloadSnell "${target_v5_version}" "Snell v5 版本" true "${current_v4_version}" if [[ $? -eq 0 ]]; then actual_version=$(cat ${snell_version_file} | sed 's/^v//') if [[ "$actual_version" =~ ^5\. ]]; then # 更新配置文件中的版本号 echo -e "${Info} 更新配置文件版本号..." sed -i "s/version = 4/version = 5/g" "${snell_conf}" else echo -e "${Tip} 注意:由于 Snell v5 下载链接问题,已回退重新安装 Snell v${actual_version} 版本" fi # 重新加载 systemd 并启动服务 echo -e "${Info} 重启 Snell Server 服务..." systemctl daemon-reload systemctl start snell-server # 检查服务状态 sleep 2 checkStatus if [[ "$status" == "running" ]]; then if [[ "$actual_version" =~ ^5\. ]]; then echo -e "${Info} Snell v4 到 Snell v5 更新成功!" echo -e "${Info} 当前版本:Snell v${actual_version}" fi else echo -e "${Error} 服务启动失败,正在回滚..." # 回滚到 Snell v4 backup_file=$(ls -t "${snell_bin}".v4.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${current_v4_version}" > ${snell_version_file} sed -i "s/version = 5/version = 4/g" "${snell_conf}" systemctl start snell-server echo -e "${Info} 已回滚到 Snell v4 版本" fi fi else echo -e "${Error} Snell v5 下载失败,保持 Snell v4 版本" systemctl start snell-server fi sleep 3s startMenu } # Snell v5 更新到 Snell v6 updateV5toV6(){ checkInstalledStatus readConfig if [[ "$ver" != "5" ]]; then echo -e "${Error} 当前版本不是 Snell v5,无法使用此功能!当前版本:Snell v${ver}" sleep 3s startMenu return 1 fi echo -e "${Info} 即将将 Snell Server 从 Snell v5 更新到 Snell v6 版本" echo -e "确定要更新吗?(y/N)" read -e -p "(默认: n):" confirm [[ -z "${confirm}" ]] && confirm="n" if [[ ${confirm} != [Yy] ]]; then echo -e "${Info} 已取消更新" sleep 2s startMenu return 0 fi echo -e "${Info} 开始更新 Snell v5 到 Snell v6..." systemctl stop snell-server if [[ -e "${snell_bin}" ]]; then echo -e "${Info} 备份当前程序文件..." cp "${snell_bin}" "${snell_bin}.v5.backup.$(date +%Y%m%d_%H%M%S)" fi current_v5_version=$(cat ${snell_version_file} | sed 's/^v//') web_v6_version=$(getLatestVersionFromWeb "v6") script_v6_version="${snell_v6_version}" target_v6_version="" if [[ -n "$web_v6_version" ]]; then if validateVersionUrl "$web_v6_version"; then target_v6_version="$web_v6_version" echo -e "${Info} 使用网页获取的 Snell v6 版本: v${target_v6_version}" fi fi if [[ -z "$target_v6_version" && -n "$script_v6_version" ]]; then if validateVersionUrl "$script_v6_version"; then target_v6_version="$script_v6_version" echo -e "${Info} 使用脚本内置的 Snell v6 版本: v${target_v6_version}" fi fi if [[ -z "$target_v6_version" ]]; then echo -e "${Error} 无法找到有效的 Snell v6 版本进行更新" systemctl start snell-server sleep 3s startMenu return 1 fi echo -e "${Info} 开始下载 Snell v6 版本..." downloadSnell "${target_v6_version}" "Snell v6 版本" true "${current_v5_version}" if [[ $? -eq 0 ]]; then actual_version=$(cat ${snell_version_file} | sed 's/^v//') if [[ "$actual_version" =~ ^6\. ]]; then echo -e "${Info} 更新配置文件版本号..." ver=6 setDNSIPPref setMode checkPskForV6 writeConfig else echo -e "${Tip} 注意:由于 Snell v6 下载链接问题,已回退重新安装 Snell v${actual_version} 版本" fi systemctl daemon-reload systemctl start snell-server for ((i=0; i<10; i++)); do sleep 1 checkStatus if [[ "$status" == "running" ]]; then break fi done if [[ "$status" == "running" ]]; then if [[ "$actual_version" =~ ^6\. ]]; then echo -e "${Info} Snell v5 到 Snell v6 更新成功!" echo -e "${Info} 当前版本:Snell v${actual_version}" fi else echo -e "${Error} 升级后服务启动失败,以下为错误日志:" journalctl -u snell-server -n 20 --no-pager echo -e "${Error} 正在回滚..." backup_file=$(ls -t "${snell_bin}".v5.backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${current_v5_version}" > ${snell_version_file} ver=5 writeConfig systemctl start snell-server echo -e "${Info} 已回滚到 Snell v5 版本" fi fi else echo -e "${Error} Snell v6 下载失败,保持 Snell v5 版本" systemctl start snell-server fi sleep 3s startMenu } # 更新 Snell Server 到最新版本 updateSnellServer(){ checkInstalledStatus readConfig echo -e "${Info} 准备更新 Snell Server..." # 显示详细的版本检查信息 echo -e "${Info} 正在检查版本信息..." updateBuiltinVersions true checkVersionUpdate true # 检查是否有更新可用 force_checked=false if [[ "$update_available" != true ]]; then echo -e "${Info} 当前已是最新版本,无需更新!" echo -e "${Info} 当前版本: ${Green_font_prefix}v${current_installed_version}${Font_color_suffix}" echo echo -e "${Tip} 是否要强制重新检查最新版本?(y/N)" read -e -p "(默认: n):" force_check [[ -z "${force_check}" ]] && force_check="n" if [[ ${force_check} == [Yy] ]]; then echo -e "${Info} 强制重新检查最新版本..." # 清除缓存并重新检查 rm -f /tmp/snell_version_cache updateBuiltinVersions true checkVersionUpdate true force_checked=true # 重新检查后如果有更新,继续更新流程 if [[ "$update_available" == true ]]; then echo -e "${Info} 检测到新版本,继续更新流程..." else echo -e "${Info} 重新检查后仍为最新版本" sleep 3s startMenu return 0 fi else sleep 3s startMenu return 0 fi fi # 显示版本信息 echo -e "${Info} 当前版本: ${Yellow_font_prefix}v${current_installed_version}${Font_color_suffix}" echo -e "${Info} 最新版本: ${Green_font_prefix}v${latest_available_version}${Font_color_suffix}" echo -e "确定要更新吗?(Y/n)" read -e -p "(默认: y):" confirm [[ -z "${confirm}" ]] && confirm="y" if [[ ${confirm} == [Nn] ]]; then echo -e "${Info} 已取消更新" sleep 2s startMenu return 0 fi echo -e "${Info} 开始更新 Snell Server 到最新版本..." # 停止服务 echo -e "${Info} 停止 Snell Server 服务..." systemctl stop snell-server # 备份当前二进制文件 if [[ -e "${snell_bin}" ]]; then echo -e "${Info} 备份当前程序文件..." cp "${snell_bin}" "${snell_bin}.backup.$(date +%Y%m%d_%H%M%S)" fi # 根据版本选择下载函数,启用回退机制 echo -e "${Info} 开始下载最新版本..." case "$ver" in "6") downloadSnell "${latest_available_version}" "Snell v6 最新版" true "${current_installed_version}" ;; *) echo -e "${Error} 不支持的版本: Snell v${ver}" systemctl start snell-server sleep 3s startMenu return 1 ;; esac if [[ $? -eq 0 ]]; then # 重新加载 systemd 并启动服务 echo -e "${Info} 重启 Snell Server 服务..." systemctl daemon-reload systemctl start snell-server # 检查服务状态 sleep 2 checkStatus if [[ "$status" == "running" ]]; then actual_version=$(cat ${snell_version_file} | sed 's/^v//') echo -e "${Info} Snell Server 更新成功!" echo -e "${Info} 当前版本:v${actual_version}" # 如果实际更新版本与预期不同,给出提示 if [[ "$actual_version" != "$latest_available_version" ]]; then echo -e "${Tip} 注意:由于下载链接问题,已回退到 v${actual_version} 版本" fi else echo -e "${Error} 服务启动失败,正在回滚..." # 回滚到备份版本 backup_file=$(ls -t "${snell_bin}".backup.* 2>/dev/null | head -1) if [[ -n "$backup_file" && -e "$backup_file" ]]; then cp "$backup_file" "${snell_bin}" echo "v${current_installed_version}" > ${snell_version_file} systemctl start snell-server echo -e "${Info} 已回滚到备份版本 v${current_installed_version}" fi fi else echo -e "${Error} 下载失败,启动原版本" systemctl start snell-server fi sleep 3s startMenu } # 自动获取 Snell 最新版本号 getLatestVersionFromWeb(){ local version_type=$1 local release_page="https://kb.nssurge.com/surge-knowledge-base/zh/release-notes/snell" page_content=$(curl -s -L --max-time 10 "$release_page" 2>/dev/null) if [[ -z "$page_content" ]]; then return 1 fi if [[ "$version_type" == "v4" ]]; then latest_v4=$(echo "$page_content" | grep -oE "snell-server-v4\.[0-9]+\.[0-9]+-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g') if [[ -n "$latest_v4" ]]; then echo "$latest_v4" return 0 fi elif [[ "$version_type" == "v5" ]]; then latest_v5=$(echo "$page_content" | grep -oE "snell-server-v5\.[0-9]+\.[0-9]+[a-z]*[0-9]*-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g') if [[ -n "$latest_v5" ]]; then echo "$latest_v5" return 0 fi elif [[ "$version_type" == "v6" ]]; then latest_v6=$(echo "$page_content" | grep -oE "snell-server-v6\.[0-9]+\.[0-9]+[a-z]*[0-9]*-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g') if [[ -n "$latest_v6" ]]; then echo "$latest_v6" return 0 fi fi return 1 } # 更新脚本内置版本号(带缓存机制,但保持脚本版本优先级) updateBuiltinVersions(){ local show_info=${1:-false} # 是否显示详细信息,默认为静默 local cache_file="/tmp/snell_version_cache" local cache_time=3600 local current_time=$(date +%s) # Snell v2 和 Snell v3 始终使用固定版本,无需检查 web_v2_newer=false web_v3_newer=false # 检查缓存是否存在且有效 if [[ -f "$cache_file" ]]; then local cache_timestamp=$(head -1 "$cache_file" 2>/dev/null) if [[ -n "$cache_timestamp" && $((current_time - cache_timestamp)) -lt $cache_time ]]; then local cached_v4=$(sed -n '2p' "$cache_file" 2>/dev/null) local cached_v5=$(sed -n '3p' "$cache_file" 2>/dev/null) local cached_v6=$(sed -n '4p' "$cache_file" 2>/dev/null) if [[ -n "$cached_v4" && -n "$cached_v5" && -n "$cached_v6" ]]; then # 检查网页版本是否比脚本内置版本更新 compareVersions "${snell_v4_version}" "$cached_v4" if [[ $? -eq 2 ]]; then web_v4_newer=true else web_v4_newer=false fi compareVersions "${snell_v5_version}" "$cached_v5" if [[ $? -eq 2 ]]; then web_v5_newer=true else web_v5_newer=false fi compareVersions "${snell_v6_version}" "$cached_v6" if [[ $? -eq 2 ]]; then web_v6_newer=true else web_v6_newer=false fi return 0 fi fi fi [[ "$show_info" == true ]] && echo -e "${Info} 正在检查官方最新版本..." # 获取最新的 Snell v4 版本 local latest_v4_web latest_v4_web=$(getLatestVersionFromWeb "v4") if [[ $? -eq 0 && -n "$latest_v4_web" ]]; then # 比较网页版本和脚本内置版本 compareVersions "${snell_v4_version}" "$latest_v4_web" if [[ $? -eq 2 ]]; then web_v4_newer=true else web_v4_newer=false fi else web_v4_newer=false latest_v4_web="${snell_v4_version}" fi # 获取最新的 Snell v5 版本 local latest_v5_web latest_v5_web=$(getLatestVersionFromWeb "v5") if [[ $? -eq 0 && -n "$latest_v5_web" ]]; then # 比较网页版本和脚本内置版本 compareVersions "${snell_v5_version}" "$latest_v5_web" if [[ $? -eq 2 ]]; then web_v5_newer=true else web_v5_newer=false fi else web_v5_newer=false latest_v5_web="${snell_v5_version}" fi # 获取最新的 Snell v6 版本 local latest_v6_web latest_v6_web=$(getLatestVersionFromWeb "v6") if [[ $? -eq 0 && -n "$latest_v6_web" ]]; then # 比较网页版本和脚本内置版本 compareVersions "${snell_v6_version}" "$latest_v6_web" if [[ $? -eq 2 ]]; then web_v6_newer=true else web_v6_newer=false fi else web_v6_newer=false latest_v6_web="${snell_v6_version}" fi # 更新缓存(缓存 Snell v4, Snell v5, Snell v6) echo "$current_time" > "$cache_file" echo "$latest_v4_web" >> "$cache_file" echo "$latest_v5_web" >> "$cache_file" echo "$latest_v6_web" >> "$cache_file" } # 强制检查最新版本(清除缓存) forceCheckVersions(){ echo -e "${Info} 强制检查 Snell 最新版本..." rm -f "/tmp/snell_version_cache" updateBuiltinVersions true echo -e "${Info} 版本检查完成!" echo -e "${Info} 脚本内置 Snell v4 版本: ${Green_font_prefix}${snell_v4_version}${Font_color_suffix}" echo -e "${Info} 脚本内置 Snell v5 版本: ${Green_font_prefix}${snell_v5_version}${Font_color_suffix}" echo -e "${Info} 脚本内置 Snell v6 版本: ${Green_font_prefix}${snell_v6_version}${Font_color_suffix}" # 获取网页版本进行对比 web_v4=$(getLatestVersionFromWeb "v4") web_v5=$(getLatestVersionFromWeb "v5") web_v6=$(getLatestVersionFromWeb "v6") if [[ -n "$web_v4" ]]; then echo -e "${Info} 网页获取 Snell v4 版本: ${Yellow_font_prefix}${web_v4}${Font_color_suffix}" compareVersions "${snell_v4_version}" "$web_v4" case $? in 1) echo -e "${Info} Snell v4 版本状态: 脚本内置版本与网页版本相同" ;; 0) echo -e "${Info} Snell v4 版本状态: 脚本内置版本比网页版本更新" ;; 2) echo -e "${Tip} Snell v4 版本状态: 网页版本比脚本内置版本更新" ;; esac fi if [[ -n "$web_v5" ]]; then echo -e "${Info} 网页获取 Snell v5 版本: ${Yellow_font_prefix}${web_v5}${Font_color_suffix}" compareVersions "${snell_v5_version}" "$web_v5" case $? in 1) echo -e "${Info} Snell v5 版本状态: 脚本内置版本与网页版本相同" ;; 0) echo -e "${Info} Snell v5 版本状态: 脚本内置版本比网页版本更新" ;; 2) echo -e "${Tip} Snell v5 版本状态: 网页版本比脚本内置版本更新" ;; esac fi if [[ -n "$web_v6" ]]; then echo -e "${Info} 网页获取 Snell v6 版本: ${Yellow_font_prefix}${web_v6}${Font_color_suffix}" compareVersions "${snell_v6_version}" "$web_v6" case $? in 1) echo -e "${Info} Snell v6 版本状态: 脚本内置版本与网页版本相同" ;; 0) echo -e "${Info} Snell v6 版本状态: 脚本内置版本比网页版本更新" ;; 2) echo -e "${Tip} Snell v6 版本状态: 网页版本比脚本内置版本更新" ;; esac fi sleep 3s startMenu } # 卸载 Snell uninstallSnell(){ checkInstalledStatus echo "确定要卸载 Snell Server ? (y/N)" echo read -e -p "(默认: n):" unyn [[ -z ${unyn} ]] && unyn="n" if [[ ${unyn} == [Yy] ]]; then echo -e "${Info} 停止并禁用服务..." systemctl stop snell-server systemctl disable snell-server echo -e "${Info} 移除主程序..." rm -rf "${snell_bin}" echo -e "${Info} 移除 systemd 服务文件..." rm -f /etc/systemd/system/snell-server.service systemctl daemon-reload if [[ -f "${sysctl_conf}" ]]; then echo -e "${Tip} 由于网络优化配置可能被其他程序共用,卸载过程未移除网络优化配置,如需彻底移除可手动删除:${sysctl_conf}" fi echo -e "${Info} 移除配置文件及版本记录..." rm -rf /etc/snell echo && echo "Snell Server 卸载完成!" && echo else echo && echo "卸载已取消..." && echo fi sleep 3s startMenu } # 获取 IPv4 地址 getIpv4(){ ipv4=$(wget -qO- -4 -t1 -T2 ipinfo.io/ip) if [[ -z "${ipv4}" ]]; then ipv4=$(wget -qO- -4 -t1 -T2 api.ip.sb/ip) if [[ -z "${ipv4}" ]]; then ipv4=$(wget -qO- -4 -t1 -T2 members.3322.org/dyndns/getip) if [[ -z "${ipv4}" ]]; then ipv4="IPv4_Error" fi fi fi } # 获取 IPv6 地址 getIpv6(){ ip6=$(wget -qO- -6 -t1 -T2 ifconfig.co) if [[ -z "${ip6}" ]]; then ip6="IPv6_Error" fi } # 查看配置信息 viewConfig(){ checkInstalledStatus readConfig getIpv4 getIpv6 clear && echo echo -e "Snell Server 配置信息:" echo -e "——————————————————————————————————————————————————" if [[ "${ipv4}" != "IPv4_Error" ]]; then echo -e " IPv4 地址\t: ${Green_font_prefix}${ipv4}${Font_color_suffix}" fi if [[ "${ip6}" != "IPv6_Error" ]]; then echo -e " IPv6 地址\t: ${Green_font_prefix}${ip6}${Font_color_suffix}" fi echo -e " 端口\t\t: ${Green_font_prefix}${port}${Font_color_suffix}" echo -e " 密钥\t\t: ${Green_font_prefix}${psk}${Font_color_suffix}" if [[ "$ver" != "6" ]]; then echo -e " OBFS\t\t: ${Green_font_prefix}${obfs}${Font_color_suffix}" if [[ "$obfs" != "off" && -n "$host" ]]; then echo -e " 域名\t\t: ${Green_font_prefix}${host}${Font_color_suffix}" fi echo -e " IPv6\t\t: ${Green_font_prefix}${ipv6}${Font_color_suffix}" fi echo -e " TFO\t\t: ${Green_font_prefix}${tfo}${Font_color_suffix}" echo -e " DNS\t\t: ${Green_font_prefix}${dns}${Font_color_suffix}" if [[ -n "${egress_interface}" ]]; then echo -e " 出口网卡\t: ${Green_font_prefix}${egress_interface}${Font_color_suffix}" fi if [[ "$ver" == "6" && -n "$dns_ip_pref" ]]; then echo -e " DNS IP 偏好\t: ${Green_font_prefix}${dns_ip_pref}${Font_color_suffix}" fi if [[ "$ver" == "6" && -n "$mode" ]]; then echo -e " 混淆模式\t: ${Green_font_prefix}${mode}${Font_color_suffix}" fi echo -e " 版本\t\t: ${Green_font_prefix}${ver}${Font_color_suffix}" local node_name_default="$(uname -n)" local node_name="" echo -e -n "请输入节点名称(${Green_font_prefix}默认${Font_color_suffix}: ${node_name_default}):" read -e node_name [[ -z "${node_name}" ]] && node_name="${node_name_default}" echo -e " 节点名称 : ${Green_font_prefix}${node_name}${Font_color_suffix}" echo -e "——————————————————————————————————————————————————" if [[ "$ver" == "6" ]]; then echo -e "${Tip} 如有监听多 IP 多端口需求,请手动编辑配置文件。" echo -e "——————————————————————————————————————————————————" fi echo -e "${Info} Surge 配置:" if [[ "${ipv4}" != "IPv4_Error" ]]; then if [[ "${obfs}" == "off" || "${ver}" == "6" ]]; then if [[ "${ver}" == "6" && -n "${mode}" ]]; then echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, mode=${mode}, reuse=true, ecn=true" else echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, reuse=true, ecn=true" fi else echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, obfs=${obfs}, obfs-host=${host}, reuse=true, ecn=true" fi elif [[ "${ip6}" != "IPv6_Error" ]]; then if [[ "${obfs}" == "off" || "${ver}" == "6" ]]; then if [[ "${ver}" == "6" && -n "${mode}" ]]; then echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, mode=${mode}, reuse=true, ecn=true" else echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, reuse=true, ecn=true" fi else echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, obfs=${obfs}, obfs-host=${host}, reuse=true, ecn=true" fi else echo -e "${Error} 无法获取 IP 地址!" fi echo -e "——————————————————————————————————————————————————" echo -e "${Info} Mihomo / Nikki 配置:" local mihomo_server="" if [[ "${ipv4}" != "IPv4_Error" ]]; then mihomo_server="${ipv4}" elif [[ "${ip6}" != "IPv6_Error" ]]; then mihomo_server="${ip6}" fi if [[ -z "${mihomo_server}" ]]; then echo -e "${Error} 无法获取 IP 地址!" elif [[ "${ver}" == "6" ]]; then echo -e "${Tip} mihomo/Nikki 当前文档仅支持 Snell v1/2/3/4/5,暂不输出 v6,v6 请用上面的 Surge 配置。" else local mihomo_json="{\"name\":\"${node_name}\",\"server\":\"${mihomo_server}\",\"port\":${port},\"psk\":\"${psk}\",\"version\":${ver},\"tfo\":${tfo},\"type\":\"snell\"" if [[ "${ver}" == "3" || "${ver}" == "4" || "${ver}" == "5" ]]; then mihomo_json+=",\"udp\":true" fi if [[ "${ver}" == "4" || "${ver}" == "5" ]]; then mihomo_json+=",\"reuse\":true" fi if [[ "${obfs}" != "off" && -n "${host}" ]]; then mihomo_json+=",\"obfs-opts\":{\"mode\":\"${obfs}\",\"host\":\"${host}\"}" fi mihomo_json+="}" echo -e "${mihomo_json}" fi echo -e "——————————————————————————————————————————————————" beforeStartMenu } # 查看运行状态 viewStatus(){ echo -e "${Info} 获取 Snell Server 活动日志 ……" echo -e "${Tip} 返回主菜单请按 q !" systemctl status snell-server startMenu } # 检查地理位置(用于更新脚本源选择) geo_check() { api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://cf-ns.com/cdn-cgi/trace" ua="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36" set -- $api_list for url in $api_list; do text="$(curl -A "$ua" -m 10 -s $url)" endpoint="$(echo $text | sed -n 's/.*h=\([^ ]*\).*/\1/p')" if echo $text | grep -qw 'CN'; then isCN=true break elif echo $url | grep -q $endpoint; then break fi done } # 更新脚本 updateShell(){ geo_check if [ ! -z "$isCN" ]; then shell_url="${SNELL_SCRIPT_CN_URL:-https://mjjtop.com/snell}" else shell_url="${SNELL_SCRIPT_URL:-https://mjjtop.com/snell}" fi echo -e "当前版本为 [ ${sh_ver} ],开始检测最新版本..." sh_new_ver=$(wget --no-check-certificate -qO- "$shell_url"|grep 'sh_ver="'|awk -F "=" '{print $NF}'|sed 's/\"//g'|head -1) [[ -z ${sh_new_ver} ]] && echo -e "${Error} 检测最新版本失败!" && startMenu if [[ ${sh_new_ver} != ${sh_ver} ]]; then echo -e "发现新版本[ ${sh_new_ver} ],是否更新?[Y/n]" read -p "(默认: y):" yn [[ -z "${yn}" ]] && yn="y" if [[ ${yn} == [Yy] ]]; then wget -O snell.sh --no-check-certificate "$shell_url" && chmod +x snell.sh echo -e "脚本已更新为最新版本[ ${sh_new_ver} ]!" echo -e "3s后执行新脚本" sleep 3s exec bash snell.sh else echo && echo " 已取消..." && echo sleep 3s startMenu fi else echo -e "当前已是最新版本[ ${sh_new_ver} ]!" sleep 3s startMenu fi } # 返回主菜单前提示 beforeStartMenu() { echo && echo -n -e "${Yellow_font_prefix}* 按回车返回主菜单 *${Font_color_suffix}" && read temp startMenu } # 显示版本检查进度 showVersionCheckProgress(){ local check_duration=${1:-3} # 检查预期耗时,默认3秒 echo -e "${Info} 正在检查 Snell 版本信息..." echo -n "检查进度: " # 根据检查时间动态调整进度条 local steps=30 local step_time=$(echo "scale=2; $check_duration / $steps" | bc 2>/dev/null || echo "0.1") for i in $(seq 1 $steps); do echo -n -e "${Green_font_prefix}█${Font_color_suffix}" sleep $step_time done echo -e " ${Green_font_prefix}完成${Font_color_suffix}" echo -e "${Info} 版本检查完成,正在加载主菜单..." sleep 0.3 } # 检查是否需要进行版本检查 shouldCheckVersion(){ local check_interval=3600 # 1小时 = 3600秒 local last_check_file="/tmp/snell_last_check" local current_time=$(date +%s) # 如果没有安装 Snell,不需要检查 if [[ ! -e ${snell_bin} || ! -e ${snell_conf} ]]; then return 1 # 不需要检查 fi # 如果版本不是 Snell v6,旧版本已停更,不需要检查 local current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}') if [[ "$current_ver" != "6" ]]; then return 1 # 不需要检查 fi # 如果检查记录文件不存在,说明是第一次检查 if [[ ! -f "$last_check_file" ]]; then echo "$current_time" > "$last_check_file" return 0 # 需要检查 fi # 读取上次检查时间 local last_check_time last_check_time=$(cat "$last_check_file" 2>/dev/null) # 如果文件内容无效,重新记录并检查 if [[ ! "$last_check_time" =~ ^[0-9]+$ ]]; then echo "$current_time" > "$last_check_file" return 0 # 需要检查 fi # 计算时间差 local time_diff=$((current_time - last_check_time)) # 如果超过1小时,需要检查 if [[ $time_diff -ge $check_interval ]]; then echo "$current_time" > "$last_check_file" return 0 # 需要检查 fi return 1 # 不需要检查 } # 检查版本更新(带进度显示) checkVersionUpdateWithProgress(){ # 先检查是否需要进行版本检查 if shouldCheckVersion; then echo -e "${Info} 正在检查 Snell 版本信息..." # 显示绿色进度条 ( echo -n "检查进度: " for i in {1..30}; do echo -n -e "${Green_font_prefix}█${Font_color_suffix}" sleep 0.1 done echo -e " ${Green_font_prefix}完成${Font_color_suffix}" ) & progress_pid=$! # 在后台进行版本检查 updateBuiltinVersions false >/dev/null 2>&1 checkVersionUpdate false >/dev/null 2>&1 # 等待进度条完成 wait $progress_pid echo -e "${Info} 版本检查完成,正在加载主菜单..." sleep 0.5 clear else # 静默进行版本检查(使用缓存) if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then updateBuiltinVersions false >/dev/null 2>&1 checkVersionUpdate false >/dev/null 2>&1 fi fi } # 主菜单 startMenu(){ clear checkRoot checkSys sysArch action=$1 # 检查版本更新(在显示菜单前) checkVersionUpdateWithProgress # 检查是否安装了 Snell,需要显示更新或升级选项 show_update_option=false major_upgrade_text="" major_upgrade_func="" if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}') if [[ "$current_ver" == "2" ]]; then major_upgrade_text="Snell v2 更新到 Snell v3" major_upgrade_func="updateV2toV3" elif [[ "$current_ver" == "3" ]]; then major_upgrade_text="Snell v3 更新到 Snell v4" major_upgrade_func="updateV3toV4" elif [[ "$current_ver" == "4" ]]; then major_upgrade_text="Snell v4 更新到 Snell v5" major_upgrade_func="updateV4toV5" elif [[ "$current_ver" == "5" ]]; then major_upgrade_text="Snell v5 更新到 Snell v6" major_upgrade_func="updateV5toV6" elif [[ "$current_ver" == "6" ]]; then show_update_option=true fi fi echo && echo -e " ============================== Snell Server 管理脚本 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix} ============================== ${Green_font_prefix} 0.${Font_color_suffix} 更新脚本 —————————————————————————————— ${Green_font_prefix} 1.${Font_color_suffix} 安装 Snell Server ${Green_font_prefix} 2.${Font_color_suffix} 卸载 Snell Server" # 根据不同情况显示更新选项 if [[ "$show_update_option" == true ]]; then if [[ "$update_available" == true ]]; then echo -e " ${Green_font_prefix} 3.${Font_color_suffix} 更新 Snell Server ${Yellow_font_prefix}(可更新)${Font_color_suffix}" else echo -e " ${Green_font_prefix} 3.${Font_color_suffix} 更新 Snell Server" fi echo -e "—————————————————————————————— ${Green_font_prefix} 4.${Font_color_suffix} 启动 Snell Server ${Green_font_prefix} 5.${Font_color_suffix} 停止 Snell Server ${Green_font_prefix} 6.${Font_color_suffix} 重启 Snell Server —————————————————————————————— ${Green_font_prefix} 7.${Font_color_suffix} 设置 配置信息 ${Green_font_prefix} 8.${Font_color_suffix} 查看 配置信息 ${Green_font_prefix} 9.${Font_color_suffix} 查看 运行状态 —————————————————————————————— ${Green_font_prefix}00.${Font_color_suffix} 退出脚本" menu_max=9 elif [[ -n "$major_upgrade_text" ]]; then echo -e " ${Green_font_prefix} 3.${Font_color_suffix} ${major_upgrade_text}" echo -e "—————————————————————————————— ${Green_font_prefix} 4.${Font_color_suffix} 启动 Snell Server ${Green_font_prefix} 5.${Font_color_suffix} 停止 Snell Server ${Green_font_prefix} 6.${Font_color_suffix} 重启 Snell Server —————————————————————————————— ${Green_font_prefix} 7.${Font_color_suffix} 设置 配置信息 ${Green_font_prefix} 8.${Font_color_suffix} 查看 配置信息 ${Green_font_prefix} 9.${Font_color_suffix} 查看 运行状态 —————————————————————————————— ${Green_font_prefix}00.${Font_color_suffix} 退出脚本" menu_max=9 else echo -e "—————————————————————————————— ${Green_font_prefix} 3.${Font_color_suffix} 启动 Snell Server ${Green_font_prefix} 4.${Font_color_suffix} 停止 Snell Server ${Green_font_prefix} 5.${Font_color_suffix} 重启 Snell Server —————————————————————————————— ${Green_font_prefix} 6.${Font_color_suffix} 设置 配置信息 ${Green_font_prefix} 7.${Font_color_suffix} 查看 配置信息 ${Green_font_prefix} 8.${Font_color_suffix} 查看 运行状态 —————————————————————————————— ${Green_font_prefix}00.${Font_color_suffix} 退出脚本" menu_max=8 fi echo "==============================" && echo if [[ -e ${snell_bin} ]]; then checkStatus if [[ "$status" == "running" ]]; then echo -e " 当前状态: ${Green_font_prefix}已安装${Yellow_font_prefix}[v$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')]${Font_color_suffix}且${Green_font_prefix}已启动${Font_color_suffix}" else echo -e " 当前状态: ${Green_font_prefix}已安装${Yellow_font_prefix}[v$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')]${Font_color_suffix}但${Red_font_prefix}未启动${Font_color_suffix}" fi else echo -e " 当前状态: ${Red_font_prefix}未安装${Font_color_suffix}" fi echo if [[ "$show_update_option" == true || -n "$major_upgrade_text" ]]; then read -e -p " 请输入数字[0-9]:" num else read -e -p " 请输入数字[0-8]:" num fi # 根据不同菜单模式处理用户输入 if [[ "$show_update_option" == true ]]; then case "$num" in 0) updateShell ;; 1) installSnell ;; 2) uninstallSnell ;; 3) updateSnellServer ;; 4) startSnell ;; 5) stopSnell ;; 6) restartSnell ;; 7) setConfig ;; 8) viewConfig ;; 9) viewStatus ;; 00) exit 1 ;; *) echo -e "请输入正确数字${Yellow_font_prefix}[0-9]${Font_color_suffix}"; sleep 2s; startMenu ;; esac elif [[ -n "$major_upgrade_func" ]]; then case "$num" in 0) updateShell ;; 1) installSnell ;; 2) uninstallSnell ;; 3) ${major_upgrade_func} ;; 4) startSnell ;; 5) stopSnell ;; 6) restartSnell ;; 7) setConfig ;; 8) viewConfig ;; 9) viewStatus ;; 00) exit 1 ;; *) echo -e "请输入正确数字${Yellow_font_prefix}[0-9]${Font_color_suffix}"; sleep 2s; startMenu ;; esac else case "$num" in 0) updateShell ;; 1) installSnell ;; 2) uninstallSnell ;; 3) startSnell ;; 4) stopSnell ;; 5) restartSnell ;; 6) setConfig ;; 7) viewConfig ;; 8) viewStatus ;; 00) exit 1 ;; *) echo -e "请输入正确数字${Yellow_font_prefix}[0-8]${Font_color_suffix}"; sleep 2s; startMenu ;; esac fi } startMenu