fix(agent): find macmon if /opt/homebrew/bin is not in path (#1746)

This commit is contained in:
henrygd
2026-03-27 13:35:04 -04:00
parent c7261b56f1
commit b53fdbe0ef
5 changed files with 48 additions and 28 deletions

View File

@@ -461,7 +461,7 @@ func (gm *GPUManager) discoverGpuCapabilities() gpuCapabilities {
caps.hasNvtop = true caps.hasNvtop = true
} }
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
if _, err := exec.LookPath(macmonCmd); err == nil { if _, err := utils.LookPathHomebrew(macmonCmd); err == nil {
caps.hasMacmon = true caps.hasMacmon = true
} }
if _, err := exec.LookPath(powermetricsCmd); err == nil { if _, err := exec.LookPath(powermetricsCmd); err == nil {

View File

@@ -13,6 +13,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/henrygd/beszel/agent/utils"
"github.com/henrygd/beszel/internal/entities/system" "github.com/henrygd/beszel/internal/entities/system"
) )
@@ -171,7 +172,11 @@ type macmonSample struct {
} }
func (gm *GPUManager) collectMacmonPipe() (err error) { 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. // Avoid blocking if macmon writes to stderr.
cmd.Stderr = io.Discard cmd.Stderr = io.Discard
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()

View File

@@ -1104,32 +1104,21 @@ func (sm *SmartManager) parseSmartForNvme(output []byte) (bool, int) {
// detectSmartctl checks if smartctl is installed, returns an error if not // detectSmartctl checks if smartctl is installed, returns an error if not
func (sm *SmartManager) detectSmartctl() (string, error) { func (sm *SmartManager) detectSmartctl() (string, error) {
isWindows := runtime.GOOS == "windows" if runtime.GOOS == "windows" {
// Load embedded smartctl.exe for Windows amd64 builds.
// Load embedded smartctl.exe for Windows amd64 builds. if runtime.GOARCH == "amd64" {
if isWindows && runtime.GOARCH == "amd64" { if path, err := ensureEmbeddedSmartctl(); err == nil {
if path, err := ensureEmbeddedSmartctl(); err == nil { return path, nil
return path, nil }
} }
} // Try to find smartctl in the default installation location
const location = "C:\\Program Files\\smartmontools\\bin\\smartctl.exe"
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 {
if _, err := os.Stat(location); err == nil { if _, err := os.Stat(location); err == nil {
return location, nil return location, nil
} }
} }
return "", errors.New("smartctl not found")
return utils.LookPathHomebrew("smartctl")
} }
// isNvmeControllerPath checks if the path matches an NVMe controller pattern // isNvmeControllerPath checks if the path matches an NVMe controller pattern

View File

@@ -4,6 +4,9 @@ import (
"io" "io"
"math" "math"
"os" "os"
"os/exec"
"path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
) )
@@ -86,3 +89,24 @@ func ReadUintFile(path string) (uint64, bool) {
} }
return parsed, true 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
}

View File

@@ -620,11 +620,13 @@ const SmartDevicesTable = memo(function SmartDevicesTable({
return <SmartDeviceTableRow key={row.id} row={row} virtualRow={virtualRow} openSheet={openSheet} /> return <SmartDeviceTableRow key={row.id} row={row} virtualRow={virtualRow} openSheet={openSheet} />
}) })
) : ( ) : (
<TableRow> <TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
<TableCell colSpan={colLength} className="h-24 text-center pointer-events-none"> {data ? (
{data ? t`No results.` : <LoaderCircleIcon className="animate-spin size-10 opacity-60 mx-auto" />} <Trans>No results.</Trans>
</TableCell> ) : (
</TableRow> <LoaderCircleIcon className="animate-spin size-10 opacity-60 mx-auto" />
)}
</TableCell>
)} )}
</TableBody> </TableBody>
</table> </table>