diff --git a/plugins/inputs/temp/temp_linux.go b/plugins/inputs/temp/temp_linux.go index 7282f06333828..f22bd4955aa2e 100644 --- a/plugins/inputs/temp/temp_linux.go +++ b/plugins/inputs/temp/temp_linux.go @@ -4,12 +4,15 @@ package temp import ( + "errors" "fmt" "os" "path/filepath" "strconv" "strings" + "golang.org/x/sys/unix" + "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" ) @@ -233,8 +236,12 @@ func (t *Temperature) gatherThermalZone(syspath string) ([]temperatureStat, erro name := strings.TrimSpace(string(buf)) // Actual temperature - buf, err = os.ReadFile(filepath.Join(path, "temp")) + buf, err = readFileAsync(filepath.Join(path, "temp")) if err != nil { + if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK) { + // Silently ignore this thermal-zone when the underlying hardware is unavailable + continue + } t.Log.Errorf("Cannot read temperature of zone %q", path) continue } @@ -249,3 +256,21 @@ func (t *Temperature) gatherThermalZone(syspath string) ([]temperatureStat, erro return stats, nil } + +func readFileAsync(path string) ([]byte, error) { + fd, err := unix.Open(path, unix.O_RDONLY|unix.O_NONBLOCK, 0) + if err != nil { + return nil, err + } + defer unix.Close(fd) + + // The kernal specifies the temparatures are expressed in millidegrees + // tempBuf of 8 bytes stores temperatures up-to 99999.999°C + var tempBuf [8]byte + + n, err := unix.Read(fd, tempBuf[:]) + if err != nil { + return nil, err + } + return tempBuf[:n], nil +} diff --git a/plugins/inputs/temp/temp_test.go b/plugins/inputs/temp/temp_test.go index b990796459c35..97e0469cd7f33 100644 --- a/plugins/inputs/temp/temp_test.go +++ b/plugins/inputs/temp/temp_test.go @@ -3,6 +3,7 @@ package temp import ( + "bytes" "fmt" "os" "path/filepath" @@ -13,6 +14,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/shirou/gopsutil/v4/sensors" "github.com/stretchr/testify/require" + "golang.org/x/sys/unix" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/config" @@ -342,3 +344,64 @@ func sensorsTemperaturesOld(syspath string) ([]sensors.TemperatureStat, error) { } return temperatures, nil } + +func TestReadFileAsync(t *testing.T) { + // fds stores the in-memory file-descriptors + fds := make([]int, 2) + // Unnamed pipe to simulate EAGAIN + require.NoError(t, unix.Pipe(fds[:])) + readFd, writeFd := fds[0], fds[1] + t.Cleanup(func() { + require.NoError(t, unix.Close(readFd)) + require.NoError(t, unix.Close(writeFd)) + }) + + // Convert the file descriptor to a virtual file path + fifoFile := fmt.Sprintf("/dev/fd/%d", readFd) + + tests := []struct { + name string + path string + expectedValue []byte + expectedError error + }{ + { + name: "Valid file", + path: filepath.Join("testdata", "temp.normal"), + expectedValue: []byte("45000"), + expectedError: nil, + }, + { + name: "File larger than 8 bytes (Truncation)", + path: filepath.Join("testdata", "temp.too_long"), + expectedValue: bytes.Repeat([]byte("9"), 8), + expectedError: nil, + }, + { + name: "Empty file", + path: filepath.Join("testdata", "temp.empty"), + expectedValue: make([]byte, 0), + expectedError: nil, + }, + { + name: "File does not exist", + path: filepath.Join("testdata", "temp.missing"), + expectedValue: nil, + expectedError: unix.ENOENT, + }, + { + name: "Non-blocking read on empty pipe (EAGAIN)", + path: fifoFile, + expectedValue: nil, + expectedError: unix.EAGAIN, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data, err := readFileAsync(tt.path) + require.ErrorIs(t, err, tt.expectedError) + require.Equal(t, tt.expectedValue, data) + }) + } +} diff --git a/plugins/inputs/temp/testdata/temp.empty b/plugins/inputs/temp/testdata/temp.empty new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/inputs/temp/testdata/temp.normal b/plugins/inputs/temp/testdata/temp.normal new file mode 100644 index 0000000000000..81908d20d4307 --- /dev/null +++ b/plugins/inputs/temp/testdata/temp.normal @@ -0,0 +1 @@ +45000 \ No newline at end of file diff --git a/plugins/inputs/temp/testdata/temp.too_long b/plugins/inputs/temp/testdata/temp.too_long new file mode 100644 index 0000000000000..b62a17c0a1294 --- /dev/null +++ b/plugins/inputs/temp/testdata/temp.too_long @@ -0,0 +1 @@ +9999999911 \ No newline at end of file