mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-12 16:36:12 +08:00
* feat(systemctl): 实现服务管理器初始化和命令执行 - 新增 systemctl 包,实现对 systemd、openrc 和 sysvinit 三种服务管理器的支持 - 添加服务状态检查、启动、停止、重启和启用/禁用功能 - 实现服务发现和智能服务名处理 - 添加配置文件查看功能 - 优化错误处理和日志记录 * refactor(system): 重构系统服务管理逻辑 - 引入 systemctl 工具包以统一处理系统服务 - 优化服务状态获取、配置文件路径解析等逻辑 - 重构 HostToolService 中的 GetToolStatus 方法 - 更新 DockerService、SettingService 等相关服务的处理方式 - 调整快照创建和恢复过程中的服务处理逻辑 * feat(utils): 添加目录复制功能并优化文件复制逻辑 - 新增 CopyDirs 函数,用于复制整个目录及其内容 - 添加对符号链接的复制支持 - 实现通用的 Copy 函数,根据文件类型自动选择 CopyFile 或 CopyDirs - 在 CopyFile 函数中增加对源文件是目录的检查和错误提示 * refactortoolbox: 重构 Fail2ban 和 Pure-FTPd 的管理逻辑 - 优化了 Fail2ban 和 Pure-FTPd 的启动、停止、重启等操作的实现 - 改进了 Fail2ban 版本信息的获取方法 - 统一了错误处理和日志记录的格式 - 调整了部分导入的包,提高了代码的可维护性 * build: 禁用 CGO 以提高构建性能和兼容性 - 在 Linux 后端构建命令中添加 CGO_ENABLED=0 环境变量 - 此修改可以提高构建速度,并确保生成的二进制文件在没有 C 库依赖的环境中也能运行 * refactor(docker): 重构 Docker 服务的重启和操作逻辑 - 添加 isDockerSnapInstalled 函数来判断 Docker 是否通过 Snap 安装 - 在 OperateDocker 和 restartDocker 函数中增加对 Snap 安装的处理 - 移除未使用的 getDockerRestartCommand 函数 * fix(service): 优化快照恢复后的服务重启逻辑 - 在使用 systemd 管理服务时,增加 daemon-reload 操作以确保服务配置更新 - 重启 1panel 服务,以应用快照恢复的更改 * refactor(server): 支持非 systemd 系统的恢复操作 - 增加 isSystemd 函数判断系统是否为 systemd 类型 - 根据系统类型选择性地恢复服务文件 - 兼容 systemd 和非 systemd 系统的恢复流程 * fix(upgrade): 优化升级过程中的服务重启逻辑 - 移动服务重启逻辑到版本号更新之后,修复因提前重启导致的版本号未更新BUG。 - 在 systemctl 重启之前添加 daemon-reload 命令 --------- Co-authored-by: gcsong023 <gcsong023@users.noreply.github.com>
221 lines
5.9 KiB
Go
221 lines
5.9 KiB
Go
package systemctl
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
|
)
|
|
|
|
func DefaultHandler(serviceName string) (*ServiceHandler, error) {
|
|
svcName, err := smartServiceName(serviceName)
|
|
if err != nil {
|
|
// global.LOG.Errorf("SmartServiceName failed for %s: %v", serviceName, err)
|
|
return nil, ErrServiceNotFound
|
|
}
|
|
return NewServiceHandler(defaultServiceConfig(svcName)), nil
|
|
}
|
|
|
|
func GetServiceName(serviceName string) (string, error) {
|
|
serviceName, err := smartServiceName(serviceName)
|
|
if err != nil {
|
|
// global.LOG.Errorf("GetServiceName validation failed: %v", err)
|
|
return "", ErrServiceNotFound
|
|
}
|
|
return serviceName, nil
|
|
}
|
|
|
|
func GetServicePath(serviceName string) (string, error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
// global.LOG.Errorf("GetServicePath handler init failed: %v", err)
|
|
return "", ErrServiceNotFound
|
|
}
|
|
return handler.GetServicePath()
|
|
}
|
|
|
|
func CustomAction(action string, serviceName string) (ServiceResult, error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
global.LOG.Errorf("CustomAction handler init failed: %v", err)
|
|
return ServiceResult{}, ErrServiceNotFound
|
|
}
|
|
result, err := handler.ExecuteAction(action)
|
|
if err != nil {
|
|
global.LOG.Errorf("CustomAction %s failed: %v", action, err)
|
|
return result, fmt.Errorf("%s operation failed: %w | Output: %s", action, err, result.Output)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func IsExist(serviceName string) (bool, error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
result, _ := handler.IsExists()
|
|
if result.IsExists {
|
|
return true, nil
|
|
} else {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
func Start(serviceName string) error {
|
|
handler, _ := DefaultHandler(serviceName)
|
|
result, err := handler.StartService()
|
|
if err != nil {
|
|
global.LOG.Errorf("Service start failed: %v | Output: %s", err, result.Output)
|
|
return fmt.Errorf("start failed: %v | Output: %s", err, result.Output)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Stop(serviceName string) error {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
global.LOG.Errorf("Stop handler init failed: %v", err)
|
|
return fmt.Errorf("%s is not exist", serviceName)
|
|
}
|
|
result, err := handler.StopService()
|
|
if err != nil {
|
|
global.LOG.Errorf("Service stop failed: %v", err)
|
|
return fmt.Errorf("stop failed: %v | Output: %s", err, result.Output)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Restart(serviceName string) error {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
global.LOG.Errorf("Restart handler init failed: %v", err)
|
|
return fmt.Errorf("%s is not exist", serviceName)
|
|
}
|
|
result, err := handler.RestartService()
|
|
if err != nil {
|
|
global.LOG.Errorf("Service restart failed: %v", err)
|
|
return fmt.Errorf("restart failed: %v | Output: %s", err, result.Output)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func SafeRestart(service string, configPaths []string) error {
|
|
for _, path := range configPaths {
|
|
if !FileExist(path) {
|
|
global.LOG.Errorf("Config file missing: %s", path)
|
|
return fmt.Errorf("config file missing: %s", path)
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
if _, err := executeCommand(ctx, "check", service); err != nil {
|
|
global.LOG.Errorf("Config test failed: %v", err)
|
|
return fmt.Errorf("config test failed: %w", err)
|
|
}
|
|
|
|
if err := Restart(service); err != nil {
|
|
global.LOG.Errorf("SafeRestart failed: %v", err)
|
|
return err
|
|
}
|
|
|
|
isActive, _, err := Status(service)
|
|
if err != nil || !isActive {
|
|
global.LOG.Error("Service not active after safe restart")
|
|
return fmt.Errorf("service not active after restart")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Enable(serviceName string) error {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
global.LOG.Errorf("Enable handler init failed: %v", err)
|
|
return fmt.Errorf("%s is not exist", serviceName)
|
|
}
|
|
result, err := handler.EnableService()
|
|
if err != nil {
|
|
global.LOG.Errorf("Service enable failed: %v | Output: %s", err, result.Output)
|
|
return fmt.Errorf("%s enable failed: %v ", serviceName, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Disable(serviceName string) error {
|
|
handler, _ := DefaultHandler(serviceName)
|
|
result, err := handler.DisableService()
|
|
if err != nil {
|
|
global.LOG.Errorf("Service disable failed: %v", err)
|
|
return fmt.Errorf("disable failed: %v | Output: %s", err, result.Output)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Status(serviceName string) (isActive bool, isEnabled bool, err error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
global.LOG.Errorf("Status handler init failed: %v", err)
|
|
return false, false, fmt.Errorf("%s is not exist", serviceName)
|
|
}
|
|
status, err := handler.CheckStatus()
|
|
if err != nil {
|
|
global.LOG.Errorf("Status check failed: %v", err)
|
|
return false, false, fmt.Errorf("status check failed: %v | Output: %s", err, status.Output)
|
|
}
|
|
return status.IsActive, status.IsEnabled, nil
|
|
}
|
|
|
|
func IsActive(serviceName string) (bool, error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
status, err := handler.IsActive()
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
return status.IsActive, nil
|
|
}
|
|
|
|
func IsEnable(serviceName string) (bool, error) {
|
|
handler, err := DefaultHandler(serviceName)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
status, err := handler.IsEnabled()
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
return status.IsEnabled, nil
|
|
}
|
|
|
|
type LogOption struct {
|
|
TailLines string
|
|
}
|
|
|
|
func ViewLog(path string, opt LogOption) (string, error) {
|
|
if !FileExist(path) {
|
|
return "", fmt.Errorf("log file not found: %s", path)
|
|
}
|
|
args := []string{"-n", opt.TailLines, path}
|
|
if opt.TailLines == "+1" {
|
|
args = []string{"-n", "1", path}
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
output, err := executeCommand(ctx, "tail", args...)
|
|
if err != nil {
|
|
return "", fmt.Errorf("tail failed: %w | Output: %s", err, string(output))
|
|
}
|
|
return string(output), nil
|
|
}
|
|
|
|
func FileExist(path string) bool {
|
|
_, err := os.Stat(path)
|
|
return err == nil
|
|
}
|