#!/bin/bash # ============================================ # LinkMaster 节点端 systemd 启动脚本 # 用于 systemd 服务,直接运行二进制文件 # ============================================ set -e # 脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # 配置 BINARY_NAME="agent" BACKEND_URL="${BACKEND_URL:-http://localhost:8080}" # 检查二进制文件是否存在且有效 check_binary() { if [ -f "$BINARY_NAME" ] && [ -x "$BINARY_NAME" ] && [ -s "$BINARY_NAME" ]; then # 验证二进制文件类型(Linux 应该是 ELF 文件) if command -v file > /dev/null 2>&1; then local file_type=$(file "$BINARY_NAME" 2>/dev/null || echo "") if [ -n "$file_type" ] && echo "$file_type" | grep -qi "ELF"; then return 0 fi else # 如果没有 file 命令,至少检查文件大小(应该大于 1MB) local file_size=$(stat -c%s "$BINARY_NAME" 2>/dev/null || stat -f%z "$BINARY_NAME" 2>/dev/null || echo "0") if [ "$file_size" -gt 1048576 ]; then return 0 fi fi fi return 1 } # 拉取最新源码并编译 update_and_build() { # 如果二进制文件已存在且有效,跳过编译 if check_binary; then echo "二进制文件已存在,跳过编译步骤" return 0 fi # 检查是否在 Git 仓库中 if [ ! -d ".git" ]; then echo "错误: 不在 Git 仓库中,无法更新代码" >&2 return 1 fi # 配置 Git safe.directory,解决所有权问题 CURRENT_DIR=$(pwd) git config --global --add safe.directory "$CURRENT_DIR" 2>/dev/null || true # 拉取最新代码 if git pull 2>&1 > /dev/null; then echo "代码更新完成" fi # 确保 Go 在 PATH 中(systemd 可能没有设置 PATH) if [ -d "/usr/local/go/bin" ] && ! echo "$PATH" | grep -q "/usr/local/go/bin"; then export PATH="/usr/local/go/bin:$PATH" fi # 检查 Go 环境 if ! command -v go > /dev/null 2>&1; then echo "错误: 未找到 Go 环境,无法编译" >&2 echo "PATH: $PATH" >&2 echo "请确保 Go 已安装: /usr/local/go/bin 或系统 PATH 中包含 go 命令" >&2 exit 1 fi # 检查是否有 vendor 目录 local use_vendor=false if [ -d "./vendor" ] && [ -f "./vendor/modules.txt" ]; then use_vendor=true echo "使用 vendor 目录编译(无需网络连接)" fi # 更新依赖(仅在非 vendor 模式下,并设置超时避免卡住) if [ "$use_vendor" != "true" ]; then echo "更新 Go 依赖..." # 使用 timeout 命令设置超时(30秒),如果系统没有 timeout 命令则直接执行 if command -v timeout > /dev/null 2>&1; then timeout 30 go mod download 2>&1 > /dev/null || { echo "警告: 依赖更新失败或超时,尝试继续编译" >&2 } else # 如果没有 timeout 命令,直接执行(可能卡住,但至少能工作) go mod download 2>&1 > /dev/null || { echo "警告: 依赖更新失败,尝试继续编译" >&2 } fi fi # 编译 ARCH=$(uname -m) case $ARCH in x86_64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; *) ARCH="amd64" ;; esac # 构建编译命令 local build_cmd="GOOS=linux GOARCH=${ARCH} CGO_ENABLED=0 go build -buildvcs=false -ldflags=\"-w -s\"" if [ "$use_vendor" = "true" ]; then build_cmd="$build_cmd -mod=vendor" fi build_cmd="$build_cmd -o \"$BINARY_NAME\" ./cmd/agent" echo "开始编译(架构: ${ARCH})..." if eval "$build_cmd" 2>&1; then if [ -f "$BINARY_NAME" ] && [ -s "$BINARY_NAME" ]; then chmod +x "$BINARY_NAME" echo "编译成功" return 0 else echo "错误: 编译失败,未生成二进制文件" >&2 return 1 fi else echo "错误: 编译失败" >&2 return 1 fi } # 检查并更新/编译 if ! check_binary; then update_and_build fi # 设置环境变量 export BACKEND_URL="$BACKEND_URL" # 直接运行二进制文件(systemd 会管理进程) exec ./"$BINARY_NAME"