Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 68 additions & 9 deletions internal/system/reboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ func (d *Detector) getLatestKernelFromBoot() string {
// Look for vmlinuz-* files
if strings.HasPrefix(name, "vmlinuz-") {
version := strings.TrimPrefix(name, "vmlinuz-")
// Skip generic/recovery kernels
if strings.Contains(version, "generic") || strings.Contains(version, "recovery") {
// Skip recovery kernels but keep generic kernels
if strings.Contains(version, "recovery") {
continue
}
kernels = append(kernels, version)
Expand Down Expand Up @@ -244,7 +244,9 @@ func (d *Detector) getLatestKernelFromDpkg() string {
return ""
}

var latestVersion string
var kernels []string
metaPackages := make(map[string]bool)

lines := strings.Split(string(output), "\n")
for _, line := range lines {
fields := strings.Fields(line)
Expand All @@ -254,19 +256,76 @@ func (d *Detector) getLatestKernelFromDpkg() string {

// Look for installed kernel image packages
if fields[0] == "ii" && strings.HasPrefix(fields[1], "linux-image-") {
// Extract version from package name
// Format: linux-image-VERSION or linux-image-X.Y.Z-N-generic
pkgName := fields[1]
version := strings.TrimPrefix(pkgName, "linux-image-")

// Skip meta packages
if version == "generic" || version == "lowlatency" {
// Identify meta-packages (generic, virtual, lowlatency, etc.)
if version == "generic" || version == "virtual" || version == "lowlatency" ||
version == "server" || version == "cloud" || version == "kvm" {
metaPackages[pkgName] = true
} else {
// This is an actual kernel package with version
kernels = append(kernels, version)
}
}
}

// If we found actual kernel versions, return the latest
if len(kernels) > 0 {
// Sort kernels by version and return the latest
sort.Slice(kernels, func(i, j int) bool {
return compareKernelVersions(kernels[i], kernels[j]) < 0
})
return kernels[len(kernels)-1]
}

// If we only found meta-packages, resolve dependencies to find actual kernels
for metaPkg := range metaPackages {
if actualVersion := d.resolveMetaPackage(metaPkg); actualVersion != "" {
return actualVersion
}
}

return ""
}

// resolveMetaPackage resolves a meta-package (like linux-image-virtual) to the actual kernel version
func (d *Detector) resolveMetaPackage(metaPkg string) string {
// Use dpkg-query to get the dependencies
cmd := exec.Command("dpkg-query", "-W", "-f=${Depends}", metaPkg)
output, err := cmd.Output()
if err != nil {
d.logger.WithError(err).Debug("Failed to query package dependencies")
return ""
}

depends := string(output)

// Parse dependencies to find linux-image-X.Y.Z-N-generic
// Dependencies format: "package1 (>= version), package2, ..."
parts := strings.Split(depends, ",")
for _, part := range parts {
part = strings.TrimSpace(part)

// Remove version constraints like (>= 6.8.0.31.31)
if idx := strings.Index(part, " ("); idx != -1 {
part = part[:idx]
}

// Check if this is a linux-image package with actual version
if strings.HasPrefix(part, "linux-image-") {
version := strings.TrimPrefix(part, "linux-image-")

// Skip if this is another meta-package
if version == "generic" || version == "virtual" || version == "lowlatency" ||
version == "server" || version == "cloud" || version == "kvm" {
continue
}

latestVersion = version
// This should be an actual kernel version
return version
}
}

return latestVersion
return ""
}