193 lines
4.5 KiB
Bash
193 lines
4.5 KiB
Bash
#!/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" <<EOF
|
||
[Unit]
|
||
Description=${service_name}
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
WorkingDirectory=$INSTALL_DIR
|
||
ExecStart=$INSTALL_DIR/$name -c $INSTALL_DIR/${name}.toml
|
||
Restart=on-failure
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
}
|
||
|
||
main() {
|
||
require_root
|
||
local arch
|
||
arch=$(detect_arch)
|
||
log "检测到架构: $arch"
|
||
|
||
local mode
|
||
if [ $# -gt 0 ]; then
|
||
case "$1" in
|
||
client|frpc) mode="client" ;;
|
||
server|frps) mode="server" ;;
|
||
both) mode="both" ;;
|
||
*) fail "无效参数: $1 (可用: client, server, both)" ;;
|
||
esac
|
||
else
|
||
mode=$(select_mode)
|
||
fi
|
||
|
||
download_and_install "$mode" "$arch"
|
||
|
||
log "=========================================="
|
||
log "安装任务完成!"
|
||
log "安装目录: $INSTALL_DIR"
|
||
log "=========================================="
|
||
}
|
||
|
||
main "$@"
|