Files
eddy 8a8832e4c9 feat: 新增 frpc2.sh,用于安装独立的第二个 frpc 实例
- 新增 SERVICE_NAME=frpc2,安装到 /root/data/docker_data/frpc2,
  可与现有 frpc(Docker 或 systemd)同时运行而不冲突
- systemd 服务文件及所有 systemctl/journalctl 调用统一使用 ${SERVICE_NAME}
- 启动前提示编辑 frpc.toml,并提醒代理名、远程端口不能与其它实例重复
- 安装时使用 systemctl restart,确保修改后的配置生效
- 卸载时若仍存在其它 frpc*.service,则保留共享的 frp 用户
- 优先用 setfacl 为 frp 用户授予目录访问权限,失败时回退到 chmod 755
- 修复 set -e 导致菜单意外退出:systemctl/cat 调用加保护,
  取消卸载后返回菜单,查看日志时 Ctrl+C 只结束日志而不退出脚本
2026-10-27 16:33:34 +08:00

690 lines
20 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# FRP Client 第二实例 (frpc2) 自动安装/卸载脚本
# 可与 frpc.sh 安装的 frpc 实例同时运行,目录和服务名互相独立
# 支持自动检测架构(amd64/arm64)并下载对应安装包
#
# 使用方法:
# chmod +x frpc2.sh && ./frpc2.sh # 交互式主菜单
# ./frpc2.sh manage # 进入服务管理界面
#
# 快速安装:
# curl -sS -O https://gitea.tohub.top/Share/script/raw/branch/main/frpc2.sh && chmod +x frpc2.sh && ./frpc2.sh
#
# 注意: 两个实例的 frpc.toml 中代理名(name)、remotePort、webServer 端口不能重复
#
# 功能说明:
# 1. 自动检测系统架构(amd64/arm64)
# 2. 自动下载对应架构的安装包
# 3. 验证安装包完整性
# 4. 安装并配置 frpc2 服务
# 5. 提供完整的服务管理功能
# 彩色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 配置变量
SERVICE_NAME="frpc2"
INSTALL_DIR="/root/data/docker_data/frpc2"
BASE_URL="https://gitea.tohub.top/Share/script/raw/branch/main/frpc"
FRPC_VERSION="0.65.0"
set -e
# 日志函数
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 兼容旧函数名
log_info() {
print_info "$1"
}
log_warn() {
print_warning "$1"
}
log_error() {
print_error "$1"
}
# 检查是否为root用户
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "此脚本需要root权限运行"
print_info "请使用: sudo $0"
exit 1
fi
}
# 检测系统架构
detect_arch() {
local arch=$(uname -m)
case "$arch" in
x86_64)
echo "amd64"
;;
aarch64)
echo "arm64"
;;
*)
print_error "不支持的系统架构: $arch"
print_info "仅支持 x86_64(amd64) 和 aarch64(arm64)"
exit 1
;;
esac
}
# 创建安装目录
create_install_dir() {
if [[ ! -d "$INSTALL_DIR" ]]; then
print_info "创建安装目录: $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
if [[ $? -ne 0 ]]; then
print_error "创建目录失败"
exit 1
fi
fi
}
# 下载文件
download_file() {
local url=$1
local output=$2
print_info "正在下载: $url"
# 如果存在旧的不完整文件,先删除
if [[ -f "$output" ]]; then
rm -f "$output"
fi
if command -v wget &> /dev/null; then
wget -q --show-progress "$url" -O "$output"
elif command -v curl &> /dev/null; then
curl -# -L "$url" -o "$output"
else
print_error "未找到 wget 或 curl,请先安装"
exit 1
fi
# 检查下载是否成功
if [[ $? -eq 0 ]] && [[ -f "$output" ]] && [[ -s "$output" ]]; then
print_success "下载完成: $output"
return 0
else
print_error "下载失败: $url"
rm -f "$output"
return 1
fi
}
# 验证tar.gz文件是否有效
verify_tar_file() {
local file=$1
if [[ ! -f "$file" ]]; then
return 1
fi
if [[ ! -s "$file" ]]; then
print_warning "文件为空: $file"
return 1
fi
# 尝试列出tar文件内容来验证完整性
if ! tar -tzf "$file" >/dev/null 2>&1; then
print_warning "文件不是有效的 tar.gz 包: $file"
return 1
fi
return 0
}
# 下载安装包
download_packages() {
local arch=$(detect_arch)
print_info "检测到系统架构: $arch"
create_install_dir
local frp_file="frp_${FRPC_VERSION}_linux_${arch}.tar.gz"
local download_failed=0
# 下载 FRP
if [[ ! -f "$INSTALL_DIR/$frp_file" ]]; then
if ! download_file "$BASE_URL/$frp_file" "$INSTALL_DIR/$frp_file"; then
download_failed=1
fi
else
print_warning "文件已存在,跳过下载: $frp_file"
fi
# 下载配置文件(如果不存在)
if [[ ! -f "$INSTALL_DIR/frpc.toml" ]]; then
print_info "下载配置文件模板..."
if ! download_file "$BASE_URL/frpc.toml" "$INSTALL_DIR/frpc.toml"; then
print_warning "配置文件下载失败,需要手动创建"
fi
fi
return $download_failed
}
# 创建frp用户
create_frp_user() {
if ! id "frp" &>/dev/null; then
log_info "创建 frp 用户..."
useradd -r -s /bin/false frp
else
log_info "frp 用户已存在"
fi
}
# 解压安装包
extract_archive() {
local arch=$(detect_arch)
local frp_file="$INSTALL_DIR/frp_${FRPC_VERSION}_linux_${arch}.tar.gz"
# 检查文件是否存在
if [[ ! -f "$frp_file" ]]; then
print_warning "安装包不存在,开始下载..."
if ! download_packages; then
print_error "下载失败,无法继续安装"
return 1
fi
fi
# 验证文件完整性
print_info "验证安装包完整性..."
if ! verify_tar_file "$frp_file"; then
print_error "安装包验证失败,请重新下载"
rm -f "$frp_file"
return 1
fi
print_success "安装包验证通过"
print_info "解压 frp_${FRPC_VERSION}_linux_${arch}.tar.gz..."
# 创建临时目录
TEMP_DIR=$(mktemp -d)
# 解压到临时目录
tar -xzf "$frp_file" -C "$TEMP_DIR"
# 复制文件到安装目录
cp "$TEMP_DIR"/frp_${FRPC_VERSION}_linux_${arch}/frpc "$INSTALL_DIR/"
# 清理临时目录
rm -rf "$TEMP_DIR"
# 设置权限 - 确保 frp 用户可以访问和执行
# 优先用 ACL 只给 frp 用户进入权限,避免把 /root 开放给所有用户
if command -v setfacl &>/dev/null && setfacl -m u:frp:x /root /root/data /root/data/docker_data; then
print_info "已通过 ACL 授予 frp 用户目录访问权限"
else
print_warning "setfacl 不可用或失败,改用 chmod 755 开放上级目录(建议安装 acl 包)"
chmod 755 /root
chmod 755 /root/data
chmod 755 /root/data/docker_data
fi
chmod 755 "$INSTALL_DIR"
# 设置文件所有者和权限
chown root:frp "$INSTALL_DIR/frpc"
chmod 755 "$INSTALL_DIR/frpc"
# 删除压缩包
print_info "清理下载的压缩包..."
rm -f "$frp_file"
print_success "已删除压缩包"
print_success "frpc 已安装到 $INSTALL_DIR/"
}
# 配置frpc
configure_frpc() {
print_info "配置 frpc..."
# 检查配置文件是否已存在
if [[ -f "$INSTALL_DIR/frpc.toml" ]]; then
print_success "配置文件已存在: $INSTALL_DIR/frpc.toml"
elif [[ -f "frpc.toml" ]]; then
# 如果当前目录有配置文件,复制它
print_warning "将复制当前目录的 frpc.toml,请确认它不是其它 frpc 实例正在使用的配置"
cp frpc.toml "$INSTALL_DIR/"
print_success "配置文件已复制到 $INSTALL_DIR/frpc.toml"
else
print_error "找不到配置文件 frpc.toml"
print_info "请手动创建配置文件: $INSTALL_DIR/frpc.toml"
return 1
fi
chown root:frp "$INSTALL_DIR/frpc.toml"
chmod 640 "$INSTALL_DIR/frpc.toml"
# 启动前确认配置,避免与其它 frpc 实例的代理名/远程端口冲突
print_warning "请确认 ${SERVICE_NAME} 的代理名、remote_port/remotePort 与其它 frpc 实例不重复"
read -p "是否现在编辑配置文件? [Y/n]: " edit_choice
if [[ ! $edit_choice =~ ^[Nn]$ ]]; then
edit_config
fi
}
# 编辑配置文件
edit_config() {
if command -v nano >/dev/null 2>&1; then
nano "$INSTALL_DIR/frpc.toml"
elif command -v vi >/dev/null 2>&1; then
vi "$INSTALL_DIR/frpc.toml"
else
print_error "未找到文本编辑器,请手动编辑: $INSTALL_DIR/frpc.toml"
fi
}
# 创建systemd服务
create_service() {
print_info "创建 systemd 服务..."
cat > /etc/systemd/system/${SERVICE_NAME}.service << EOF
[Unit]
Description=Frp Client Service (${SERVICE_NAME})
After=network.target
[Service]
Type=simple
User=frp
Restart=on-failure
RestartSec=5s
ExecStart=${INSTALL_DIR}/frpc -c ${INSTALL_DIR}/frpc.toml
ExecReload=/bin/kill -s HUP \$MAINPID
KillMode=mixed
TimeoutStopSec=5s
[Install]
WantedBy=multi-user.target
EOF
# 重新加载systemd
systemctl daemon-reload
print_success "systemd 服务已创建"
}
# 启动服务
start_service() {
print_info "启动 ${SERVICE_NAME} 服务..."
# 启用开机自启
systemctl enable ${SERVICE_NAME}
# 启动服务(已运行时重启以加载新配置)
systemctl restart ${SERVICE_NAME}
# 检查服务状态
if systemctl is-active --quiet ${SERVICE_NAME}; then
print_success "${SERVICE_NAME} 服务启动成功"
else
print_error "${SERVICE_NAME} 服务启动失败"
print_error "请检查日志: journalctl -u ${SERVICE_NAME} -f"
return 1
fi
}
# 显示状态信息
show_status() {
echo ""
print_success "============================================"
print_success "FRP Client (${SERVICE_NAME}) 安装完成!"
print_success "============================================"
echo ""
echo "服务状态:"
systemctl status ${SERVICE_NAME} --no-pager
echo ""
print_info "常用命令:"
echo " 启动服务: systemctl start ${SERVICE_NAME}"
echo " 停止服务: systemctl stop ${SERVICE_NAME}"
echo " 重启服务: systemctl restart ${SERVICE_NAME}"
echo " 查看状态: systemctl status ${SERVICE_NAME}"
echo " 查看日志: journalctl -u ${SERVICE_NAME} -f"
echo ""
print_info "配置文件位置: $INSTALL_DIR/frpc.toml"
print_info "程序位置: $INSTALL_DIR/frpc"
print_success "============================================"
echo ""
}
# 安装frpc主函数
install_frpc() {
print_info "开始安装 FRP Client..."
create_frp_user
extract_archive || return 1
configure_frpc || return 1
create_service
start_service || return 1
show_status
print_success "FRP Client (${SERVICE_NAME}) 安装完成!"
}
# 卸载frpc
uninstall_frpc() {
print_warning "即将卸载 FRP Client (${SERVICE_NAME})"
read -p "是否继续?[y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "已取消卸载"
return 1
fi
# 停止服务
print_info "停止 ${SERVICE_NAME} 服务..."
systemctl stop ${SERVICE_NAME} 2>/dev/null || true
# 禁用开机自启
print_info "禁用开机自启..."
systemctl disable ${SERVICE_NAME} 2>/dev/null || true
# 删除服务文件
print_info "删除服务文件..."
rm -f /etc/systemd/system/${SERVICE_NAME}.service
systemctl daemon-reload
# 删除程序文件
print_info "删除程序文件..."
rm -rf "$INSTALL_DIR"
# 删除frp用户(其它 frpc 实例仍在使用时保留)
if ls /etc/systemd/system/frpc*.service &>/dev/null; then
print_warning "检测到其它 frpc 实例仍在使用 frp 用户,保留该用户"
else
print_info "删除 frp 用户..."
userdel frp 2>/dev/null || true
fi
echo ""
print_success "FRP Client (${SERVICE_NAME}) 已完全卸载"
}
# 检查安装状态
check_status() {
local arch=$(detect_arch)
local frp_file="$INSTALL_DIR/frp_${FRPC_VERSION}_linux_${arch}.tar.gz"
echo ""
print_info "============================================"
print_info "检查 FRP Client 状态"
print_info "============================================"
# 检查程序是否安装
if [[ -f "$INSTALL_DIR/frpc" ]]; then
print_success "FRP Client 已安装"
print_info " 程序位置: $INSTALL_DIR/frpc"
# 获取程序版本
if [[ -x "$INSTALL_DIR/frpc" ]]; then
local version=$("$INSTALL_DIR/frpc" --version 2>/dev/null | head -n 1 || echo "未知")
print_info " 版本信息: $version"
fi
# 检查服务状态
if systemctl is-active --quiet ${SERVICE_NAME}; then
print_success "${SERVICE_NAME} 服务运行中"
else
print_warning "${SERVICE_NAME} 服务未运行"
fi
if systemctl is-enabled --quiet ${SERVICE_NAME}; then
print_success "开机自启已启用"
else
print_warning "开机自启未启用"
fi
# 检查配置文件
if [[ -f "$INSTALL_DIR/frpc.toml" ]]; then
print_success "配置文件存在"
else
print_warning "配置文件不存在"
fi
else
print_warning "FRP Client 未安装"
fi
# 检查frp用户
if id "frp" &>/dev/null; then
print_success "frp 用户存在"
else
print_warning "frp 用户不存在"
fi
# 检查安装包状态
echo ""
print_info "安装包状态:"
if [[ -f "$frp_file" ]]; then
local size=$(du -h "$frp_file" 2>/dev/null | cut -f1)
print_info " FRP 压缩包: ${CYAN}已下载${NC} (${size})"
else
print_warning " FRP 压缩包: 未下载"
fi
if [[ -f "$INSTALL_DIR/frpc.toml" ]]; then
print_info " 配置文件: ${CYAN}存在${NC}"
else
print_warning " 配置文件: 不存在"
fi
print_info "============================================"
echo ""
}
# 交互式服务管理菜单
interactive_menu() {
while true; do
echo ""
echo "==============================================="
echo " FRP Client 服务管理 (${SERVICE_NAME})"
echo "==============================================="
echo "1. 启动服务 (start)"
echo "2. 停止服务 (stop)"
echo "3. 重启服务 (restart)"
echo "4. 查看状态 (status)"
echo "5. 查看日志 (logs)"
echo "6. 查看配置文件 (config)"
echo "7. 编辑配置文件 (edit)"
echo "8. 卸载服务 (uninstall)"
echo "0. 退出 (exit)"
echo "==============================================="
read -p "请选择操作 [0-8]: " choice
case $choice in
1|start)
log_info "启动 ${SERVICE_NAME} 服务..."
systemctl start ${SERVICE_NAME} || true
if systemctl is-active --quiet ${SERVICE_NAME}; then
log_info "服务启动成功"
else
log_error "服务启动失败"
fi
systemctl status ${SERVICE_NAME} --no-pager || true
;;
2|stop)
log_info "停止 ${SERVICE_NAME} 服务..."
if systemctl stop ${SERVICE_NAME}; then
log_info "服务已停止"
else
log_error "服务停止失败"
fi
;;
3|restart)
log_info "重启 ${SERVICE_NAME} 服务..."
systemctl restart ${SERVICE_NAME} || true
if systemctl is-active --quiet ${SERVICE_NAME}; then
log_info "服务重启成功"
else
log_error "服务重启失败"
fi
systemctl status ${SERVICE_NAME} --no-pager || true
;;
4|status)
echo "服务状态:"
systemctl status ${SERVICE_NAME} --no-pager || true
;;
5|logs)
log_info "查看服务日志 (按 Ctrl+C 退出):"
sleep 1
# 临时忽略 Ctrl+C,只结束 journalctl 而不退出脚本
trap 'true' INT
journalctl -u ${SERVICE_NAME} -f || true
trap - INT
;;
6|config)
echo "配置文件内容:"
echo "----------------------------------------"
cat "$INSTALL_DIR/frpc.toml" || true
echo "----------------------------------------"
;;
7|edit)
print_info "编辑配置文件..."
edit_config
read -p "是否重启服务使配置生效? [y/N]: " restart_choice
if [[ $restart_choice =~ ^[Yy]$ ]]; then
if systemctl restart ${SERVICE_NAME}; then
print_info "服务已重启"
else
log_error "服务重启失败,请检查日志: journalctl -u ${SERVICE_NAME} -f"
fi
fi
;;
8|uninstall)
if uninstall_frpc; then
exit 0
fi
;;
0|exit|quit|q)
log_info "退出管理界面"
exit 0
;;
*)
log_error "无效选择,请输入 0-8 或对应命令"
;;
esac
read -p "按回车键继续..."
done
}
# 主菜单
show_main_menu() {
clear
echo -e "${CYAN}"
echo "============================================"
echo " FRP Client 自动安装/卸载脚本 (${SERVICE_NAME})"
echo "============================================"
echo -e "${NC}"
echo -e "${GREEN}1.${NC} 安装 FRP Client"
echo -e "${GREEN}2.${NC} 卸载 FRP Client"
echo -e "${GREEN}3.${NC} 检查状态"
echo -e "${GREEN}4.${NC} 下载安装包"
echo -e "${GREEN}5.${NC} 服务管理"
echo -e "${GREEN}0.${NC} 退出"
echo ""
echo -e "${CYAN}============================================${NC}"
}
# 主函数
main() {
check_root
# 检查是否为管理模式
if [[ "$1" == "manage" || "$1" == "menu" || "$1" == "-m" ]]; then
interactive_menu
return
fi
# 如果没有参数,显示交互式主菜单
if [[ $# -eq 0 ]]; then
while true; do
show_main_menu
read -p "请选择操作 [0-5]: " choice
case $choice in
1)
install_frpc
read -p "按任意键继续..." -n 1
;;
2)
uninstall_frpc || true
read -p "按任意键继续..." -n 1
;;
3)
check_status
read -p "按任意键继续..." -n 1
;;
4)
print_info "开始下载安装包..."
if download_packages; then
print_success "下载完成!"
else
print_error "下载失败"
fi
read -p "按任意键继续..." -n 1
;;
5)
interactive_menu
;;
0)
print_info "退出脚本"
exit 0
;;
*)
print_error "无效的选择,请重新输入"
sleep 2
;;
esac
done
else
# 兼容旧的直接安装模式(如果有参数的话)
print_info "开始安装 FRP Client..."
install_frpc
echo ""
# 询问是否进入交互管理界面
read -p "是否进入服务管理界面? [y/N]: " enter_menu
if [[ $enter_menu =~ ^[Yy]$ ]]; then
interactive_menu
else
echo ""
print_info "可使用以下命令:"
echo " $0 # 进入主菜单"
echo " $0 manage # 进入服务管理界面"
echo " systemctl [start|stop|restart|status] ${SERVICE_NAME}"
fi
fi
}
# 运行主函数
main "$@"