feat: Fleet Forge UEM - device management, agent, remote desktop (VNC), TLS reverse proxy
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user