From c6f7fad4945f73811d65b79924fb787a2f60c050 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 17 Feb 2024 11:31:22 +0000 Subject: [PATCH] [sensors] move Temperatures from host to sensors. --- host/host.go | 16 -- host/host_fallback.go | 4 - host/host_freebsd.go | 4 - host/host_linux.go | 148 ---------------- host/host_netbsd.go | 4 - host/host_openbsd.go | 4 - host/host_solaris.go | 41 +---- host/host_test.go | 13 -- host/host_windows.go | 36 ---- sensors/sensors.go | 30 ++++ .../sensors_darwin_cgo.go | 4 +- .../sensors_darwin_nocgo.go | 4 +- sensors/sensors_fallback.go | 14 ++ sensors/sensors_freebsd.go | 14 ++ sensors/sensors_linux.go | 163 ++++++++++++++++++ sensors/sensors_netbsd.go | 14 ++ sensors/sensors_openbsd.go | 14 ++ sensors/sensors_solaris.go | 49 ++++++ sensors/sensors_test.go | 21 +++ sensors/sensors_windows.go | 46 +++++ {host => sensors}/smc_darwin.c | 0 {host => sensors}/smc_darwin.h | 0 22 files changed, 371 insertions(+), 272 deletions(-) create mode 100644 sensors/sensors.go rename host/host_darwin_cgo.go => sensors/sensors_darwin_cgo.go (90%) rename host/host_darwin_nocgo.go => sensors/sensors_darwin_nocgo.go (67%) create mode 100644 sensors/sensors_fallback.go create mode 100644 sensors/sensors_freebsd.go create mode 100644 sensors/sensors_linux.go create mode 100644 sensors/sensors_netbsd.go create mode 100644 sensors/sensors_openbsd.go create mode 100644 sensors/sensors_solaris.go create mode 100644 sensors/sensors_test.go create mode 100644 sensors/sensors_windows.go rename {host => sensors}/smc_darwin.c (100%) rename {host => sensors}/smc_darwin.h (100%) 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