diff --git a/internal/env.go b/internal/env.go index e91c4f0440991..850afad23bcb2 100644 --- a/internal/env.go +++ b/internal/env.go @@ -1,6 +1,9 @@ package internal -import "os" +import ( + "os" + "path/filepath" +) // GetProcPath returns the path stored in HOST_PROC env variable, or /proc if HOST_PROC has not been set. func GetProcPath() string { @@ -15,5 +18,36 @@ func GetSysPath() string { if hostSys := os.Getenv("HOST_SYS"); hostSys != "" { return hostSys } + if prefix := os.Getenv("HOST_MOUNT_PREFIX"); prefix != "" { + return filepath.Join(prefix, "sys") + } return "/sys" } + +// GetDevPath returns the path of the host /dev tree when telegraf is +// running inside a container. It prefers the explicit HOST_DEV env var +// (matching the gopsutil convention), falls back to +// HOST_MOUNT_PREFIX/dev so a single HOST_MOUNT_PREFIX covers /dev, +// /proc, /sys, /run in one go, and finally defaults to /dev on bare +// metal. See influxdata/telegraf#18671. +func GetDevPath() string { + if hostDev := os.Getenv("HOST_DEV"); hostDev != "" { + return hostDev + } + if prefix := os.Getenv("HOST_MOUNT_PREFIX"); prefix != "" { + return filepath.Join(prefix, "dev") + } + return "/dev" +} + +// GetRunPath returns the path of the host /run tree. Mirrors GetDevPath +// and is used by plugins that read /run/udev entries. +func GetRunPath() string { + if hostRun := os.Getenv("HOST_RUN"); hostRun != "" { + return hostRun + } + if prefix := os.Getenv("HOST_MOUNT_PREFIX"); prefix != "" { + return filepath.Join(prefix, "run") + } + return "/run" +} diff --git a/plugins/inputs/diskio/diskio_linux.go b/plugins/inputs/diskio/diskio_linux.go index 1c0db61466f83..619b5d1d944cd 100644 --- a/plugins/inputs/diskio/diskio_linux.go +++ b/plugins/inputs/diskio/diskio_linux.go @@ -11,6 +11,8 @@ import ( "strings" "golang.org/x/sys/unix" + + "github.com/influxdata/telegraf/internal" ) type diskInfoCache struct { @@ -21,8 +23,12 @@ type diskInfoCache struct { } func (d *DiskIO) diskInfo(devName string) (map[string]string, error) { + devPath := internal.GetDevPath() + runPath := internal.GetRunPath() + sysPath := internal.GetSysPath() + // Check if the device exists - path := "/dev/" + devName + path := filepath.Join(devPath, devName) var stat unix.Stat_t if err := unix.Stat(path, &stat); err != nil { return nil, fmt.Errorf("error reading %s: %w", path, err) @@ -43,10 +49,10 @@ func (d *DiskIO) diskInfo(devName string) (map[string]string, error) { } else { major := unix.Major(uint64(stat.Rdev)) //nolint:unconvert // Conversion needed for some architectures minor := unix.Minor(uint64(stat.Rdev)) //nolint:unconvert // Conversion needed for some architectures - udevDataPath = fmt.Sprintf("/run/udev/data/b%d:%d", major, minor) + udevDataPath = filepath.Join(runPath, "udev", "data", fmt.Sprintf("b%d:%d", major, minor)) if _, err := os.Stat(udevDataPath); err != nil { // This path failed, try the fallback .udev style (non-systemd) - udevDataPath = "/dev/.udev/db/block:" + devName + udevDataPath = filepath.Join(devPath, ".udev", "db", "block:"+devName) if _, err := os.Stat(udevDataPath); err != nil { // Giving up, cannot retrieve disk info return nil, fmt.Errorf("error reading %s: %w", udevDataPath, err) @@ -66,7 +72,7 @@ func (d *DiskIO) diskInfo(devName string) (map[string]string, error) { // This allows us to also "poison" it during test scenarios sysBlockPath = ic.sysBlockPath } else { - sysBlockPath = "/sys/class/block/" + devName + sysBlockPath = filepath.Join(sysPath, "class", "block", devName) } devInfo, err := readDevData(sysBlockPath) @@ -173,8 +179,8 @@ func resolveName(name string) string { if !errors.Is(err, fs.ErrNotExist) { return name } - // Try to prepend "/dev" - resolved, err = filepath.EvalSymlinks("/dev/" + name) + // Try to prepend the host /dev path (HOST_DEV / HOST_MOUNT_PREFIX). + resolved, err = filepath.EvalSymlinks(filepath.Join(internal.GetDevPath(), name)) if err != nil { return name } @@ -183,7 +189,7 @@ func resolveName(name string) string { } func getDeviceWWID(name string) string { - path := fmt.Sprintf("/sys/block/%s/wwid", filepath.Base(name)) + path := filepath.Join(internal.GetSysPath(), "block", filepath.Base(name), "wwid") buf, err := os.ReadFile(path) if err != nil { return ""