Files
Jason Staack b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00

100 lines
3.0 KiB
Go

package device
import (
"log/slog"
routeros "github.com/go-routeros/routeros/v3"
)
// FirmwareInfo holds firmware update status collected from a RouterOS device.
type FirmwareInfo struct {
InstalledVersion string `json:"installed_version"`
LatestVersion string `json:"latest_version,omitempty"`
Channel string `json:"channel,omitempty"`
Status string `json:"status"` // "New version is available", "System is already up to date", "check-failed"
Architecture string `json:"architecture"` // CPU architecture (e.g., "arm", "arm64", "mipsbe")
}
// CheckFirmwareUpdate queries a RouterOS device for firmware update status.
//
// It performs two API calls:
// 1. /system/resource/print — to get the architecture and installed version.
// 2. /system/package/update/check-for-updates + /system/package/update/print
// — to get the latest available version from MikroTik's servers.
//
// If the device cannot reach MikroTik's servers (no internet), the function
// returns what it knows (installed version, architecture) with status "check-failed".
// This is non-fatal — the device may simply not have internet access.
func CheckFirmwareUpdate(c *routeros.Client) (FirmwareInfo, error) {
// 1. Get architecture and installed version from /system/resource/print.
resReply, err := c.Run("/system/resource/print")
if err != nil {
return FirmwareInfo{}, err
}
arch := ""
installedVer := ""
if len(resReply.Re) > 0 {
arch = resReply.Re[0].Map["architecture-name"]
installedVer = resReply.Re[0].Map["version"]
}
// 2. Trigger check-for-updates (makes outbound HTTP from device to MikroTik servers).
_, err = c.Run("/system/package/update/check-for-updates")
if err != nil {
slog.Debug("firmware update check failed (device may lack internet)",
"error", err,
"architecture", arch,
)
// Non-fatal: return what we know.
return FirmwareInfo{
InstalledVersion: installedVer,
Architecture: arch,
Status: "check-failed",
}, nil
}
// 3. Read results from /system/package/update/print.
reply, err := c.Run("/system/package/update/print")
if err != nil {
return FirmwareInfo{
InstalledVersion: installedVer,
Architecture: arch,
Status: "check-failed",
}, nil
}
if len(reply.Re) == 0 {
return FirmwareInfo{
InstalledVersion: installedVer,
Architecture: arch,
Status: "check-failed",
}, nil
}
m := reply.Re[0].Map
info := FirmwareInfo{
InstalledVersion: m["installed-version"],
LatestVersion: m["latest-version"],
Channel: m["channel"],
Status: m["status"],
Architecture: arch,
}
// Use the resource-detected values as fallback.
if info.InstalledVersion == "" {
info.InstalledVersion = installedVer
}
slog.Debug("firmware update check complete",
"installed", info.InstalledVersion,
"latest", info.LatestVersion,
"channel", info.Channel,
"status", info.Status,
"architecture", info.Architecture,
)
return info, nil
}