package collector import ( "os/exec" "strings" ) type ServiceInfo struct { Name string `json:"name"` Description string `json:"description"` Status string `json:"status"` } func CollectServices() []ServiceInfo { var services []ServiceInfo // systemctl list-units --type=service --all --no-pager --no-legend output, err := exec.Command("/usr/bin/systemctl", "list-units", "--type=service", "--all", "--no-pager", "--no-legend").CombinedOutput() if err != nil { return services } lines := strings.Split(string(output), "\n") for _, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } // Format: UNIT LOAD ACTIVE SUB DESCRIPTION fields := strings.Fields(line) if len(fields) < 4 { continue } serviceName := fields[0] load := fields[1] active := fields[2] sub := fields[3] // Beschreibung ist der Rest ab Feld 4 description := "" if len(fields) > 4 { description = strings.Join(fields[4:], " ") } // Status bestimmen status := determineServiceStatus(load, active, sub) // .service suffix entfernen für bessere Lesbarkeit displayName := strings.TrimSuffix(serviceName, ".service") services = append(services, ServiceInfo{ Name: displayName, Description: description, Status: status, }) } return services } // Bestimmt den Service-Status basierend auf systemctl Ausgabe func determineServiceStatus(load, active, sub string) string { // Priorität auf active state switch active { case "active": // Wenn active, schaue auf sub-state switch sub { case "running": return "running" case "exited": return "stopped" // one-shot service, beendet default: return "active" } case "inactive": return "stopped" case "failed": return "error" case "activating": return "starting" case "deactivating": return "stopping" default: // Falls load state problematisch ist if load == "not-found" { return "not-found" } else if load == "error" { return "error" } return "unknown" } }