pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/system/sensors/sensors_linux.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  // Package sensors provide methods for collecting sensors information
     5  package sensors
     6  
     7  // ////////////////////////////////////////////////////////////////////////////////// //
     8  //                                                                                    //
     9  //                         Copyright (c) 2022 ESSENTIAL KAOS                          //
    10  //      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
    11  //                                                                                    //
    12  // ////////////////////////////////////////////////////////////////////////////////// //
    13  
    14  import (
    15  	"fmt"
    16  	"io/ioutil"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"pkg.re/essentialkaos/ek.v12/fsutil"
    21  	"pkg.re/essentialkaos/ek.v12/sortutil"
    22  )
    23  
    24  // ////////////////////////////////////////////////////////////////////////////////// //
    25  
    26  // Device contains info from different device sensors
    27  type Device struct {
    28  	Name        string
    29  	TempSensors []TempSensor
    30  }
    31  
    32  // TempSensor contains info from temperature sensor
    33  type TempSensor struct {
    34  	Name string
    35  	Cur  float64
    36  	Min  float64
    37  	Max  float64
    38  	Crit float64
    39  }
    40  
    41  // ////////////////////////////////////////////////////////////////////////////////// //
    42  
    43  var hwmonDir = "/sys/class/hwmon"
    44  
    45  // ////////////////////////////////////////////////////////////////////////////////// //
    46  
    47  // Collect collects sensors information
    48  // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
    49  func Collect() ([]*Device, error) {
    50  	if !fsutil.CheckPerms("DR", hwmonDir) {
    51  		return nil, fmt.Errorf("Can't read sensors information")
    52  	}
    53  
    54  	var result []*Device
    55  
    56  	deviceDir := fsutil.List(hwmonDir, false)
    57  
    58  	sortutil.StringsNatural(deviceDir)
    59  
    60  	for _, deviceDir := range deviceDir {
    61  		if !hasSensorsData(hwmonDir + "/" + deviceDir) {
    62  			continue
    63  		}
    64  
    65  		device, err := collectDeviceInfo(hwmonDir + "/" + deviceDir)
    66  
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  
    71  		if device != nil {
    72  			result = append(result, device)
    73  		}
    74  	}
    75  
    76  	return result, nil
    77  }
    78  
    79  // ////////////////////////////////////////////////////////////////////////////////// //
    80  
    81  // Temperature returns min, max and average temperature
    82  func (d *Device) Temperature() (float64, float64, float64) {
    83  	if len(d.TempSensors) == 0 {
    84  		return 0.0, 0.0, 0.0
    85  	}
    86  
    87  	min, max, tot := 100.0, 0.0, 0.0
    88  
    89  	for _, v := range d.TempSensors {
    90  		if v.Cur < min {
    91  			min = v.Cur
    92  		}
    93  
    94  		if v.Cur > max {
    95  			max = v.Cur
    96  		}
    97  
    98  		tot += v.Cur
    99  	}
   100  
   101  	return min, max, tot / float64(len(d.TempSensors))
   102  }
   103  
   104  // String formats sensor data as a string
   105  func (s TempSensor) String() string {
   106  	return fmt.Sprintf(
   107  		"{Name:%s Cur:+%g°C Min:+%g°C Max:+%g°C Crit:+%g°C}",
   108  		s.Name, s.Cur, s.Min, s.Max, s.Crit,
   109  	)
   110  }
   111  
   112  // ////////////////////////////////////////////////////////////////////////////////// //
   113  
   114  func hasSensorsData(dir string) bool {
   115  	switch {
   116  	case hasTempSensorsData(dir):
   117  		return true
   118  	}
   119  
   120  	return false
   121  }
   122  
   123  func hasTempSensorsData(dir string) bool {
   124  	return fsutil.IsExist(dir + "/temp1_input")
   125  }
   126  
   127  func collectDeviceInfo(dir string) (*Device, error) {
   128  	var err error
   129  
   130  	device := &Device{}
   131  
   132  	device.Name, err = readSensorLabel(dir + "/name")
   133  
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	if hasTempSensorsData(dir) {
   139  		device.TempSensors, err = collectTempSensorsInfo(dir)
   140  
   141  		if err != nil {
   142  			return nil, err
   143  		}
   144  	}
   145  
   146  	return device, nil
   147  }
   148  
   149  func collectTempSensorsInfo(dir string) ([]TempSensor, error) {
   150  	var result []TempSensor
   151  
   152  	for i := 1; i < 128; i++ {
   153  		filePrefix := dir + "/temp" + strconv.Itoa(i)
   154  
   155  		if !fsutil.IsExist(filePrefix + "_input") {
   156  			break
   157  		}
   158  
   159  		sensor, err := readTempSensors(filePrefix)
   160  
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  
   165  		result = append(result, sensor)
   166  	}
   167  
   168  	return result, nil
   169  }
   170  
   171  func readTempSensors(filePrefix string) (TempSensor, error) {
   172  	var err error
   173  
   174  	sensor := TempSensor{}
   175  
   176  	if fsutil.IsExist(filePrefix + "_label") {
   177  		sensor.Name, err = readSensorLabel(filePrefix + "_label")
   178  
   179  		if err != nil {
   180  			return TempSensor{}, err
   181  		}
   182  	}
   183  
   184  	sensor.Cur, err = readTempSensorValue(filePrefix + "_input")
   185  
   186  	if err != nil {
   187  		return TempSensor{}, err
   188  	}
   189  
   190  	sensor.Min, err = readTempSensorValue(filePrefix + "_min")
   191  
   192  	if err != nil {
   193  		return TempSensor{}, err
   194  	}
   195  
   196  	sensor.Max, err = readTempSensorValue(filePrefix + "_max")
   197  
   198  	if err != nil {
   199  		return TempSensor{}, err
   200  	}
   201  
   202  	sensor.Crit, err = readTempSensorValue(filePrefix + "_crit")
   203  
   204  	if err != nil {
   205  		return TempSensor{}, err
   206  	}
   207  
   208  	return sensor, nil
   209  }
   210  
   211  func readSensorLabel(file string) (string, error) {
   212  	data, err := ioutil.ReadFile(file)
   213  
   214  	if err != nil {
   215  		return "", fmt.Errorf("Can't read data from %s: %w", file, err)
   216  	}
   217  
   218  	return strings.Trim(string(data), "\r\n"), nil
   219  }
   220  
   221  func readTempSensorValue(file string) (float64, error) {
   222  	if !fsutil.IsExist(file) {
   223  		return 0.0, nil
   224  	}
   225  
   226  	data, err := ioutil.ReadFile(file)
   227  
   228  	if err != nil {
   229  		return 0.0, fmt.Errorf("Can't read sensor data from %s: %w", file, err)
   230  	}
   231  
   232  	value, err := strconv.ParseFloat(strings.Trim(string(data), "\r\n"), 64)
   233  
   234  	if err != nil {
   235  		return 0.0, fmt.Errorf("Can't parse sensor data from %s: %w", file, err)
   236  	}
   237  
   238  	return value / 1000.0, nil
   239  }