package controller import ( "errors" "fmt" "os" "os/exec" "strings" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/utils/controller/manager" "github.com/1Panel-dev/1Panel/core/utils/ssh" ) type Controller interface { Name() string IsActive(serviceName string) (bool, error) IsEnable(serviceName string) (bool, error) IsExist(serviceName string) (bool, error) Status(serviceName string) (string, error) Operate(operate, serviceName string) error Reload() error } func New() (Controller, error) { managerOptions := []string{"systemctl", "rc-service", "service"} for _, item := range managerOptions { if _, err := exec.LookPath(item); err != nil { continue } switch item { case "systemctl": return manager.NewSystemd(), nil case "rc-service": return manager.NewOpenrc(), nil case "service": return manager.NewSysvinit(), nil } } return nil, errors.New("not support such manager initializatio") } func NewWithClient(client *ssh.SSHClient) (Controller, error) { managerOptions := []string{"systemctl", "rc-service", "service"} for _, item := range managerOptions { stdout, err := client.Runf("which %s", item) if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0) { continue } switch item { case "systemctl": mgr := manager.NewSystemd() mgr.Client = client return mgr, nil case "rc-service": mgr := manager.NewOpenrc() mgr.Client = client return mgr, nil case "service": mgr := manager.NewSysvinit() mgr.Client = client return mgr, nil } } return nil, errors.New("not support such manager initializatio") } func Handle(operate, serviceName string) error { service, err := LoadServiceName(serviceName) if err != nil { return err } client, err := New() if err != nil { return err } return client.Operate(operate, service) } func HandleStart(serviceName string) error { service, err := LoadServiceName(serviceName) if err != nil { return err } return Handle("start", service) } func HandleStop(serviceName string) error { service, err := LoadServiceName(serviceName) if err != nil { return err } return Handle("stop", service) } func HandleRestart(serviceName string) error { service, err := LoadServiceName(serviceName) if err != nil { return err } return Handle("restart", service) } func CheckExist(serviceName string) (bool, error) { service, err := LoadServiceName(serviceName) if err != nil { return false, err } client, err := New() if err != nil { return false, err } b, er := client.IsExist(service) return b, er } func CheckActive(serviceName string) (bool, error) { service, err := LoadServiceName(serviceName) if err != nil { return false, err } client, err := New() if err != nil { return false, err } return client.IsActive(service) } func CheckEnable(serviceName string) (bool, error) { service, err := LoadServiceName(serviceName) if err != nil { return false, err } client, err := New() if err != nil { return false, err } return client.IsEnable(service) } func Reload() error { client, err := New() if err != nil { return err } return client.Reload() } func RestartPanel(core, agent, reload bool) { client, err := New() if err != nil { global.LOG.Errorf("load client for controller failed, err: %v", err) return } if reload { if err := client.Reload(); err != nil { global.LOG.Errorf("restart 1panel service failed, err: %v", err) return } } if agent { if err := client.Operate("restart", "1panel-agent"); err != nil { global.LOG.Errorf("restart 1panel agent service failed, err: %v", err) return } } if core { if err := client.Operate("restart", "1panel-core"); err != nil { global.LOG.Errorf("restart 1panel core service failed, err: %v", err) return } } } func LoadServiceName(keyword string) (string, error) { client, err := New() if err != nil { return "", err } processedName := loadProcessedName(client.Name(), keyword) exist, _ := client.IsExist(processedName) if exist { return processedName, nil } alistName := loadFromPredefined(client, keyword) if len(alistName) != 0 { return alistName, nil } return "", fmt.Errorf("find such service for %s failed", keyword) } func loadProcessedName(mgr, keyword string) string { keyword = strings.ToLower(keyword) if strings.HasSuffix(keyword, ".service.socket") { keyword = strings.TrimSuffix(keyword, ".service.socket") + ".socket" } if mgr != "systemd" { keyword = strings.TrimSuffix(keyword, ".service") return keyword } if !strings.HasSuffix(keyword, ".service") && !strings.HasSuffix(keyword, ".socket") { keyword += ".service" } return keyword } func loadFromPredefined(mgr Controller, keyword string) string { predefinedMap := map[string][]string{ "clam": {"clamav-daemon.service", "clamd@scan.service", "clamd"}, "freshclam": {"clamav-freshclam.service", "freshclam.service"}, "fail2ban": {"fail2ban.service", "fail2ban"}, "supervisor": {"supervisord.service", "supervisor.service", "supervisord", "supervisor"}, "ssh": {"sshd.service", "ssh.service", "sshd", "ssh"}, "1panel-core": {"1panel-core.service"}, "1panel-agent": {"1panel-agent.service"}, "docker": {"docker.service", "dockerd"}, } if val, ok := predefinedMap[keyword]; ok { for _, item := range val { if exist, _ := mgr.IsExist(item); exist { return item } } } return "" } // GetServicePath returns the configuration file path for the specified service. // If serviceName is empty, it returns the default directory path based on the system's service manager. // For non-empty serviceName, it retrieves the exact service file path. // Parameters: // - serviceName: Name of the service. If empty, returns the default directory. // // Returns: // - string: The service configuration file path. // - error: Error if the service manager is unsupported or command execution fails. func GetServicePath(serviceName string) (string, error) { if serviceName == "" { client, err := New() if err != nil { return "", err } switch client.Name() { case "systemd": return "/etc/systemd/system/", nil case "openrc", "sysvinit": return "/etc/init.d/", nil default: return "", fmt.Errorf("unsupported manager: %s", client.Name()) } } service, err := LoadServiceName(serviceName) if err != nil { return "", err } client, err := New() if err != nil { return "", err } switch client.Name() { case "systemd": stdout, err := exec.Command("systemctl", "show", "-p", "FragmentPath", service).Output() if err != nil { return "", err } parts := strings.SplitN(string(stdout), "=", 2) if len(parts) != 2 { return "", fmt.Errorf("unexpected output: %s", string(stdout)) } return strings.TrimSpace(parts[1]), nil case "openrc", "sysvinit": return fmt.Sprintf("/etc/init.d/%s", service), nil default: return "", fmt.Errorf("unsupported manager: %s", client.Name()) } } func SelectInitScript(keyword string) (string, error) { client, err := New() if err != nil { return "", err } switch client.Name() { case "systemd": keyword = strings.TrimSuffix(keyword, ".service") + ".service" case "openrc": keyword = strings.TrimSuffix(keyword, ".service") + ".openrc" case "sysvinit": if _, err := os.Stat("/etc/rc.common"); err == nil { keyword = strings.TrimSuffix(keyword, ".service") + ".prod" } else { keyword = strings.TrimSuffix(keyword, ".service") + ".init" } default: return "", fmt.Errorf("unsupported manager: %s", client.Name()) } return keyword, nil } func GetScriptName(keyword string) (string, error) { client, err := New() if err != nil { return "", err } switch client.Name() { case "systemd": keyword = strings.TrimSuffix(keyword, ".service") + ".service" case "openrc", "sysvinit": lastDotIdx := strings.LastIndex(keyword, ".") if lastDotIdx != -1 { keyword = keyword[:lastDotIdx] } default: return "", fmt.Errorf("unsupported manager: %s", client.Name()) } return keyword, nil }