//go:build linux package service import ( "fmt" "os" "os/exec" "strings" ) // NewServiceManager 创建服务管理器 (Linux) func NewServiceManager() ServiceManager { return &LinuxServiceManager{} } // LinuxServiceManager Linux systemd服务管理器 type LinuxServiceManager struct{} func (m *LinuxServiceManager) Install(execPath string) error { // 检查是否有足够权限操作系统服务 if err := m.checkPermissions(); err != nil { return err } if err := ensureLogDir(); err != nil { return fmt.Errorf("创建日志目录失败: %v", err) } serviceName := getServiceName() serviceContent := m.generateSystemdUnit(execPath) // 写入systemd服务文件 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", strings.ToLower(serviceName)) if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil { return fmt.Errorf("写入服务文件失败: %v", err) } // 处理 SELinux 上下文,确保可执行文件可以被 systemd 执行 if err := m.handleSELinuxContext(execPath); err != nil { // SELinux 处理失败不算致命错误,只记录警告 fmt.Printf("警告: SELinux 上下文设置失败: %v\n", err) } // 重新加载systemd配置 cmd := exec.Command("systemctl", "daemon-reload") if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("重新加载systemd配置失败: %v, 输出: %s", err, string(output)) } // 启用服务自动启动 cmd = exec.Command("systemctl", "enable", strings.ToLower(serviceName)) if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("启用服务自动启动失败: %v, 输出: %s", err, string(output)) } return nil } func (m *LinuxServiceManager) Uninstall() error { serviceName := strings.ToLower(getServiceName()) // 停止服务 m.Stop() // 禁用服务 cmd := exec.Command("systemctl", "disable", serviceName) cmd.CombinedOutput() // 忽略错误 // 删除服务文件 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", serviceName) if err := os.Remove(servicePath); err != nil && !os.IsNotExist(err) { return fmt.Errorf("删除服务文件失败: %v", err) } // 重新加载systemd配置 cmd = exec.Command("systemctl", "daemon-reload") cmd.CombinedOutput() // 忽略错误 return nil } func (m *LinuxServiceManager) Start() error { serviceName := strings.ToLower(getServiceName()) cmd := exec.Command("systemctl", "start", serviceName) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("启动服务失败: %v, 输出: %s", err, string(output)) } return nil } func (m *LinuxServiceManager) Stop() error { serviceName := strings.ToLower(getServiceName()) cmd := exec.Command("systemctl", "stop", serviceName) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("停止服务失败: %v, 输出: %s", err, string(output)) } return nil } func (m *LinuxServiceManager) Restart() error { serviceName := strings.ToLower(getServiceName()) cmd := exec.Command("systemctl", "restart", serviceName) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("重启服务失败: %v, 输出: %s", err, string(output)) } return nil } func (m *LinuxServiceManager) Status() (ServiceStatus, error) { serviceName := strings.ToLower(getServiceName()) // 检查服务是否存在 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", serviceName) if _, err := os.Stat(servicePath); os.IsNotExist(err) { return StatusNotInstalled, nil } cmd := exec.Command("systemctl", "is-active", serviceName) output, err := cmd.CombinedOutput() outputStr := strings.TrimSpace(string(output)) // 处理不同的退出状态 if err != nil { if exitError, ok := err.(*exec.ExitError); ok { // systemctl is-active 的退出码含义: // 0: active, 1: inactive, 3: activating/deactivating switch exitError.ExitCode() { case 1: // 服务未运行 switch outputStr { case "inactive": return StatusStopped, nil case "failed": return StatusStopped, nil default: return StatusStopped, nil } case 3: // 服务正在启动或停止中 switch outputStr { case "activating": return StatusRunning, nil // 启动中视为运行状态 case "deactivating": return StatusStopped, nil // 停止中视为停止状态 default: return StatusStopped, nil // 其他转换状态默认为停止 } default: if strings.Contains(outputStr, "could not be found") { return StatusNotInstalled, nil } return StatusUnknown, fmt.Errorf("获取服务状态失败: %v, 输出: %s", err, outputStr) } } else { return StatusUnknown, fmt.Errorf("执行systemctl命令失败: %v", err) } } // 没有错误的情况下,服务应该是active状态 switch outputStr { case "active": return StatusRunning, nil case "activating": return StatusRunning, nil // 正在启动中,视为运行状态 case "inactive": return StatusStopped, nil case "failed": return StatusStopped, nil case "deactivating": return StatusStopped, nil // 正在停止中,视为停止状态 default: return StatusUnknown, fmt.Errorf("未知的服务状态: %s", outputStr) } } func (m *LinuxServiceManager) generateSystemdUnit(execPath string) string { description := getServiceDescription() // 获取用户配置目录 userConfigDir, err := getUserConfigDir() if err != nil { // 如果获取失败,使用默认路径 userConfigDir = "/root/.hostsync" } return fmt.Sprintf(`[Unit] Description=%s After=network.target network-online.target Wants=network-online.target [Service] Type=simple ExecStart=%s service run -c %s Restart=always RestartSec=10 User=root Group=root StandardOutput=journal StandardError=journal SyslogIdentifier=hostsync Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin [Install] WantedBy=multi-user.target `, description, execPath, userConfigDir) } // checkPermissions 检查是否有足够权限操作系统服务 func (m *LinuxServiceManager) checkPermissions() error { // 检查是否为 root 用户 if os.Getuid() != 0 { return fmt.Errorf("安装系统服务需要 root 权限,请使用 sudo 运行") } // 检查 /etc/systemd/system 目录是否可写 testPath := "/etc/systemd/system" if _, err := os.Stat(testPath); os.IsNotExist(err) { return fmt.Errorf("systemd 系统目录不存在: %s", testPath) } // 尝试创建一个测试文件来验证写权限 testFile := fmt.Sprintf("%s/.hostsync-permission-test", testPath) if file, err := os.Create(testFile); err != nil { return fmt.Errorf("无法写入系统服务目录 %s: %v", testPath, err) } else { file.Close() os.Remove(testFile) // 清理测试文件 } // 检查 systemctl 命令是否可用 if _, err := exec.LookPath("systemctl"); err != nil { return fmt.Errorf("系统未安装 systemd 或 systemctl 命令不可用: %v", err) } return nil } // handleSELinuxContext 处理 SELinux 上下文设置 func (m *LinuxServiceManager) handleSELinuxContext(execPath string) error { // 检查是否启用了 SELinux if !m.isSELinuxEnabled() { return nil // SELinux 未启用,无需处理 } // 检查 chcon 命令是否可用 if _, err := exec.LookPath("chcon"); err != nil { return fmt.Errorf("chcon 命令不可用: %v", err) } // 为可执行文件设置正确的 SELinux 上下文 // bin_t 类型允许程序被 systemd 执行 cmd := exec.Command("chcon", "-t", "bin_t", execPath) if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("设置 SELinux 上下文失败: %v, 输出: %s", err, string(output)) } return nil } // isSELinuxEnabled 检查 SELinux 是否启用 func (m *LinuxServiceManager) isSELinuxEnabled() bool { // 检查 /selinux/enforce 文件是否存在 if _, err := os.Stat("/selinux/enforce"); err == nil { return true } // 检查 /sys/fs/selinux/enforce 文件是否存在 if _, err := os.Stat("/sys/fs/selinux/enforce"); err == nil { return true } // 尝试运行 getenforce 命令 if _, err := exec.LookPath("getenforce"); err == nil { cmd := exec.Command("getenforce") if output, err := cmd.CombinedOutput(); err == nil { status := strings.TrimSpace(string(output)) return status == "Enforcing" || status == "Permissive" } } return false }