- Go Agent (FreeBSD/amd64) fuer OPNsense - Go Backend (Linux/amd64) mit REST API + SQLite - Collector: Hardware, CPU, Memory, Disks, Network, Services, WireGuard, DHCP (KEA/ISC/dnsmasq), Routes, Gateways, Certificates, Plugins, Updates - Persistente Agent-ID - TLS + API-Key Auth - WebSocket-Infrastruktur (WIP): bidirektionaler Command-Kanal, TCP-Tunnel fuer Remote-Zugriff auf Firewall-WebUI - Lastenheft und README
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
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
|
|
}
|