From b53fdbe0ef357767ce6eba88696c11d8d403739f Mon Sep 17 00:00:00 2001 From: henrygd Date: Fri, 27 Mar 2026 13:35:04 -0400 Subject: [PATCH] fix(agent): find macmon if /opt/homebrew/bin is not in path (#1746) --- agent/gpu.go | 2 +- agent/gpu_darwin.go | 7 ++++- agent/smart.go | 31 ++++++------------- agent/utils/utils.go | 24 ++++++++++++++ .../components/routes/system/smart-table.tsx | 12 ++++--- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/agent/gpu.go b/agent/gpu.go index 8a0ffc1b..03c0783e 100644 --- a/agent/gpu.go +++ b/agent/gpu.go @@ -461,7 +461,7 @@ func (gm *GPUManager) discoverGpuCapabilities() gpuCapabilities { caps.hasNvtop = true } if runtime.GOOS == "darwin" { - if _, err := exec.LookPath(macmonCmd); err == nil { + if _, err := utils.LookPathHomebrew(macmonCmd); err == nil { caps.hasMacmon = true } if _, err := exec.LookPath(powermetricsCmd); err == nil { diff --git a/agent/gpu_darwin.go b/agent/gpu_darwin.go index a3ba1e1f..5c648f78 100644 --- a/agent/gpu_darwin.go +++ b/agent/gpu_darwin.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/henrygd/beszel/agent/utils" "github.com/henrygd/beszel/internal/entities/system" ) @@ -171,7 +172,11 @@ type macmonSample struct { } func (gm *GPUManager) collectMacmonPipe() (err error) { - cmd := exec.Command(macmonCmd, "pipe", "-i", strconv.Itoa(macmonIntervalMs)) + macmonPath, err := utils.LookPathHomebrew(macmonCmd) + if err != nil { + return err + } + cmd := exec.Command(macmonPath, "pipe", "-i", strconv.Itoa(macmonIntervalMs)) // Avoid blocking if macmon writes to stderr. cmd.Stderr = io.Discard stdout, err := cmd.StdoutPipe() diff --git a/agent/smart.go b/agent/smart.go index 5fbb2c64..b0a10d7a 100644 --- a/agent/smart.go +++ b/agent/smart.go @@ -1104,32 +1104,21 @@ func (sm *SmartManager) parseSmartForNvme(output []byte) (bool, int) { // detectSmartctl checks if smartctl is installed, returns an error if not func (sm *SmartManager) detectSmartctl() (string, error) { - isWindows := runtime.GOOS == "windows" - - // Load embedded smartctl.exe for Windows amd64 builds. - if isWindows && runtime.GOARCH == "amd64" { - if path, err := ensureEmbeddedSmartctl(); err == nil { - return path, nil + if runtime.GOOS == "windows" { + // Load embedded smartctl.exe for Windows amd64 builds. + if runtime.GOARCH == "amd64" { + if path, err := ensureEmbeddedSmartctl(); err == nil { + return path, nil + } } - } - - if path, err := exec.LookPath("smartctl"); err == nil { - return path, nil - } - locations := []string{} - if isWindows { - locations = append(locations, - "C:\\Program Files\\smartmontools\\bin\\smartctl.exe", - ) - } else { - locations = append(locations, "/opt/homebrew/bin/smartctl") - } - for _, location := range locations { + // Try to find smartctl in the default installation location + const location = "C:\\Program Files\\smartmontools\\bin\\smartctl.exe" if _, err := os.Stat(location); err == nil { return location, nil } } - return "", errors.New("smartctl not found") + + return utils.LookPathHomebrew("smartctl") } // isNvmeControllerPath checks if the path matches an NVMe controller pattern diff --git a/agent/utils/utils.go b/agent/utils/utils.go index 1941a909..1e682b1e 100644 --- a/agent/utils/utils.go +++ b/agent/utils/utils.go @@ -4,6 +4,9 @@ import ( "io" "math" "os" + "os/exec" + "path/filepath" + "runtime" "strconv" "strings" ) @@ -86,3 +89,24 @@ func ReadUintFile(path string) (uint64, bool) { } return parsed, true } + +// LookPathHomebrew is like exec.LookPath but also checks Homebrew paths. +func LookPathHomebrew(file string) (string, error) { + foundPath, lookPathErr := exec.LookPath(file) + if lookPathErr == nil { + return foundPath, nil + } + var homebrewPath string + switch runtime.GOOS { + case "darwin": + homebrewPath = filepath.Join("/opt", "homebrew", "bin", file) + case "linux": + homebrewPath = filepath.Join("/home", "linuxbrew", ".linuxbrew", "bin", file) + } + if homebrewPath != "" { + if _, err := os.Stat(homebrewPath); err == nil { + return homebrewPath, nil + } + } + return "", lookPathErr +} diff --git a/internal/site/src/components/routes/system/smart-table.tsx b/internal/site/src/components/routes/system/smart-table.tsx index 270c338b..a67a17ed 100644 --- a/internal/site/src/components/routes/system/smart-table.tsx +++ b/internal/site/src/components/routes/system/smart-table.tsx @@ -620,11 +620,13 @@ const SmartDevicesTable = memo(function SmartDevicesTable({ return }) ) : ( - - - {data ? t`No results.` : } - - + + {data ? ( + No results. + ) : ( + + )} + )}