288 lines
8.0 KiB
Go
288 lines
8.0 KiB
Go
package svcmgr
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
type ServiceInfo struct {
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
func ListServices() ([]ServiceInfo, error) {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return listWindowsServices()
|
|
case "darwin":
|
|
return listDarwinServices()
|
|
default:
|
|
return listLinuxServices()
|
|
}
|
|
}
|
|
|
|
func GetServiceStatus(name string) (string, bool, error) {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return getWindowsServiceStatus(name)
|
|
case "darwin":
|
|
return getDarwinServiceStatus(name)
|
|
default:
|
|
return getLinuxServiceStatus(name)
|
|
}
|
|
}
|
|
|
|
func StartService(name string) (string, error) {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return startWindowsService(name)
|
|
case "darwin":
|
|
return startDarwinService(name)
|
|
default:
|
|
return startLinuxService(name)
|
|
}
|
|
}
|
|
|
|
func StopService(name string) (string, error) {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return stopWindowsService(name)
|
|
case "darwin":
|
|
return stopDarwinService(name)
|
|
default:
|
|
return stopLinuxService(name)
|
|
}
|
|
}
|
|
|
|
func RestartService(name string) (string, error) {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return restartWindowsService(name)
|
|
case "darwin":
|
|
return restartDarwinService(name)
|
|
default:
|
|
return restartLinuxService(name)
|
|
}
|
|
}
|
|
|
|
func Reboot() error {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return exec.Command("shutdown", "/r", "/t", "0").Run()
|
|
case "darwin":
|
|
return exec.Command("sudo", "shutdown", "-r", "now").Run()
|
|
default:
|
|
return exec.Command("sudo", "reboot").Run()
|
|
}
|
|
}
|
|
|
|
func Shutdown() error {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return exec.Command("shutdown", "/s", "/t", "0").Run()
|
|
case "darwin":
|
|
return exec.Command("sudo", "shutdown", "-h", "now").Run()
|
|
default:
|
|
return exec.Command("sudo", "poweroff").Run()
|
|
}
|
|
}
|
|
|
|
func listWindowsServices() ([]ServiceInfo, error) {
|
|
out, err := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
|
|
"Get-Service -ErrorAction SilentlyContinue | ForEach-Object { '{0}`t{1}`t{2}' -f $_.Name, $_.Status, $_.StartType }").Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Get-Service failed: %w", err)
|
|
}
|
|
|
|
var services []ServiceInfo
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
|
fields := strings.Split(strings.TrimSpace(line), "\t")
|
|
if len(fields) < 3 {
|
|
continue
|
|
}
|
|
services = append(services, ServiceInfo{
|
|
Name: fields[0],
|
|
Status: strings.ToLower(fields[1]),
|
|
Enabled: strings.EqualFold(fields[2], "Automatic"),
|
|
})
|
|
}
|
|
return services, nil
|
|
}
|
|
|
|
func getWindowsServiceStatus(name string) (string, bool, error) {
|
|
out, err := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
|
|
fmt.Sprintf("$s = Get-Service -Name '%s' -ErrorAction Stop; '{0}`t{1}' -f $s.Status, $s.StartType", name)).Output()
|
|
if err != nil {
|
|
return "", false, fmt.Errorf("Get-Service failed: %w", err)
|
|
}
|
|
fields := strings.Split(strings.TrimSpace(string(out)), "\t")
|
|
if len(fields) < 2 {
|
|
return "", false, fmt.Errorf("unexpected Get-Service output: %s", strings.TrimSpace(string(out)))
|
|
}
|
|
return strings.ToLower(fields[0]), strings.EqualFold(fields[1], "Automatic"), nil
|
|
}
|
|
|
|
func startWindowsService(name string) (string, error) {
|
|
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
|
|
fmt.Sprintf("Start-Service -Name '%s' -ErrorAction Stop", name))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return string(out), fmt.Errorf("start failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "started", nil
|
|
}
|
|
|
|
func stopWindowsService(name string) (string, error) {
|
|
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
|
|
fmt.Sprintf("Stop-Service -Name '%s' -ErrorAction Stop", name))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return string(out), fmt.Errorf("stop failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "stopped", nil
|
|
}
|
|
|
|
func restartWindowsService(name string) (string, error) {
|
|
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
|
|
fmt.Sprintf("Restart-Service -Name '%s' -ErrorAction Stop", name))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return string(out), fmt.Errorf("restart failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "restarted", nil
|
|
}
|
|
|
|
func listDarwinServices() ([]ServiceInfo, error) {
|
|
out, err := exec.Command("launchctl", "list").Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("launchctl list failed: %w", err)
|
|
}
|
|
|
|
var services []ServiceInfo
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n")[1:] {
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 3 {
|
|
continue
|
|
}
|
|
status := "unknown"
|
|
if fields[0] != "-" {
|
|
status = "running"
|
|
}
|
|
services = append(services, ServiceInfo{
|
|
Name: fields[2],
|
|
Status: status,
|
|
})
|
|
}
|
|
return services, nil
|
|
}
|
|
|
|
func getDarwinServiceStatus(name string) (string, bool, error) {
|
|
out, err := exec.Command("launchctl", "list").Output()
|
|
if err != nil {
|
|
return "", false, fmt.Errorf("launchctl list failed: %w", err)
|
|
}
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n")[1:] {
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 3 && fields[2] == name {
|
|
if fields[0] != "-" {
|
|
return "running", true, nil
|
|
}
|
|
return "inactive", false, nil
|
|
}
|
|
}
|
|
return "not_found", false, nil
|
|
}
|
|
|
|
func startDarwinService(name string) (string, error) {
|
|
cmd := exec.Command("launchctl", "load", fmt.Sprintf("/Library/LaunchDaemons/%s.plist", name))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return string(out), fmt.Errorf("start failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "started", nil
|
|
}
|
|
|
|
func stopDarwinService(name string) (string, error) {
|
|
cmd := exec.Command("launchctl", "unload", fmt.Sprintf("/Library/LaunchDaemons/%s.plist", name))
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return string(out), fmt.Errorf("stop failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "stopped", nil
|
|
}
|
|
|
|
func restartDarwinService(name string) (string, error) {
|
|
stopDarwinService(name)
|
|
return startDarwinService(name)
|
|
}
|
|
|
|
func listLinuxServices() ([]ServiceInfo, error) {
|
|
cmd := exec.Command("systemctl", "list-units", "--type=service", "--all", "--no-pager", "--no-legend", "--plain", "--no-accept-unknown")
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("systemctl list-units failed: %w", err)
|
|
}
|
|
|
|
var services []ServiceInfo
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 4 {
|
|
continue
|
|
}
|
|
|
|
svcName := strings.TrimSuffix(fields[0], ".service")
|
|
status := fields[2]
|
|
enabled := fields[3] == "loaded"
|
|
|
|
services = append(services, ServiceInfo{
|
|
Name: svcName,
|
|
Status: status,
|
|
Enabled: enabled,
|
|
})
|
|
}
|
|
return services, nil
|
|
}
|
|
|
|
func getLinuxServiceStatus(name string) (string, bool, error) {
|
|
cmd := exec.Command("systemctl", "is-active", name)
|
|
out, err := cmd.Output()
|
|
status := strings.TrimSpace(string(out))
|
|
|
|
enabledCmd := exec.Command("systemctl", "is-enabled", name)
|
|
enabledOut, _ := enabledCmd.Output()
|
|
enabled := strings.TrimSpace(string(enabledOut)) == "enabled"
|
|
|
|
if err != nil && status != "inactive" {
|
|
return status, enabled, fmt.Errorf("systemctl status failed: %w", err)
|
|
}
|
|
return status, enabled, nil
|
|
}
|
|
|
|
func startLinuxService(name string) (string, error) {
|
|
cmd := exec.Command("sudo", "systemctl", "start", name)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return string(out), fmt.Errorf("start failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "started", nil
|
|
}
|
|
|
|
func stopLinuxService(name string) (string, error) {
|
|
cmd := exec.Command("sudo", "systemctl", "stop", name)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return string(out), fmt.Errorf("stop failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "stopped", nil
|
|
}
|
|
|
|
func restartLinuxService(name string) (string, error) {
|
|
cmd := exec.Command("sudo", "systemctl", "restart", name)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return string(out), fmt.Errorf("restart failed: %w\nOutput: %s", err, string(out))
|
|
}
|
|
return "restarted", nil
|
|
} |