diff --git a/host/host.go b/host/host.go
index 1e401a220..b69d2f624 100644
--- a/host/host.go
+++ b/host/host.go
@@ -41,13 +41,6 @@ type UserStat struct {
Started int `json:"started"`
}
-type TemperatureStat struct {
- SensorKey string `json:"sensorKey"`
- Temperature float64 `json:"temperature"`
- High float64 `json:"sensorHigh"`
- Critical float64 `json:"sensorCritical"`
-}
-
func (h InfoStat) String() string {
s, _ := json.Marshal(h)
return string(s)
@@ -58,11 +51,6 @@ func (u UserStat) String() string {
return string(s)
}
-func (t TemperatureStat) String() string {
- s, _ := json.Marshal(t)
- return string(s)
-}
-
var enableBootTimeCache bool
// EnableBootTimeCache change cache behavior of BootTime. If true, cache BootTime value. Default is false.
@@ -158,10 +146,6 @@ func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
-func SensorsTemperatures() ([]TemperatureStat, error) {
- return SensorsTemperaturesWithContext(context.Background())
-}
-
func timeSince(ts uint64) uint64 {
return uint64(time.Now().Unix()) - ts
}
diff --git a/host/host_fallback.go b/host/host_fallback.go
index dcfc4bc07..f6d9b79dc 100644
--- a/host/host_fallback.go
+++ b/host/host_fallback.go
@@ -41,10 +41,6 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string
return "", "", "", common.ErrNotImplementedError
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- return []TemperatureStat{}, common.ErrNotImplementedError
-}
-
func KernelArch() (string, error) {
return "", common.ErrNotImplementedError
}
diff --git a/host/host_freebsd.go b/host/host_freebsd.go
index 884e079df..97aa05a14 100644
--- a/host/host_freebsd.go
+++ b/host/host_freebsd.go
@@ -141,10 +141,6 @@ func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
return ret, nil
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- return []TemperatureStat{}, common.ErrNotImplementedError
-}
-
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformationWithContext(ctx)
return version, err
diff --git a/host/host_linux.go b/host/host_linux.go
index f8a4aa2bb..04bda6c98 100644
--- a/host/host_linux.go
+++ b/host/host_linux.go
@@ -10,9 +10,7 @@ import (
"fmt"
"io"
"os"
- "path/filepath"
"regexp"
- "strconv"
"strings"
"golang.org/x/sys/unix"
@@ -30,8 +28,6 @@ type lsbStruct struct {
// from utmp.h
const (
user_PROCESS = 7
-
- hostTemperatureScale = 1000.0
)
func HostIDWithContext(ctx context.Context) (string, error) {
@@ -392,147 +388,3 @@ func getSusePlatform(contents []string) string {
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return common.VirtualizationWithContext(ctx)
}
-
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- var err error
-
- var files []string
-
- temperatures := make([]TemperatureStat, 0)
-
- // Only the temp*_input file provides current temperature
- // value in millidegree Celsius as reported by the temperature to the device:
- // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
- if files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/hwmon/hwmon*/temp*_input")); err != nil {
- return temperatures, err
- }
-
- if len(files) == 0 {
- // CentOS has an intermediate /device directory:
- // https://github.com/giampaolo/psutil/issues/971
- if files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/hwmon/hwmon*/device/temp*_input")); err != nil {
- return temperatures, err
- }
- }
-
- var warns Warnings
-
- if len(files) == 0 { // handle distributions without hwmon, like raspbian #391, parse legacy thermal_zone files
- files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/thermal/thermal_zone*/"))
- if err != nil {
- return temperatures, err
- }
- for _, file := range files {
- // Get the name of the temperature you are reading
- name, err := os.ReadFile(filepath.Join(file, "type"))
- if err != nil {
- warns.Add(err)
- continue
- }
- // Get the temperature reading
- current, err := os.ReadFile(filepath.Join(file, "temp"))
- if err != nil {
- warns.Add(err)
- continue
- }
- temperature, err := strconv.ParseInt(strings.TrimSpace(string(current)), 10, 64)
- if err != nil {
- warns.Add(err)
- continue
- }
-
- temperatures = append(temperatures, TemperatureStat{
- SensorKey: strings.TrimSpace(string(name)),
- Temperature: float64(temperature) / 1000.0,
- })
- }
- return temperatures, warns.Reference()
- }
-
- temperatures = make([]TemperatureStat, 0, len(files))
-
- // example directory
- // device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm
- // name temp1_input temp2_input temp3_input temp4_input temp5_input temp6_input temp7_input
- // power/ temp1_label temp2_label temp3_label temp4_label temp5_label temp6_label temp7_label
- // subsystem/ temp1_max temp2_max temp3_max temp4_max temp5_max temp6_max temp7_max
- // temp1_crit temp2_crit temp3_crit temp4_crit temp5_crit temp6_crit temp7_crit uevent
- for _, file := range files {
- var raw []byte
-
- var temperature float64
-
- // Get the base directory location
- directory := filepath.Dir(file)
-
- // Get the base filename prefix like temp1
- basename := strings.Split(filepath.Base(file), "_")[0]
-
- // Get the base path like
/temp1
- basepath := filepath.Join(directory, basename)
-
- // Get the label of the temperature you are reading
- label := ""
-
- if raw, _ = os.ReadFile(basepath + "_label"); len(raw) != 0 {
- // Format the label from "Core 0" to "core_0"
- label = strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(raw))), " "), "_")
- }
-
- // Get the name of the temperature you are reading
- if raw, err = os.ReadFile(filepath.Join(directory, "name")); err != nil {
- warns.Add(err)
- continue
- }
-
- name := strings.TrimSpace(string(raw))
-
- if label != "" {
- name = name + "_" + label
- }
-
- // Get the temperature reading
- if raw, err = os.ReadFile(file); err != nil {
- warns.Add(err)
- continue
- }
-
- if temperature, err = strconv.ParseFloat(strings.TrimSpace(string(raw)), 64); err != nil {
- warns.Add(err)
- continue
- }
-
- // Add discovered temperature sensor to the list
- temperatures = append(temperatures, TemperatureStat{
- SensorKey: name,
- Temperature: temperature / hostTemperatureScale,
- High: optionalValueReadFromFile(basepath+"_max") / hostTemperatureScale,
- Critical: optionalValueReadFromFile(basepath+"_crit") / hostTemperatureScale,
- })
- }
-
- return temperatures, warns.Reference()
-}
-
-func optionalValueReadFromFile(filename string) float64 {
- var raw []byte
-
- var err error
-
- var value float64
-
- // Check if file exists
- if _, err := os.Stat(filename); os.IsNotExist(err) {
- return 0
- }
-
- if raw, err = os.ReadFile(filename); err != nil {
- return 0
- }
-
- if value, err = strconv.ParseFloat(strings.TrimSpace(string(raw)), 64); err != nil {
- return 0
- }
-
- return value
-}
diff --git a/host/host_netbsd.go b/host/host_netbsd.go
index 5bc338ec6..f3cddb7be 100644
--- a/host/host_netbsd.go
+++ b/host/host_netbsd.go
@@ -45,10 +45,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return ret, common.ErrNotImplementedError
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- return []TemperatureStat{}, common.ErrNotImplementedError
-}
-
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformationWithContext(ctx)
return version, err
diff --git a/host/host_openbsd.go b/host/host_openbsd.go
index 47db4ff8d..f21c5e859 100644
--- a/host/host_openbsd.go
+++ b/host/host_openbsd.go
@@ -95,10 +95,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return ret, nil
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- return []TemperatureStat{}, common.ErrNotImplementedError
-}
-
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformationWithContext(ctx)
return version, err
diff --git a/host/host_solaris.go b/host/host_solaris.go
index ae4e710a7..371cc98e8 100644
--- a/host/host_solaris.go
+++ b/host/host_solaris.go
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: BSD-3-Clause
+//go:build solaris
+
package host
import (
"bufio"
"bytes"
"context"
- "encoding/csv"
"fmt"
- "io"
"os"
"regexp"
"strconv"
@@ -95,43 +95,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return []UserStat{}, common.ErrNotImplementedError
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- var ret []TemperatureStat
-
- out, err := invoke.CommandWithContext(ctx, "ipmitool", "-c", "sdr", "list")
- if err != nil {
- return ret, err
- }
-
- r := csv.NewReader(strings.NewReader(string(out)))
- // Output may contain errors, e.g. "bmc_send_cmd: Permission denied", don't expect a consistent number of records
- r.FieldsPerRecord = -1
- for {
- record, err := r.Read()
- if err == io.EOF {
- break
- }
- if err != nil {
- return ret, err
- }
- // CPU1 Temp,40,degrees C,ok
- if len(record) < 3 || record[1] == "" || record[2] != "degrees C" {
- continue
- }
- v, err := strconv.ParseFloat(record[1], 64)
- if err != nil {
- return ret, err
- }
- ts := TemperatureStat{
- SensorKey: strings.TrimSuffix(record[0], " Temp"),
- Temperature: v,
- }
- ret = append(ret, ts)
- }
-
- return ret, nil
-}
-
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
diff --git a/host/host_test.go b/host/host_test.go
index 5eda93896..3e53ca74e 100644
--- a/host/host_test.go
+++ b/host/host_test.go
@@ -137,19 +137,6 @@ func TestHostGuid(t *testing.T) {
}
}
-func TestTemperatureStat_String(t *testing.T) {
- v := TemperatureStat{
- SensorKey: "CPU",
- Temperature: 1.1,
- High: 30.1,
- Critical: 0.1,
- }
- s := `{"sensorKey":"CPU","temperature":1.1,"sensorHigh":30.1,"sensorCritical":0.1}`
- if s != fmt.Sprintf("%v", v) {
- t.Errorf("TemperatureStat string is invalid, %v", fmt.Sprintf("%v", v))
- }
-}
-
func TestVirtualization(t *testing.T) {
wg := sync.WaitGroup{}
testCount := 10
diff --git a/host/host_windows.go b/host/host_windows.go
index 11783a641..1e02e01ee 100644
--- a/host/host_windows.go
+++ b/host/host_windows.go
@@ -6,7 +6,6 @@ package host
import (
"context"
"fmt"
- "math"
"strconv"
"strings"
"sync/atomic"
@@ -16,7 +15,6 @@ import (
"github.com/shirou/gopsutil/v4/internal/common"
"github.com/shirou/gopsutil/v4/process"
- "github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@@ -57,13 +55,6 @@ type systemInfo struct {
wProcessorRevision uint16
}
-type msAcpi_ThermalZoneTemperature struct {
- Active bool
- CriticalTripPoint uint32
- CurrentTemperature uint32
- InstanceName string
-}
-
func HostIDWithContext(ctx context.Context) (string, error) {
// there has been reports of issues on 32bit using golang.org/x/sys/windows/registry, see https://github.com/shirou/gopsutil/pull/312#issuecomment-277422612
// for rationale of using windows.RegOpenKeyEx/RegQueryValueEx instead of registry.OpenKey/GetStringValue
@@ -232,33 +223,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return ret, common.ErrNotImplementedError
}
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
- var ret []TemperatureStat
- var dst []msAcpi_ThermalZoneTemperature
- q := wmi.CreateQuery(&dst, "")
- if err := common.WMIQueryWithContext(ctx, q, &dst, nil, "root/wmi"); err != nil {
- return ret, err
- }
-
- for _, v := range dst {
- ts := TemperatureStat{
- SensorKey: v.InstanceName,
- Temperature: kelvinToCelsius(v.CurrentTemperature, 2),
- }
- ret = append(ret, ts)
- }
-
- return ret, nil
-}
-
-func kelvinToCelsius(temp uint32, n int) float64 {
- // wmi return temperature Kelvin * 10, so need to divide the result by 10,
- // and then minus 273.15 to get °Celsius.
- t := float64(temp/10) - 273.15
- n10 := math.Pow10(n)
- return math.Trunc((t+0.5/n10)*n10) / n10
-}
-
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
diff --git a/sensors/sensors.go b/sensors/sensors.go
new file mode 100644
index 000000000..feb399626
--- /dev/null
+++ b/sensors/sensors.go
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+package sensors
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+type Warnings = common.Warnings
+
+var invoke common.Invoker = common.Invoke{}
+
+type TemperatureStat struct {
+ SensorKey string `json:"sensorKey"`
+ Temperature float64 `json:"temperature"`
+ High float64 `json:"sensorHigh"`
+ Critical float64 `json:"sensorCritical"`
+}
+
+func (t TemperatureStat) String() string {
+ s, _ := json.Marshal(t)
+ return string(s)
+}
+
+func SensorsTemperatures() ([]TemperatureStat, error) {
+ return TemperaturesWithContext(context.Background())
+}
diff --git a/host/host_darwin_cgo.go b/sensors/sensors_darwin_cgo.go
similarity index 90%
rename from host/host_darwin_cgo.go
rename to sensors/sensors_darwin_cgo.go
index 93c613eb7..6ac656b88 100644
--- a/host/host_darwin_cgo.go
+++ b/sensors/sensors_darwin_cgo.go
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && cgo
-package host
+package sensors
// #cgo LDFLAGS: -framework IOKit
// #include "smc_darwin.h"
import "C"
import "context"
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
temperatureKeys := []string{
C.AMBIENT_AIR_0,
C.AMBIENT_AIR_1,
diff --git a/host/host_darwin_nocgo.go b/sensors/sensors_darwin_nocgo.go
similarity index 67%
rename from host/host_darwin_nocgo.go
rename to sensors/sensors_darwin_nocgo.go
index d73a89334..45c85069e 100644
--- a/host/host_darwin_nocgo.go
+++ b/sensors/sensors_darwin_nocgo.go
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin && !cgo
-package host
+package sensors
import (
"context"
@@ -9,6 +9,6 @@ import (
"github.com/shirou/gopsutil/v4/internal/common"
)
-func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
}
diff --git a/sensors/sensors_fallback.go b/sensors/sensors_fallback.go
new file mode 100644
index 000000000..40471d844
--- /dev/null
+++ b/sensors/sensors_fallback.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build !darwin && !linux && !freebsd && !openbsd && !netbsd && !solaris && !windows
+
+package sensors
+
+import (
+ "context"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ return []TemperatureStat{}, common.ErrNotImplementedError
+}
diff --git a/sensors/sensors_freebsd.go b/sensors/sensors_freebsd.go
new file mode 100644
index 000000000..f7bb29c4f
--- /dev/null
+++ b/sensors/sensors_freebsd.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build freebsd
+
+package freebsd
+
+import (
+ "context"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ return []TemperatureStat{}, common.ErrNotImplementedError
+}
diff --git a/sensors/sensors_linux.go b/sensors/sensors_linux.go
new file mode 100644
index 000000000..85548f642
--- /dev/null
+++ b/sensors/sensors_linux.go
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build linux
+
+package sensors
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+// from utmp.h
+const (
+ hostTemperatureScale = 1000.0
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ var err error
+
+ var files []string
+
+ temperatures := make([]TemperatureStat, 0)
+
+ // Only the temp*_input file provides current temperature
+ // value in millidegree Celsius as reported by the temperature to the device:
+ // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
+ if files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/hwmon/hwmon*/temp*_input")); err != nil {
+ return temperatures, err
+ }
+
+ if len(files) == 0 {
+ // CentOS has an intermediate /device directory:
+ // https://github.com/giampaolo/psutil/issues/971
+ if files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/hwmon/hwmon*/device/temp*_input")); err != nil {
+ return temperatures, err
+ }
+ }
+
+ var warns Warnings
+
+ if len(files) == 0 { // handle distributions without hwmon, like raspbian #391, parse legacy thermal_zone files
+ files, err = filepath.Glob(common.HostSysWithContext(ctx, "/class/thermal/thermal_zone*/"))
+ if err != nil {
+ return temperatures, err
+ }
+ for _, file := range files {
+ // Get the name of the temperature you are reading
+ name, err := os.ReadFile(filepath.Join(file, "type"))
+ if err != nil {
+ warns.Add(err)
+ continue
+ }
+ // Get the temperature reading
+ current, err := os.ReadFile(filepath.Join(file, "temp"))
+ if err != nil {
+ warns.Add(err)
+ continue
+ }
+ temperature, err := strconv.ParseInt(strings.TrimSpace(string(current)), 10, 64)
+ if err != nil {
+ warns.Add(err)
+ continue
+ }
+
+ temperatures = append(temperatures, TemperatureStat{
+ SensorKey: strings.TrimSpace(string(name)),
+ Temperature: float64(temperature) / 1000.0,
+ })
+ }
+ return temperatures, warns.Reference()
+ }
+
+ temperatures = make([]TemperatureStat, 0, len(files))
+
+ // example directory
+ // device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm
+ // name temp1_input temp2_input temp3_input temp4_input temp5_input temp6_input temp7_input
+ // power/ temp1_label temp2_label temp3_label temp4_label temp5_label temp6_label temp7_label
+ // subsystem/ temp1_max temp2_max temp3_max temp4_max temp5_max temp6_max temp7_max
+ // temp1_crit temp2_crit temp3_crit temp4_crit temp5_crit temp6_crit temp7_crit uevent
+ for _, file := range files {
+ var raw []byte
+
+ var temperature float64
+
+ // Get the base directory location
+ directory := filepath.Dir(file)
+
+ // Get the base filename prefix like temp1
+ basename := strings.Split(filepath.Base(file), "_")[0]
+
+ // Get the base path like /temp1
+ basepath := filepath.Join(directory, basename)
+
+ // Get the label of the temperature you are reading
+ label := ""
+
+ if raw, _ = os.ReadFile(basepath + "_label"); len(raw) != 0 {
+ // Format the label from "Core 0" to "core_0"
+ label = strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(raw))), " "), "_")
+ }
+
+ // Get the name of the temperature you are reading
+ if raw, err = os.ReadFile(filepath.Join(directory, "name")); err != nil {
+ warns.Add(err)
+ continue
+ }
+
+ name := strings.TrimSpace(string(raw))
+
+ if label != "" {
+ name = name + "_" + label
+ }
+
+ // Get the temperature reading
+ if raw, err = os.ReadFile(file); err != nil {
+ warns.Add(err)
+ continue
+ }
+
+ if temperature, err = strconv.ParseFloat(strings.TrimSpace(string(raw)), 64); err != nil {
+ warns.Add(err)
+ continue
+ }
+
+ // Add discovered temperature sensor to the list
+ temperatures = append(temperatures, TemperatureStat{
+ SensorKey: name,
+ Temperature: temperature / hostTemperatureScale,
+ High: optionalValueReadFromFile(basepath+"_max") / hostTemperatureScale,
+ Critical: optionalValueReadFromFile(basepath+"_crit") / hostTemperatureScale,
+ })
+ }
+
+ return temperatures, warns.Reference()
+}
+
+func optionalValueReadFromFile(filename string) float64 {
+ var raw []byte
+
+ var err error
+
+ var value float64
+
+ // Check if file exists
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
+ return 0
+ }
+
+ if raw, err = os.ReadFile(filename); err != nil {
+ return 0
+ }
+
+ if value, err = strconv.ParseFloat(strings.TrimSpace(string(raw)), 64); err != nil {
+ return 0
+ }
+
+ return value
+}
diff --git a/sensors/sensors_netbsd.go b/sensors/sensors_netbsd.go
new file mode 100644
index 000000000..f2e2f7f2a
--- /dev/null
+++ b/sensors/sensors_netbsd.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build netbsd
+
+package sensors
+
+import (
+ "context"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ return []TemperatureStat{}, common.ErrNotImplementedError
+}
diff --git a/sensors/sensors_openbsd.go b/sensors/sensors_openbsd.go
new file mode 100644
index 000000000..8ae3198e0
--- /dev/null
+++ b/sensors/sensors_openbsd.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build openbsd
+
+package openbsd
+
+import (
+ "context"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ return []TemperatureStat{}, common.ErrNotImplementedError
+}
diff --git a/sensors/sensors_solaris.go b/sensors/sensors_solaris.go
new file mode 100644
index 000000000..c5d1bce8d
--- /dev/null
+++ b/sensors/sensors_solaris.go
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build solaris
+
+package sensors
+
+import (
+ "context"
+ "encoding/csv"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ var ret []TemperatureStat
+
+ out, err := invoke.CommandWithContext(ctx, "ipmitool", "-c", "sdr", "list")
+ if err != nil {
+ return ret, err
+ }
+
+ r := csv.NewReader(strings.NewReader(string(out)))
+ // Output may contain errors, e.g. "bmc_send_cmd: Permission denied", don't expect a consistent number of records
+ r.FieldsPerRecord = -1
+ for {
+ record, err := r.Read()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return ret, err
+ }
+ // CPU1 Temp,40,degrees C,ok
+ if len(record) < 3 || record[1] == "" || record[2] != "degrees C" {
+ continue
+ }
+ v, err := strconv.ParseFloat(record[1], 64)
+ if err != nil {
+ return ret, err
+ }
+ ts := TemperatureStat{
+ SensorKey: strings.TrimSuffix(record[0], " Temp"),
+ Temperature: v,
+ }
+ ret = append(ret, ts)
+ }
+
+ return ret, nil
+}
diff --git a/sensors/sensors_test.go b/sensors/sensors_test.go
new file mode 100644
index 000000000..454316a9c
--- /dev/null
+++ b/sensors/sensors_test.go
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: BSD-3-Clause
+
+package sensors
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestTemperatureStat_String(t *testing.T) {
+ v := TemperatureStat{
+ SensorKey: "CPU",
+ Temperature: 1.1,
+ High: 30.1,
+ Critical: 0.1,
+ }
+ s := `{"sensorKey":"CPU","temperature":1.1,"sensorHigh":30.1,"sensorCritical":0.1}`
+ if s != fmt.Sprintf("%v", v) {
+ t.Errorf("TemperatureStat string is invalid, %v", fmt.Sprintf("%v", v))
+ }
+}
diff --git a/sensors/sensors_windows.go b/sensors/sensors_windows.go
new file mode 100644
index 000000000..32b9ee492
--- /dev/null
+++ b/sensors/sensors_windows.go
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//go:build windows
+
+package sensors
+
+import (
+ "context"
+ "math"
+
+ "github.com/shirou/gopsutil/v4/internal/common"
+ "github.com/yusufpapurcu/wmi"
+)
+
+type msAcpi_ThermalZoneTemperature struct {
+ Active bool
+ CriticalTripPoint uint32
+ CurrentTemperature uint32
+ InstanceName string
+}
+
+func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
+ var ret []TemperatureStat
+ var dst []msAcpi_ThermalZoneTemperature
+ q := wmi.CreateQuery(&dst, "")
+ if err := common.WMIQueryWithContext(ctx, q, &dst, nil, "root/wmi"); err != nil {
+ return ret, err
+ }
+
+ for _, v := range dst {
+ ts := TemperatureStat{
+ SensorKey: v.InstanceName,
+ Temperature: kelvinToCelsius(v.CurrentTemperature, 2),
+ }
+ ret = append(ret, ts)
+ }
+
+ return ret, nil
+}
+
+func kelvinToCelsius(temp uint32, n int) float64 {
+ // wmi return temperature Kelvin * 10, so need to divide the result by 10,
+ // and then minus 273.15 to get °Celsius.
+ t := float64(temp/10) - 273.15
+ n10 := math.Pow10(n)
+ return math.Trunc((t+0.5/n10)*n10) / n10
+}
diff --git a/host/smc_darwin.c b/sensors/smc_darwin.c
similarity index 100%
rename from host/smc_darwin.c
rename to sensors/smc_darwin.c
diff --git a/host/smc_darwin.h b/sensors/smc_darwin.h
similarity index 100%
rename from host/smc_darwin.h
rename to sensors/smc_darwin.h