#!/usr/bin/env bash set -euo pipefail # FRP 一键安装脚本 # 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/frp/install_frp.sh" | sudo bash # 包含 frps (服务端) 和 frpc (客户端) 的安装 # 目录结构假设: # BASE_URL/linux_amd64/frps # BASE_URL/linux_amd64/frpc # BASE_URL/frps.toml # BASE_URL/frpc.toml # BASE_URL/smy-frps.service (模板) # BASE_URL/smy-frpc.service (模板) BASE_URL="https://pan.shumengya.top/d/scripts/frp/frp" INSTALL_DIR="/shumengya/bin/frp" SERVICE_PREFIX="smy-frp" log() { printf '[frp-安装] %s\n' "$*" >&2; } fail() { log "错误: $*" >&2; exit 1; } # 进度条函数 show_progress() { local pid=$1 local text=$2 local delay=0.1 local spin='-\|/' printf "[frp-安装] %s... " "$text" >&2 while ps -p "$pid" > /dev/null 2>&1; do local temp=${spin#?} printf "\b%c" "$spin" >&2 local spin=$temp${spin%"$temp"} sleep $delay done printf "\b完成\n" >&2 } require_root() { if [ "${EUID:-$(id -u)}" -ne 0 ]; then fail "请使用 root 权限运行 (sudo bash install_frp.sh)" fi } detect_arch() { local machine machine=$(uname -m) case "$machine" in x86_64|amd64) echo "linux_amd64" ;; aarch64|arm64) echo "linux_arm64" ;; *) fail "不支持的架构: $machine" ;; esac } select_mode() { echo "请选择安装模式:" >&2 echo "1) 安装客户端 (frpc)" >&2 echo "2) 安装服务端 (frps)" >&2 echo "3) 同时安装" >&2 read -p "请输入选项 [1-3]: " choice < /dev/tty case "$choice" in 1) echo "client" ;; 2) echo "server" ;; 3) echo "both" ;; *) fail "无效选项" ;; esac } download_and_install() { local mode=$1 local arch=$2 local tmp_dir tmp_dir=$(mktemp -d) trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi' EXIT mkdir -p "$INSTALL_DIR" if [ "$mode" = "client" ] || [ "$mode" = "both" ]; then install_component "frpc" "$arch" "$tmp_dir" fi if [ "$mode" = "server" ] || [ "$mode" = "both" ]; then install_component "frps" "$arch" "$tmp_dir" fi } install_component() { local name=$1 # frpc or frps local arch=$2 local tmp_dir=$3 local bin_url="${BASE_URL}/${arch}/${name}" local conf_url="${BASE_URL}/${name}.toml" local service_name="${SERVICE_PREFIX}${name:3}" # smy-frpc or smy-frps log "正在处理 $name ..." # 停止旧服务 systemctl stop "${service_name}.service" 2>/dev/null || true # 强制清理可能残留的进程 (避免 Text file busy) pids=$(pgrep -x "$name" || true) if [ -n "$pids" ]; then log "清理 $name 残留进程..." kill -9 $pids 2>/dev/null || true fi # 下载二进制 curl -fsSL "$bin_url" -o "$tmp_dir/$name" & show_progress $! "下载 $name 二进制文件" [ -f "$tmp_dir/$name" ] || fail "下载 $name 失败" # 下载配置 curl -fsSL "$conf_url" -o "$tmp_dir/${name}.toml" & show_progress $! "下载 $name 配置文件" [ -f "$tmp_dir/${name}.toml" ] || fail "下载配置文件失败" # 安装文件 cp "$tmp_dir/$name" "$INSTALL_DIR/$name" chmod +x "$INSTALL_DIR/$name" if [ ! -f "$INSTALL_DIR/${name}.toml" ]; then cp "$tmp_dir/${name}.toml" "$INSTALL_DIR/${name}.toml" else log "保留现有配置文件: $INSTALL_DIR/${name}.toml" fi # 配置服务 write_service "$name" # 启动服务 systemctl daemon-reload systemctl enable "${service_name}.service" systemctl restart "${service_name}.service" sleep 2 if systemctl is-active --quiet "${service_name}.service"; then log "$name 服务启动成功" else log "警告: $name 服务启动失败,查看日志:" journalctl -u "${service_name}.service" --no-pager -n 10 fi } write_service() { local name=$1 local service_name="${SERVICE_PREFIX}${name:3}" local service_file="/etc/systemd/system/${service_name}.service" cat > "$service_file" <