From d10fad98ba5a112bca3b71b69cee6eb2b1eaa5bf Mon Sep 17 00:00:00 2001 From: dushixiang Date: Sat, 6 Dec 2025 15:20:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=A8=20Linux=20=E7=8E=AF=E5=A2=83=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE=20ICMP=20?= =?UTF-8?q?=E6=9D=83=E9=99=90=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/agent/service/service.go | 65 +++++++++++++----------- pkg/agent/sysutil/sysctl_linux.go | 83 +++++++++++++++++++++++++++++++ pkg/agent/sysutil/sysctl_other.go | 10 ++++ 3 files changed, 129 insertions(+), 29 deletions(-) create mode 100644 pkg/agent/sysutil/sysctl_linux.go create mode 100644 pkg/agent/sysutil/sysctl_other.go diff --git a/pkg/agent/service/service.go b/pkg/agent/service/service.go index c2c4d51..8638ca8 100644 --- a/pkg/agent/service/service.go +++ b/pkg/agent/service/service.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/dushixiang/pika/pkg/agent/config" + "github.com/dushixiang/pika/pkg/agent/sysutil" "github.com/dushixiang/pika/pkg/agent/updater" "github.com/kardianos/service" ) @@ -21,33 +22,53 @@ type program struct { cancel context.CancelFunc } -// Start 启动服务 -func (p *program) Start(s service.Service) error { - log.Println("✅ Pika Agent 服务启动中...") - - // 创建 context - p.ctx, p.cancel = context.WithCancel(context.Background()) +// configureICMP 配置 ICMP 权限(抽取通用逻辑) +func configureICMP() { + if err := sysutil.ConfigureICMPPermissions(); err != nil { + log.Printf("⚠️ 配置 ICMP 权限失败: %v", err) + log.Println(" 提示: ICMP 监控可能需要 root 权限运行,或手动执行:") + log.Println(" sudo sysctl -w net.ipv4.ping_group_range=\"0 2147483647\"") + } +} +// startAgent 启动 Agent 和自动更新(抽取通用逻辑) +func startAgent(ctx context.Context, cfg *config.Config) *Agent { // 创建 Agent 实例 - p.agent = New(p.cfg) + agent := New(cfg) // 启动自动更新(如果启用) - if p.cfg.AutoUpdate.Enabled { - upd, err := updater.New(p.cfg, GetVersion()) + if cfg.AutoUpdate.Enabled { + upd, err := updater.New(cfg, GetVersion()) if err != nil { log.Printf("⚠️ 创建更新器失败: %v", err) } else { - go upd.Start(p.ctx) + go upd.Start(ctx) } } // 在后台启动 Agent go func() { - if err := p.agent.Start(p.ctx); err != nil { + if err := agent.Start(ctx); err != nil { log.Printf("⚠️ 探针运行出错: %v", err) } }() + return agent +} + +// Start 启动服务 +func (p *program) Start(s service.Service) error { + log.Println("✅ Pika Agent 服务启动中...") + + // 初始化系统配置(Linux ICMP 权限等) + configureICMP() + + // 创建 context + p.ctx, p.cancel = context.WithCancel(context.Background()) + + // 启动 Agent + p.agent = startAgent(p.ctx, p.cfg) + return nil } @@ -189,6 +210,9 @@ func (m *ServiceManager) Run() error { log.Printf(" 采集间隔: %v", m.cfg.GetCollectorInterval()) log.Printf(" 心跳间隔: %v", m.cfg.GetHeartbeatInterval()) + // 初始化系统配置(Linux ICMP 权限等) + configureICMP() + // 创建 context ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -197,25 +221,8 @@ func (m *ServiceManager) Run() error { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - // 创建 Agent 实例 - agent := New(m.cfg) - - // 启动自动更新(如果启用) - if m.cfg.AutoUpdate.Enabled { - upd, err := updater.New(m.cfg, GetVersion()) - if err != nil { - log.Printf("⚠️ 创建更新器失败: %v", err) - } else { - go upd.Start(ctx) - } - } - // 启动 Agent - go func() { - if err := agent.Start(ctx); err != nil { - log.Printf("⚠️ 探针运行出错: %v", err) - } - }() + agent := startAgent(ctx, m.cfg) // 等待中断信号 <-interrupt diff --git a/pkg/agent/sysutil/sysctl_linux.go b/pkg/agent/sysutil/sysctl_linux.go new file mode 100644 index 0000000..b2250c7 --- /dev/null +++ b/pkg/agent/sysutil/sysctl_linux.go @@ -0,0 +1,83 @@ +package sysutil + +import ( + "fmt" + "log" + "os" + "strconv" + "strings" +) + +// ConfigureICMPPermissions 配置 ICMP 权限 +// 在 Linux 系统上,允许非特权用户发起 ICMP 请求 +// 使用 sync.Once 确保只执行一次 +func ConfigureICMPPermissions() error { + return doConfigureICMPPermissions() +} + +// doConfigureICMPPermissions 实际执行配置 +func doConfigureICMPPermissions() error { + const sysctlPath = "/proc/sys/net/ipv4/ping_group_range" + + // 1. 先检查当前配置 + currentMin, currentMax, err := readPingGroupRange(sysctlPath) + if err != nil { + return fmt.Errorf("读取当前 ICMP 配置失败: %w", err) + } + + // 2. 检查是否已经满足要求 (范围包含 0 到 2147483647) + if currentMin <= 0 && currentMax >= 2147483647 { + log.Printf("✅ ICMP 权限已配置: net.ipv4.ping_group_range=%d %d", currentMin, currentMax) + return nil + } + + // 3. 需要配置,写入新值 + log.Printf("🔧 当前 ICMP 配置: net.ipv4.ping_group_range=%d %d (不满足要求)", currentMin, currentMax) + log.Println(" 正在配置为: 0 2147483647") + + if err := writePingGroupRange(sysctlPath, 0, 2147483647); err != nil { + return fmt.Errorf("配置 ICMP 权限失败: %w", err) + } + + log.Println("✅ ICMP 权限配置成功: net.ipv4.ping_group_range=0 2147483647") + return nil +} + +// readPingGroupRange 读取 ping_group_range 的当前值 +func readPingGroupRange(path string) (min, max int, err error) { + data, err := os.ReadFile(path) + if err != nil { + return 0, 0, err + } + + // 格式: "min max\n" + fields := strings.Fields(string(data)) + if len(fields) != 2 { + return 0, 0, fmt.Errorf("无效的格式: %s", string(data)) + } + + min, err = strconv.Atoi(fields[0]) + if err != nil { + return 0, 0, fmt.Errorf("解析 min 值失败: %w", err) + } + + max, err = strconv.Atoi(fields[1]) + if err != nil { + return 0, 0, fmt.Errorf("解析 max 值失败: %w", err) + } + + return min, max, nil +} + +// writePingGroupRange 写入新的 ping_group_range 值 +func writePingGroupRange(path string, min, max int) error { + value := fmt.Sprintf("%d\t%d", min, max) + if err := os.WriteFile(path, []byte(value), 0644); err != nil { + // 检查是否是权限问题 + if os.IsPermission(err) { + return fmt.Errorf("需要 root 权限才能配置 ICMP 权限: %w", err) + } + return err + } + return nil +} diff --git a/pkg/agent/sysutil/sysctl_other.go b/pkg/agent/sysutil/sysctl_other.go new file mode 100644 index 0000000..aa3de4e --- /dev/null +++ b/pkg/agent/sysutil/sysctl_other.go @@ -0,0 +1,10 @@ +//go:build !linux +// +build !linux + +package sysutil + +// ConfigureICMPPermissions 在非 Linux 平台上不需要配置 +func ConfigureICMPPermissions() error { + // Windows 和 macOS 不需要特殊配置 + return nil +}