package collector import ( "os/exec" "strings" ) type PendingUpdate struct { Package string `json:"package"` CurrentVer string `json:"current_version"` NewVer string `json:"new_version"` } type UpdateInfo struct { UpdateAvailable bool `json:"update_available"` PendingCount int `json:"pending_count"` Updates []PendingUpdate `json:"updates"` OPNsenseUpdate string `json:"opnsense_update,omitempty"` // z.B. "26.1.2 -> 26.1.2_5" } // CollectUpdates prueft ob Updates verfuegbar sind func CollectUpdates() UpdateInfo { info := UpdateInfo{} // 1. OPNsense Core Update pruefen // opnsense-update -c: exit 0 = kein Update, exit 1 = Update verfuegbar (mit Versions-Info auf stdout) if out, err := exec.Command("/usr/local/sbin/opnsense-update", "-c").CombinedOutput(); err != nil { // Exit 1 = Update verfuegbar outStr := strings.TrimSpace(string(out)) if outStr != "" { info.OPNsenseUpdate = outStr info.UpdateAvailable = true } } // 2. Paket-Updates pruefen via pkg upgrade -n (dry-run) out, err := exec.Command("/usr/sbin/pkg", "upgrade", "-n").CombinedOutput() if err != nil || len(out) == 0 { // Bei pkg upgrade -n ist exit 1 = Updates verfuegbar // Trotzdem stdout parsen } content := string(out) // Zeilen mit ":" im UPGRADED-Block parsen // Format: "\tpackage: old_version -> new_version" inUpgradeBlock := false for _, line := range strings.Split(content, "\n") { trimmed := strings.TrimSpace(line) if strings.Contains(trimmed, "packages to be UPGRADED") || strings.Contains(trimmed, "will be affected") { inUpgradeBlock = true continue } if inUpgradeBlock && strings.Contains(trimmed, "->") { // Format: "package: old -> new" parts := strings.SplitN(trimmed, ":", 2) if len(parts) == 2 { pkg := strings.TrimSpace(parts[0]) versions := strings.TrimSpace(parts[1]) arrowParts := strings.SplitN(versions, "->", 2) if len(arrowParts) == 2 { update := PendingUpdate{ Package: pkg, CurrentVer: strings.TrimSpace(arrowParts[0]), NewVer: strings.TrimSpace(arrowParts[1]), } info.Updates = append(info.Updates, update) info.UpdateAvailable = true } } } // Block endet bei Leerzeile oder "Number of packages" if inUpgradeBlock && (trimmed == "" || strings.HasPrefix(trimmed, "Number of")) { if strings.HasPrefix(trimmed, "Number of") { continue } inUpgradeBlock = false } } info.PendingCount = len(info.Updates) return info }