diff --git a/agent/fans.go b/agent/fans.go index 0e3b2688f..2562d8d22 100644 --- a/agent/fans.go +++ b/agent/fans.go @@ -72,14 +72,30 @@ func discoverHwmonFans(root string) ([]fanSensor, error) { var sensors []fanSensor for _, entry := range entries { chipDir := filepath.Join(root, entry.Name()) - chipName := utils.ReadStringFile(filepath.Join(chipDir, "name")) + sensorDir := chipDir + inputs, _ := filepath.Glob(filepath.Join(sensorDir, "fan*_input")) + + // Some legacy hwmon drivers (notably applesmc) register a hwmon class + // device but create fan attributes on the parent platform device. In + // sysfs that parent is exposed through hwmonN/device. + if len(inputs) == 0 { + deviceDir := filepath.Join(chipDir, "device") + if deviceInputs, _ := filepath.Glob(filepath.Join(deviceDir, "fan*_input")); len(deviceInputs) > 0 { + sensorDir = deviceDir + inputs = deviceInputs + } + } + + chipName := utils.ReadStringFile(filepath.Join(sensorDir, "name")) + if chipName == "" { + chipName = utils.ReadStringFile(filepath.Join(chipDir, "name")) + } if chipName == "" { chipName = entry.Name() } - inputs, _ := filepath.Glob(filepath.Join(chipDir, "fan*_input")) for _, inputPath := range inputs { base := strings.TrimSuffix(filepath.Base(inputPath), "_input") - label := utils.ReadStringFile(filepath.Join(chipDir, base+"_label")) + label := utils.ReadStringFile(filepath.Join(sensorDir, base+"_label")) key := chipName + "_" + base if label != "" { key = chipName + "_" + label diff --git a/agent/fans_test.go b/agent/fans_test.go index ab3743343..3bdb480d6 100644 --- a/agent/fans_test.go +++ b/agent/fans_test.go @@ -50,6 +50,24 @@ func TestReadHwmonFans(t *testing.T) { }, fans) } +// TestReadHwmonFansLegacyParent verifies legacy hwmon layouts such as applesmc, +// where the hwmon class node exists but fan attributes live on hwmonN/device. +func TestReadHwmonFansLegacyParent(t *testing.T) { + root := t.TempDir() + deviceDir := filepath.Join(root, "devices", "applesmc.768") + writeFile(t, filepath.Join(deviceDir, "name"), "applesmc\n") + writeFile(t, filepath.Join(deviceDir, "fan1_input"), "1202\n") + writeFile(t, filepath.Join(deviceDir, "fan1_label"), "Exhaust\n") + + chipDir := filepath.Join(root, "hwmon1") + require.NoError(t, os.MkdirAll(chipDir, 0o755)) + require.NoError(t, os.Symlink(deviceDir, filepath.Join(chipDir, "device"))) + + fans, err := readHwmonFans(root) + require.NoError(t, err) + assert.Equal(t, map[string]uint16{"applesmc_Exhaust": 1202}, fans) +} + // TestReadHwmonFansMissingRoot returns an error rather than panicking when the // hwmon root doesn't exist (e.g. running on a kernel without hwmon support). func TestReadHwmonFansMissingRoot(t *testing.T) {