@cryptotaxi247 / netdata-1 / commits / 01ce883ea

vendor https://github.com/mdlayher/lmsensors (#18427)

Ilya Mashchenko committed Aug 28, 2024 at 12:59 UTC 01ce883ea62aba5f019cc3bcf9df97dceab1ad56
13 files changed +1543 -172
REDISTRIBUTED.md
+34 -172
@@ -23,176 +23,38 @@ We have decided to redistribute all these, instead of using them
23 through a CDN, to allow Netdata to work in cases where Internet
24 connectivity is not available.
25
26 -- [Dygraphs](http://dygraphs.com/)
27 -
28 - Copyright 2009, Dan Vanderkam
29 - [MIT License](http://dygraphs.com/legal.html)
30 -
31 -- [Easy Pie Chart](https://rendro.github.io/easy-pie-chart/)
32 -
33 - Copyright 2013, Robert Fleischmann
34 - [MIT License](https://github.com/rendro/easy-pie-chart/blob/master/LICENSE)
35 -
36 -- [Gauge.js](http://bernii.github.io/gauge.js/)
37 -
38 - Copyright, Bernard Kobos
39 - [MIT License](https://github.com/getgauge/gauge-js/blob/master/LICENSE)
40 -
41 -- [d3pie](https://github.com/benkeen/d3pie)
42 -
43 - Copyright (c) 2014-2015 Benjamin Keen
44 - [MIT License](https://github.com/benkeen/d3pie/blob/master/LICENSE)
45 -
46 -- [jQuery Sparklines](http://omnipotent.net/jquery.sparkline/)
47 -
48 - Copyright 2009-2012, Splunk Inc.
49 - [New BSD License](http://opensource.org/licenses/BSD-3-Clause)
50 -
51 -- [Peity](http://benpickles.github.io/peity/)
52 -
53 - Copyright 2009-2015, Ben Pickles
54 - [MIT License](https://github.com/benpickles/peity/blob/master/LICENCE)
55 -
56 -- [morris.js](http://morrisjs.github.io/morris.js/)
57 -
58 - Copyright 2013, Olly Smith
59 - [Simplified BSD License](http://morrisjs.github.io/morris.js/)
60 -
61 -- [Raphaël](http://dmitrybaranovskiy.github.io/raphael/)
62 -
63 - Copyright 2008, Dmitry Baranovskiy
64 - [MIT License](http://dmitrybaranovskiy.github.io/raphael/license.html)
65 -
66 -- [C3](http://c3js.org/)
67 -
68 - Copyright 2013, Masayuki Tanaka
69 - [MIT License](https://github.com/masayuki0812/c3/blob/master/LICENSE)
70 -
71 -- [D3](http://d3js.org/)
72 -
73 - Copyright 2015, Mike Bostock
74 - [BSD License](http://opensource.org/licenses/BSD-3-Clause)
75 -
76 -- [jQuery](https://jquery.org/)
77 -
78 - Copyright 2015, jQuery Foundation
79 - [MIT License](https://jquery.org/license/)
80 -
81 -- [Bootstrap](http://getbootstrap.com/getting-started/)
82 -
83 - Copyright 2015, Twitter
84 - [MIT License](https://github.com/twbs/bootstrap/blob/v4-dev/LICENSE)
85 -
86 -- [Bootstrap Toggle](http://www.bootstraptoggle.com/)
87 -
88 - Copyright (c) 2011-2014 Min Hur, The New York Times Company
89 - [MIT License](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE)
90 -
91 -- [Bootstrap-slider](http://seiyria.com/bootstrap-slider/)
92 -
93 - Copyright 2017 Kyle Kemp, Rohit Kalkur, and contributors
94 - [MIT License](https://github.com/seiyria/bootstrap-slider/blob/master/LICENSE.md)
95 -
96 -- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/)
97 -
98 - Copyright (c) 2012-2016 Zhixin Wen [wenzhixin2010@gmail.com](mailto:wenzhixin2010@gmail.com)
99 - [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE)
100 -
101 -- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin)
102 -
103 - Copyright (c) 2015,2016 hhurz
104 - [MIT License](https://github.com/hhurz/tableExport.jquery.plugin/blob/master/LICENSE)
105 -
106 -- [perfect-scrollbar](https://jamesflorentino.github.io/nanoScrollerJS/)
107 -
108 - Copyright 2016, Hyunje Alex Jun and other contributors
109 - [MIT License](https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE)
110 -
111 -- [FontAwesome](https://github.com/FortAwesome/Font-Awesome)
112 -
113 - Created by Dave Gandy
114 - Font license: [SIL OFL 1.1](http://scripts.sil.org/OFL)
115 - Icon license [Creative Commons Attribution 4.0 (CC-BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
116 - Code license: [MIT License](http://opensource.org/licenses/mit-license.html)
117 -
118 -- [node-extend](https://github.com/justmoon/node-extend)
119 -
120 - Copyright 2014, Stefan Thomas
121 - [MIT License](https://github.com/justmoon/node-extend/blob/master/LICENSE)
122 -
123 -- [node-net-snmp](https://github.com/stephenwvickers/node-net-snmp)
124 -
125 - Copyright 2013, Stephen Vickers
126 - [MIT License](https://github.com/nospaceships/node-net-snmp#license)
127 -
128 -- [node-asn1-ber](https://github.com/stephenwvickers/node-asn1-ber)
129 -
130 - Copyright 2017, Stephen Vickers
131 - Copyright 2011, Mark Cavage
132 - [MIT License](https://github.com/nospaceships/node-asn1-ber#license)
133 -
134 -- [pixl-xml](https://github.com/jhuckaby/pixl-xml)
135 -
136 - Copyright 2015, Joseph Huckaby
137 - [MIT License](https://github.com/jhuckaby/pixl-xml#license)
138 -
139 -- [sensors](https://github.com/paroj/sensors.py)
140 -
141 - Copyright 2014, Pavel Rojtberg
142 - [LGPL 2.1 License](http://opensource.org/licenses/LGPL-2.1)
143 -
144 -- [PyYAML](https://pypi.org/project/PyYAML/)
145 -
146 - Copyright 2006, Kirill Simonov
147 - [MIT License](https://github.com/yaml/pyyaml/blob/master/LICENSE)
148 -
149 -- [urllib3](https://github.com/shazow/urllib3)
150 -
151 - Copyright 2008-2016 Andrey Petrov and [contributors](https://github.com/shazow/urllib3/blob/master/CONTRIBUTORS.txt)
152 - [MIT License](https://github.com/shazow/urllib3/blob/master/LICENSE.txt)
153 -
154 -- [lz-string](http://pieroxy.net/blog/pages/lz-string/index.html)
155 -
156 - Copyright 2013 Pieroxy
157 - [WTFPL License](http://pieroxy.net/blog/pages/lz-string/index.html#inline_menu_10)
158 -
159 -- [pako](http://nodeca.github.io/pako/)
160 -
161 - Copyright 2014-2017 Vitaly Puzrin and Andrei Tuputcyn
162 - [MIT License](https://github.com/nodeca/pako/blob/master/LICENSE)
163 -
164 -- [clipboard-polyfill](https://github.com/lgarron/clipboard-polyfill)
165 -
166 - Copyright (c) 2014 Lucas Garron
167 - [MIT License](https://github.com/lgarron/clipboard-polyfill/blob/master/LICENSE.md)
168 -
169 -- [Utilities for writing code that runs on Python 2 and 3](https://raw.githubusercontent.com/netdata/netdata/master/src/collectors/python.d.plugin/python_modules/urllib3/packages/six.py)
170 -
171 - Copyright (c) 2010-2015 Benjamin Peterson
172 - [MIT License](https://github.com/benjaminp/six/blob/master/LICENSE)
173 -
174 -- [mcrcon](https://github.com/barneygale/MCRcon)
175 -
176 - Copyright (C) 2015 Barnaby Gale
177 - [MIT License](https://raw.githubusercontent.com/barneygale/MCRcon/master/COPYING.txt)
178 -
179 -- [monotonic](https://github.com/atdt/monotonic)
180 -
181 - Copyright 2014, 2015, 2016 Ori Livneh [ori@wikimedia.org](mailto:ori@wikimedia.org)
182 - [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)
183 -
184 -- [filelock](https://github.com/benediktschmitt/py-filelock)
185 -
186 - Copyright 2015, Benedikt Schmitt [Unlicense License](https://unlicense.org/)
187 -
188 -- [Kolmogorov-Smirnov distribution](http://simul.iro.umontreal.ca/ksdir/)
189 -
190 - Copyright March 2010 by Université de Montréal, Richard Simard and Pierre L'Ecuyer
191 - [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)
192 -
193 -- [xxHash](https://github.com/Cyan4973/xxHash)
194 -
195 - Copyright (c) 2012-2021 Yann Collet
196 - [BSD](https://github.com/Cyan4973/xxHash/blob/dev/LICENSE)
197 -
26
27 +| Name | Copyright | License |
28 +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
29 +| [Dygraphs](http://dygraphs.com/) | Copyright 2009, Dan Vanderkam | [MIT](http://dygraphs.com/legal.html) |
30 +| [Easy Pie Chart](https://rendro.github.io/easy-pie-chart/) | Copyright 2013, Robert Fleischmann | [MIT](https://github.com/rendro/easy-pie-chart/blob/master/LICENSE) |
31 +| [Gauge.js](http://bernii.github.io/gauge.js/) | Copyright, Bernard Kobos | [MIT](https://github.com/getgauge/gauge-js/blob/master/LICENSE) |
32 +| [d3pie](https://github.com/benkeen/d3pie) | Copyright (c) 2014-2015 Benjamin Keen | [MIT](https://github.com/benkeen/d3pie/blob/master/LICENSE) |
33 +| [jQuery Sparklines](http://omnipotent.net/jquery.sparkline/) | Copyright 2009-2012, Splunk Inc. | [New BSD](http://opensource.org/licenses/BSD-3-Clause) |
34 +| [Peity](http://benpickles.github.io/peity/) | Copyright 2009-2015, Ben Pickles | [MIT](https://github.com/benpickles/peity/blob/master/LICENCE) |
35 +| [morris.js](http://morrisjs.github.io/morris.js/) | Copyright 2013, Olly Smith | [Simplified BSD](http://morrisjs.github.io/morris.js/) |
36 +| [Raphaël](http://dmitrybaranovskiy.github.io/raphael/) | Copyright 2008, Dmitry Baranovskiy | [MIT](http://dmitrybaranovskiy.github.io/raphael/license.html) |
37 +| [C3](http://c3js.org/) | Copyright 2013, Masayuki Tanaka | [MIT](https://github.com/masayuki0812/c3/blob/master/LICENSE) |
38 +| [D3](http://d3js.org/) | Copyright 2015, Mike Bostock | [BSD](http://opensource.org/licenses/BSD-3-Clause) |
39 +| [jQuery](https://jquery.org/) | Copyright 2015, jQuery Foundation | [MIT](https://jquery.org/license/) |
40 +| [Bootstrap](http://getbootstrap.com/getting-started/) | Copyright 2015, Twitter | [MIT](https://github.com/twbs/bootstrap/blob/v4-dev/LICENSE) |
41 +| [Bootstrap Toggle](http://www.bootstraptoggle.com/) | Copyright (c) 2011-2014 Min Hur, The New York Times Company | [MIT](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE) |
42 +| [Bootstrap-slider](http://seiyria.com/bootstrap-slider/) | Copyright 2017 Kyle Kemp, Rohit Kalkur, and contributors | [MIT](https://github.com/seiyria/bootstrap-slider/blob/master/LICENSE.md) |
43 +| [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/) | Copyright (c) 2012-2016 Zhixin Wen | [MIT](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE) |
44 +| [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin) | Copyright (c) 2015,2016 hhurz | [MIT](https://github.com/hhurz/tableExport.jquery.plugin/blob/master/LICENSE) |
45 +| [perfect-scrollbar](https://jamesflorentino.github.io/nanoScrollerJS/) | Copyright 2016, Hyunje Alex Jun and other contributors | [MIT](https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE) |
46 +| [FontAwesome](https://github.com/FortAwesome/Font-Awesome) | Created by Dave Gandy | Font: [SIL OFL 1.1](http://scripts.sil.org/OFL), Icon: [Creative Commons Attribution 4.0 (CC-BY 4.0)](https://creativecommons.org/licenses/by/4.0/), Code: [MIT](http://opensource.org/licenses/mit-license.html) |
47 +| [node-extend](https://github.com/justmoon/node-extend) | Copyright 2014, Stefan Thomas | [MIT](https://github.com/justmoon/node-extend/blob/master/LICENSE) |
48 +| [pixl-xml](https://github.com/jhuckaby/pixl-xml) | Copyright 2015, Joseph Huckaby | [MIT](https://github.com/jhuckaby/pixl-xml#license) |
49 +| [PyYAML](https://pypi.org/project/PyYAML/) | Copyright 2006, Kirill Simonov | [MIT](https://github.com/yaml/pyyaml/blob/master/LICENSE) |
50 +| [urllib3](https://github.com/shazow/urllib3) | Copyright 2008-2016 Andrey Petrov and contributors | [MIT](https://github.com/shazow/urllib3/blob/master/LICENSE.txt) |
51 +| [lz-string](http://pieroxy.net/blog/pages/lz-string/index.html) | Copyright 2013 Pieroxy | [WTFPL](http://pieroxy.net/blog/pages/lz-string/index.html#inline_menu_10) |
52 +| [pako](http://nodeca.github.io/pako/) | Copyright 2014-2017 Vitaly Puzrin and Andrei Tuputcyn | [MIT](https://github.com/nodeca/pako/blob/master/LICENSE) |
53 +| [clipboard-polyfill](https://github.com/lgarron/clipboard-polyfill) | Copyright (c) 2014 Lucas Garron | [MIT](https://github.com/lgarron/clipboard-polyfill/blob/master/LICENSE.md) |
54 +| [Utilities for writing code that runs on Python 2 and 3](https://raw.githubusercontent.com/netdata/netdata/master/src/collectors/python.d.plugin/python_modules/urllib3/packages/six.py) | Copyright (c) 2010-2015 Benjamin Peterson | [MIT](https://github.com/benjaminp/six/blob/master/LICENSE) |
55 +| [mcrcon](https://github.com/barneygale/MCRcon) | Copyright (C) 2015 Barnaby Gale | [MIT](https://raw.githubusercontent.com/barneygale/MCRcon/master/COPYING.txt) |
56 +| [monotonic](https://github.com/atdt/monotonic) | Copyright 2014, 2015, 2016 Ori Livneh | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) |
57 +| [filelock](https://github.com/benediktschmitt/py-filelock) | Copyright 2015, Benedikt Schmitt | [Unlicense](https://unlicense.org/) |
58 +| [Kolmogorov-Smirnov distribution](http://simul.iro.umontreal.ca/ksdir/) | Copyright March 2010 by Université de Montréal, Richard Simard and Pierre L'Ecuyer | [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.en.html) |
59 +| [xxHash](https://github.com/Cyan4973/xxHash) | Copyright (c) 2012-2021 Yann Collet | [BSD](https://github.com/Cyan4973/xxHash/blob/dev/LICENSE) |
60 +| [lmsensors](https://github.com/mdlayher/lmsensors) | Copyright 2016, Matt Layher | [MIT](https://github.com/mdlayher/lmsensors/blob/master/LICENSE.md) |
src/go/plugin/go.d/modules/sensors/lmsensors/LICENSE.md new
+10
@@ -0,0 +1,10 @@
1 +MIT License
2 +===========
3 +
4 +Copyright (C) 2016 Matt Layher
5 +
6 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 +
8 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 +
10 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
src/go/plugin/go.d/modules/sensors/lmsensors/README.md new
+5
@@ -0,0 +1,5 @@
1 +lmsensors [![Build Status](https://travis-ci.org/mdlayher/lmsensors.svg?branch=master)](https://travis-ci.org/mdlayher/lmsensors) [![GoDoc](http://godoc.org/github.com/mdlayher/lmsensors?status.svg)](http://godoc.org/github.com/mdlayher/lmsensors) [![Report Card](https://goreportcard.com/badge/github.com/mdlayher/lmsensors)](https://goreportcard.com/report/github.com/mdlayher/lmsensors)
2 +=========
3 +
4 +Package `lmsensors` provides access to Linux monitoring sensors data, such
5 +as temperatures, voltage, and fan speeds. MIT Licensed.
src/go/plugin/go.d/modules/sensors/lmsensors/currentsensor.go new
+62
@@ -0,0 +1,62 @@
1 +package lmsensors
2 +
3 +import (
4 + "strconv"
5 +)
6 +
7 +var _ Sensor = &CurrentSensor{}
8 +
9 +// A CurrentSensor is a Sensor that detects current in Amperes.
10 +type CurrentSensor struct {
11 + // The name of the sensor.
12 + Name string
13 +
14 + // A label that describes what the sensor is monitoring. Label may be
15 + // empty.
16 + Label string
17 +
18 + // Whether or not the sensor has an alarm triggered.
19 + Alarm bool
20 +
21 + // The input current, in Amperes, indicated by the sensor.
22 + Input float64
23 +
24 + // The maximum current threshold, in Amperes, indicated by the sensor.
25 + Maximum float64
26 +
27 + // The critical current threshold, in Amperes, indicated by the sensor.
28 + Critical float64
29 +}
30 +
31 +func (s *CurrentSensor) name() string { return s.Name }
32 +func (s *CurrentSensor) setName(name string) { s.Name = name }
33 +
34 +func (s *CurrentSensor) parse(raw map[string]string) error {
35 + for k, v := range raw {
36 + switch k {
37 + case "crit", "input", "max":
38 + f, err := strconv.ParseFloat(v, 64)
39 + if err != nil {
40 + return err
41 + }
42 +
43 + // Raw current values are scaled by 1000
44 + f /= 1000
45 +
46 + switch k {
47 + case "crit":
48 + s.Critical = f
49 + case "input":
50 + s.Input = f
51 + case "max":
52 + s.Maximum = f
53 + }
54 + case "alarm":
55 + s.Alarm = v != "0"
56 + case "label":
57 + s.Label = v
58 + }
59 + }
60 +
61 + return nil
62 +}
src/go/plugin/go.d/modules/sensors/lmsensors/doc.go new
+3
@@ -0,0 +1,3 @@
1 +// Package lmsensors provides access to Linux monitoring sensors data, such
2 +// as temperatures, voltage, and fan speeds.
3 +package lmsensors
src/go/plugin/go.d/modules/sensors/lmsensors/fansensor.go new
+55
@@ -0,0 +1,55 @@
1 +package lmsensors
2 +
3 +import (
4 + "strconv"
5 +)
6 +
7 +var _ Sensor = &FanSensor{}
8 +
9 +// A FanSensor is a Sensor that detects fan speeds in rotations per minute.
10 +type FanSensor struct {
11 + // The name of the sensor.
12 + Name string
13 +
14 + // Whether or not the fan speed is below the minimum threshold.
15 + Alarm bool
16 +
17 + // Whether or not the fan will sound an audible alarm when fan speed is
18 + // below the minimum threshold.
19 + Beep bool
20 +
21 + // The input fan speed, in rotations per minute, indicated by the sensor.
22 + Input int
23 +
24 + // The low threshold fan speed, in rotations per minute, indicated by the
25 + // sensor.
26 + Minimum int
27 +}
28 +
29 +func (s *FanSensor) name() string { return s.Name }
30 +func (s *FanSensor) setName(name string) { s.Name = name }
31 +
32 +func (s *FanSensor) parse(raw map[string]string) error {
33 + for k, v := range raw {
34 + switch k {
35 + case "input", "min":
36 + i, err := strconv.Atoi(v)
37 + if err != nil {
38 + return err
39 + }
40 +
41 + switch k {
42 + case "input":
43 + s.Input = i
44 + case "min":
45 + s.Minimum = i
46 + }
47 + case "alarm":
48 + s.Alarm = v != "0"
49 + case "beep":
50 + s.Beep = v != "0"
51 + }
52 + }
53 +
54 + return nil
55 +}
src/go/plugin/go.d/modules/sensors/lmsensors/intrusionsensor.go new
+28
@@ -0,0 +1,28 @@
1 +package lmsensors
2 +
3 +var _ Sensor = &IntrusionSensor{}
4 +
5 +// An IntrusionSensor is a Sensor that detects when the machine's chassis
6 +// has been opened.
7 +type IntrusionSensor struct {
8 + // The name of the sensor.
9 + Name string
10 +
11 + // Whether or not the machine's chassis has been opened, and the alarm
12 + // has been triggered.
13 + Alarm bool
14 +}
15 +
16 +func (s *IntrusionSensor) name() string { return s.Name }
17 +func (s *IntrusionSensor) setName(name string) { s.Name = name }
18 +
19 +func (s *IntrusionSensor) parse(raw map[string]string) error {
20 + for k, v := range raw {
21 + switch k {
22 + case "alarm":
23 + s.Alarm = v != "0"
24 + }
25 + }
26 +
27 + return nil
28 +}
src/go/plugin/go.d/modules/sensors/lmsensors/powersensor.go new
+72
@@ -0,0 +1,72 @@
1 +package lmsensors
2 +
3 +import (
4 + "strconv"
5 + "time"
6 +)
7 +
8 +var _ Sensor = &PowerSensor{}
9 +
10 +// A PowerSensor is a Sensor that detects average electrical power consumption
11 +// in watts.
12 +type PowerSensor struct {
13 + // The name of the sensor.
14 + Name string
15 +
16 + // The average electrical power consumption, in watts, indicated
17 + // by the sensor.
18 + Average float64
19 +
20 + // The interval of time over which the average electrical power consumption
21 + // is collected.
22 + AverageInterval time.Duration
23 +
24 + // Whether or not this sensor has a battery.
25 + Battery bool
26 +
27 + // The model number of the sensor.
28 + ModelNumber string
29 +
30 + // Miscellaneous OEM information about the sensor.
31 + OEMInfo string
32 +
33 + // The serial number of the sensor.
34 + SerialNumber string
35 +}
36 +
37 +func (s *PowerSensor) name() string { return s.Name }
38 +func (s *PowerSensor) setName(name string) { s.Name = name }
39 +
40 +func (s *PowerSensor) parse(raw map[string]string) error {
41 + for k, v := range raw {
42 + switch k {
43 + case "average":
44 + f, err := strconv.ParseFloat(v, 64)
45 + if err != nil {
46 + return err
47 + }
48 +
49 + // Raw temperature values are scaled by one million
50 + f /= 1000000
51 + s.Average = f
52 + case "average_interval":
53 + // Time values in milliseconds
54 + d, err := time.ParseDuration(v + "ms")
55 + if err != nil {
56 + return err
57 + }
58 +
59 + s.AverageInterval = d
60 + case "is_battery":
61 + s.Battery = v != "0"
62 + case "model_number":
63 + s.ModelNumber = v
64 + case "oem_info":
65 + s.OEMInfo = v
66 + case "serial_number":
67 + s.SerialNumber = v
68 + }
69 + }
70 +
71 + return nil
72 +}
src/go/plugin/go.d/modules/sensors/lmsensors/scanner.go new
+232
@@ -0,0 +1,232 @@
1 +package lmsensors
2 +
3 +import (
4 + "fmt"
5 + "io/ioutil"
6 + "os"
7 + "path/filepath"
8 + "strings"
9 +)
10 +
11 +// A filesystem is an interface to a filesystem, used for testing.
12 +type filesystem interface {
13 + ReadFile(filename string) (string, error)
14 + Readlink(name string) (string, error)
15 + Stat(name string) (os.FileInfo, error)
16 + Walk(root string, walkFn filepath.WalkFunc) error
17 +}
18 +
19 +// A Scanner scans for Devices, so data can be read from their Sensors.
20 +type Scanner struct {
21 + fs filesystem
22 +}
23 +
24 +// New creates a new Scanner.
25 +func New() *Scanner {
26 + return &Scanner{
27 + fs: &systemFilesystem{},
28 + }
29 +}
30 +
31 +// Scan scans for Devices and their Sensors.
32 +func (s *Scanner) Scan() ([]*Device, error) {
33 + // Determine common device locations in Linux /sys filesystem.
34 + paths, err := s.detectDevicePaths()
35 + if err != nil {
36 + return nil, err
37 + }
38 +
39 + var devices []*Device
40 + for _, p := range paths {
41 + d := &Device{}
42 + raw := make(map[string]map[string]string, 0)
43 +
44 + // Walk filesystem paths to fetch devices and sensors
45 + err := s.fs.Walk(p, func(path string, info os.FileInfo, err error) error {
46 + if err != nil {
47 + return err
48 + }
49 +
50 + // Skip directories and anything that isn't a regular file
51 + if info.IsDir() || !info.Mode().IsRegular() {
52 + return nil
53 + }
54 +
55 + // Skip some files that can't be read or don't provide useful
56 + // sensor information
57 + file := filepath.Base(path)
58 + if shouldSkip(file) {
59 + return nil
60 + }
61 +
62 + s, err := s.fs.ReadFile(path)
63 + if err != nil {
64 + return nil
65 + }
66 +
67 + switch file {
68 + // Found name of device
69 + case "name":
70 + d.Name = s
71 + }
72 +
73 + // Sensor names in format "sensor#_foo", e.g. "temp1_input"
74 + fs := strings.SplitN(file, "_", 2)
75 + if len(fs) != 2 {
76 + return nil
77 + }
78 +
79 + // Gather sensor data into map for later processing
80 + if _, ok := raw[fs[0]]; !ok {
81 + raw[fs[0]] = make(map[string]string, 0)
82 + }
83 +
84 + raw[fs[0]][fs[1]] = s
85 + return nil
86 + })
87 + if err != nil {
88 + return nil, err
89 + }
90 +
91 + // Parse all possible sensors from raw data
92 + sensors, err := parseSensors(raw)
93 + if err != nil {
94 + return nil, err
95 + }
96 +
97 + d.Sensors = sensors
98 + devices = append(devices, d)
99 + }
100 +
101 + renameDevices(devices)
102 + return devices, nil
103 +}
104 +
105 +// renameDevices renames devices in place to prevent duplicate device names,
106 +// and to number each device.
107 +func renameDevices(devices []*Device) {
108 + nameCount := make(map[string]int, 0)
109 +
110 + for i := range devices {
111 + name := devices[i].Name
112 + devices[i].Name = fmt.Sprintf("%s-%02d",
113 + name,
114 + nameCount[name],
115 + )
116 + nameCount[name]++
117 + }
118 +}
119 +
120 +// detectDevicePaths performs a filesystem walk to paths where devices may
121 +// reside on Linux.
122 +func (s *Scanner) detectDevicePaths() ([]string, error) {
123 + const lookPath = "/sys/class/hwmon"
124 +
125 + var paths []string
126 + err := s.fs.Walk(lookPath, func(path string, info os.FileInfo, err error) error {
127 + if err != nil {
128 + return err
129 + }
130 +
131 + // Skip anything that isn't a symlink
132 + if info.Mode()&os.ModeSymlink == 0 {
133 + return nil
134 + }
135 +
136 + dest, err := s.fs.Readlink(path)
137 + if err != nil {
138 + return err
139 + }
140 + dest = filepath.Join(lookPath, filepath.Clean(dest))
141 +
142 + // Symlink destination has a file called name, meaning a sensor exists
143 + // here and data can be retrieved
144 + fi, err := s.fs.Stat(filepath.Join(dest, "name"))
145 + if err != nil && !os.IsNotExist(err) {
146 + return err
147 + }
148 + if err == nil && fi.Mode().IsRegular() {
149 + paths = append(paths, dest)
150 + return nil
151 + }
152 +
153 + // Symlink destination has another symlink called device, which can be
154 + // read and used to retrieve data
155 + device := filepath.Join(dest, "device")
156 + fi, err = s.fs.Stat(device)
157 + if err != nil {
158 + if os.IsNotExist(err) {
159 + return nil
160 + }
161 +
162 + return err
163 + }
164 +
165 + if fi.Mode()&os.ModeSymlink != 0 {
166 + return nil
167 + }
168 +
169 + device, err = s.fs.Readlink(device)
170 + if err != nil {
171 + return err
172 + }
173 + dest = filepath.Join(dest, filepath.Clean(device))
174 +
175 + // Symlink destination has a file called name, meaning a sensor exists
176 + // here and data can be retrieved
177 + if _, err := s.fs.Stat(filepath.Join(dest, "name")); err != nil {
178 + if os.IsNotExist(err) {
179 + return nil
180 + }
181 +
182 + return err
183 + }
184 +
185 + paths = append(paths, dest)
186 + return nil
187 + })
188 +
189 + return paths, err
190 +}
191 +
192 +// shouldSkip indicates if a given filename should be skipped during the
193 +// filesystem walk operation.
194 +func shouldSkip(file string) bool {
195 + if strings.HasPrefix(file, "runtime_") {
196 + return true
197 + }
198 +
199 + switch file {
200 + case "async":
201 + case "autosuspend_delay_ms":
202 + case "control":
203 + case "driver_override":
204 + case "modalias":
205 + case "uevent":
206 + default:
207 + return false
208 + }
209 +
210 + return true
211 +}
212 +
213 +var _ filesystem = &systemFilesystem{}
214 +
215 +// A systemFilesystem is a filesystem which uses operations on the host
216 +// filesystem.
217 +type systemFilesystem struct{}
218 +
219 +func (fs *systemFilesystem) ReadFile(filename string) (string, error) {
220 + b, err := ioutil.ReadFile(filename)
221 + if err != nil {
222 + return "", err
223 + }
224 +
225 + return strings.TrimSpace(string(b)), nil
226 +}
227 +
228 +func (fs *systemFilesystem) Readlink(name string) (string, error) { return os.Readlink(name) }
229 +func (fs *systemFilesystem) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
230 +func (fs *systemFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
231 + return filepath.Walk(root, walkFn)
232 +}
src/go/plugin/go.d/modules/sensors/lmsensors/scanner_test.go new
+810
@@ -0,0 +1,810 @@
1 +package lmsensors
2 +
3 +import (
4 + "fmt"
5 + "os"
6 + "path/filepath"
7 + "reflect"
8 + "strings"
9 + "testing"
10 + "time"
11 +)
12 +
13 +// TODO(mdlayher): why does scanning work if device file isn't a symlink,
14 +// even though it is in the actual filesystem (and the actual filesystem
15 +// exhibits the same behavior)?
16 +
17 +func TestScannerScan(t *testing.T) {
18 + tests := []struct {
19 + name string
20 + fs filesystem
21 + devices []*Device
22 + }{
23 + {
24 + name: "power_meter device",
25 + fs: &memoryFilesystem{
26 + symlinks: map[string]string{
27 + "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
28 + "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
29 + },
30 + files: []memoryFile{
31 + {
32 + name: "/sys/class/hwmon",
33 + info: &memoryFileInfo{
34 + isDir: true,
35 + },
36 + },
37 + {
38 + name: "/sys/class/hwmon/hwmon0",
39 + info: &memoryFileInfo{
40 + mode: os.ModeSymlink,
41 + },
42 + },
43 + {
44 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00",
45 + info: &memoryFileInfo{
46 + isDir: true,
47 + },
48 + },
49 + {
50 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name",
51 + err: os.ErrNotExist,
52 + },
53 + {
54 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device",
55 + info: &memoryFileInfo{
56 + // mode: os.ModeSymlink,
57 + },
58 + },
59 + {
60 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name",
61 + contents: "power_meter",
62 + },
63 + {
64 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average",
65 + contents: "345000000",
66 + },
67 + {
68 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_interval",
69 + contents: "1000",
70 + },
71 + {
72 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_is_battery",
73 + contents: "0",
74 + },
75 + {
76 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_model_number",
77 + contents: "Intel(R) Node Manager",
78 + },
79 + {
80 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_oem_info",
81 + contents: "Meter measures total domain",
82 + },
83 + {
84 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_serial_number",
85 + contents: "",
86 + },
87 + },
88 + },
89 + devices: []*Device{{
90 + Name: "power_meter-00",
91 + Sensors: []Sensor{
92 + &PowerSensor{
93 + Name: "power1",
94 + Average: 345.0,
95 + AverageInterval: 1 * time.Second,
96 + Battery: false,
97 + ModelNumber: "Intel(R) Node Manager",
98 + OEMInfo: "Meter measures total domain",
99 + SerialNumber: "",
100 + },
101 + },
102 + }},
103 + },
104 + {
105 + name: "acpitz device",
106 + fs: &memoryFilesystem{
107 + symlinks: map[string]string{
108 + "/sys/class/hwmon/hwmon0": "../../devices/virtual/hwmon/hwmon0",
109 + },
110 + files: []memoryFile{
111 + {
112 + name: "/sys/class/hwmon",
113 + info: &memoryFileInfo{
114 + isDir: true,
115 + },
116 + },
117 + {
118 + name: "/sys/class/hwmon/hwmon0",
119 + info: &memoryFileInfo{
120 + mode: os.ModeSymlink,
121 + },
122 + },
123 + {
124 + name: "/sys/devices/virtual/hwmon/hwmon0",
125 + info: &memoryFileInfo{
126 + isDir: true,
127 + },
128 + },
129 + {
130 + name: "/sys/devices/virtual/hwmon/hwmon0/name",
131 + contents: "acpitz",
132 + },
133 + {
134 + name: "/sys/devices/virtual/hwmon/hwmon0/temp1_crit",
135 + contents: "105000",
136 + },
137 + {
138 + name: "/sys/devices/virtual/hwmon/hwmon0/temp1_input",
139 + contents: "27800",
140 + },
141 + },
142 + },
143 + devices: []*Device{{
144 + Name: "acpitz-00",
145 + Sensors: []Sensor{
146 + &TemperatureSensor{
147 + Name: "temp1",
148 + Input: 27.8,
149 + Critical: 105.0,
150 + CriticalAlarm: false,
151 + },
152 + },
153 + }},
154 + },
155 + {
156 + name: "coretemp device",
157 + fs: &memoryFilesystem{
158 + symlinks: map[string]string{
159 + "/sys/class/hwmon/hwmon1": "../../devices/platform/coretemp.0/hwmon/hwmon1",
160 + "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device": "../../../coretemp.0",
161 + },
162 + files: []memoryFile{
163 + {
164 + name: "/sys/class/hwmon",
165 + info: &memoryFileInfo{
166 + isDir: true,
167 + },
168 + },
169 + {
170 + name: "/sys/class/hwmon/hwmon1",
171 + info: &memoryFileInfo{
172 + mode: os.ModeSymlink,
173 + },
174 + },
175 + {
176 + name: "/sys/devices/platform/coretemp.0",
177 + info: &memoryFileInfo{
178 + isDir: true,
179 + },
180 + },
181 + {
182 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/name",
183 + err: os.ErrNotExist,
184 + },
185 + {
186 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
187 + info: &memoryFileInfo{
188 + // mode: os.ModeSymlink,
189 + },
190 + },
191 + {
192 + name: "/sys/devices/platform/coretemp.0/name",
193 + contents: "coretemp",
194 + },
195 + {
196 + name: "/sys/devices/platform/coretemp.0/temp1_crit",
197 + contents: "100000",
198 + },
199 + {
200 + name: "/sys/devices/platform/coretemp.0/temp1_crit_alarm",
201 + contents: "0",
202 + },
203 + {
204 + name: "/sys/devices/platform/coretemp.0/temp1_input",
205 + contents: "40000",
206 + },
207 + {
208 + name: "/sys/devices/platform/coretemp.0/temp1_label",
209 + contents: "Core 0",
210 + },
211 + {
212 + name: "/sys/devices/platform/coretemp.0/temp1_max",
213 + contents: "80000",
214 + },
215 + {
216 + name: "/sys/devices/platform/coretemp.0/temp2_crit",
217 + contents: "100000",
218 + },
219 + {
220 + name: "/sys/devices/platform/coretemp.0/temp2_crit_alarm",
221 + contents: "0",
222 + },
223 + {
224 + name: "/sys/devices/platform/coretemp.0/temp2_input",
225 + contents: "42000",
226 + },
227 + {
228 + name: "/sys/devices/platform/coretemp.0/temp2_label",
229 + contents: "Core 1",
230 + },
231 + {
232 + name: "/sys/devices/platform/coretemp.0/temp2_max",
233 + contents: "80000",
234 + },
235 + },
236 + },
237 + devices: []*Device{{
238 + Name: "coretemp-00",
239 + Sensors: []Sensor{
240 + &TemperatureSensor{
241 + Name: "temp1",
242 + Label: "Core 0",
243 + Input: 40.0,
244 + High: 80.0,
245 + Critical: 100.0,
246 + CriticalAlarm: false,
247 + },
248 + &TemperatureSensor{
249 + Name: "temp2",
250 + Label: "Core 1",
251 + Input: 42.0,
252 + High: 80.0,
253 + Critical: 100.0,
254 + CriticalAlarm: false,
255 + },
256 + },
257 + }},
258 + },
259 + {
260 + name: "it8728 device",
261 + fs: &memoryFilesystem{
262 + symlinks: map[string]string{
263 + "/sys/class/hwmon/hwmon2": "../../devices/platform/it87.2608/hwmon/hwmon2",
264 + "/sys/devices/platform/it87.2608/hwmon/hwmon2/device": "../../../it87.2608",
265 + },
266 + files: []memoryFile{
267 + {
268 + name: "/sys/class/hwmon",
269 + info: &memoryFileInfo{
270 + isDir: true,
271 + },
272 + },
273 + {
274 + name: "/sys/class/hwmon/hwmon2",
275 + info: &memoryFileInfo{
276 + mode: os.ModeSymlink,
277 + },
278 + },
279 + {
280 + name: "/sys/devices/platform/it87.2608",
281 + info: &memoryFileInfo{
282 + isDir: true,
283 + },
284 + },
285 + {
286 + name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/name",
287 + err: os.ErrNotExist,
288 + },
289 + {
290 + name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/device",
291 + info: &memoryFileInfo{
292 + // mode: os.ModeSymlink,
293 + },
294 + },
295 + {
296 + name: "/sys/devices/platform/it87.2608/name",
297 + contents: "it8728",
298 + },
299 + {
300 + name: "/sys/devices/platform/it87.2608/fan1_alarm",
301 + contents: "0",
302 + },
303 + {
304 + name: "/sys/devices/platform/it87.2608/fan1_beep",
305 + contents: "1",
306 + },
307 + {
308 + name: "/sys/devices/platform/it87.2608/fan1_input",
309 + contents: "1010",
310 + },
311 + {
312 + name: "/sys/devices/platform/it87.2608/fan1_min",
313 + contents: "10",
314 + },
315 + {
316 + name: "/sys/devices/platform/it87.2608/in0_alarm",
317 + contents: "0",
318 + },
319 + {
320 + name: "/sys/devices/platform/it87.2608/in0_beep",
321 + contents: "0",
322 + },
323 + {
324 + name: "/sys/devices/platform/it87.2608/in0_input",
325 + contents: "1056",
326 + },
327 + {
328 + name: "/sys/devices/platform/it87.2608/in0_max",
329 + contents: "3060",
330 + },
331 + {
332 + name: "/sys/devices/platform/it87.2608/in1_alarm",
333 + contents: "0",
334 + },
335 + {
336 + name: "/sys/devices/platform/it87.2608/in1_beep",
337 + contents: "0",
338 + },
339 + {
340 + name: "/sys/devices/platform/it87.2608/in1_input",
341 + contents: "3384",
342 + },
343 + {
344 + name: "/sys/devices/platform/it87.2608/in1_label",
345 + contents: "3VSB",
346 + },
347 + {
348 + name: "/sys/devices/platform/it87.2608/in1_max",
349 + contents: "6120",
350 + },
351 + {
352 + name: "/sys/devices/platform/it87.2608/intrusion0_alarm",
353 + contents: "1",
354 + },
355 + {
356 + name: "/sys/devices/platform/it87.2608/temp1_alarm",
357 + contents: "0",
358 + },
359 + {
360 + name: "/sys/devices/platform/it87.2608/temp1_beep",
361 + contents: "1",
362 + },
363 + {
364 + name: "/sys/devices/platform/it87.2608/temp1_input",
365 + contents: "43000",
366 + },
367 + {
368 + name: "/sys/devices/platform/it87.2608/temp1_max",
369 + contents: "127000",
370 + },
371 + {
372 + name: "/sys/devices/platform/it87.2608/temp1_type",
373 + contents: "4",
374 + },
375 + },
376 + },
377 + devices: []*Device{{
378 + Name: "it8728-00",
379 + Sensors: []Sensor{
380 + &FanSensor{
381 + Name: "fan1",
382 + Alarm: false,
383 + Beep: true,
384 + Input: 1010,
385 + Minimum: 10,
386 + },
387 + &VoltageSensor{
388 + Name: "in0",
389 + Alarm: false,
390 + Beep: false,
391 + Input: 1.056,
392 + Maximum: 3.060,
393 + },
394 + &VoltageSensor{
395 + Name: "in1",
396 + Label: "3VSB",
397 + Alarm: false,
398 + Beep: false,
399 + Input: 3.384,
400 + Maximum: 6.120,
401 + },
402 + &IntrusionSensor{
403 + Name: "intrusion0",
404 + Alarm: true,
405 + },
406 + &TemperatureSensor{
407 + Name: "temp1",
408 + Alarm: false,
409 + Beep: true,
410 + Type: TemperatureSensorTypeThermistor,
411 + Input: 43.0,
412 + High: 127.0,
413 + },
414 + },
415 + }},
416 + },
417 + {
418 + name: "multiple coretemp devices",
419 + fs: &memoryFilesystem{
420 + symlinks: map[string]string{
421 + "/sys/class/hwmon/hwmon1": "../../devices/platform/coretemp.0/hwmon/hwmon1",
422 + "/sys/class/hwmon/hwmon2": "../../devices/platform/coretemp.1/hwmon/hwmon2",
423 + "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device": "../../../coretemp.0",
424 + "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device": "../../../coretemp.1",
425 + },
426 + files: []memoryFile{
427 + {
428 + name: "/sys/class/hwmon",
429 + info: &memoryFileInfo{
430 + isDir: true,
431 + },
432 + },
433 + {
434 + name: "/sys/class/hwmon/hwmon1",
435 + info: &memoryFileInfo{
436 + mode: os.ModeSymlink,
437 + },
438 + },
439 + {
440 + name: "/sys/class/hwmon/hwmon2",
441 + info: &memoryFileInfo{
442 + mode: os.ModeSymlink,
443 + },
444 + },
445 + {
446 + name: "/sys/devices/platform/coretemp.0",
447 + info: &memoryFileInfo{
448 + isDir: true,
449 + },
450 + },
451 + {
452 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/name",
453 + err: os.ErrNotExist,
454 + },
455 + {
456 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
457 + info: &memoryFileInfo{
458 + // mode: os.ModeSymlink,
459 + },
460 + },
461 + {
462 + name: "/sys/devices/platform/coretemp.0/name",
463 + contents: "coretemp",
464 + },
465 + {
466 + name: "/sys/devices/platform/coretemp.0/temp1_crit",
467 + contents: "100000",
468 + },
469 + {
470 + name: "/sys/devices/platform/coretemp.0/temp1_crit_alarm",
471 + contents: "0",
472 + },
473 + {
474 + name: "/sys/devices/platform/coretemp.0/temp1_input",
475 + contents: "40000",
476 + },
477 + {
478 + name: "/sys/devices/platform/coretemp.0/temp1_label",
479 + contents: "Core 0",
480 + },
481 + {
482 + name: "/sys/devices/platform/coretemp.0/temp1_max",
483 + contents: "80000",
484 + },
485 + {
486 + name: "/sys/devices/platform/coretemp.0/temp2_crit",
487 + contents: "100000",
488 + },
489 + {
490 + name: "/sys/devices/platform/coretemp.0/temp2_crit_alarm",
491 + contents: "0",
492 + },
493 + {
494 + name: "/sys/devices/platform/coretemp.0/temp2_input",
495 + contents: "42000",
496 + },
497 + {
498 + name: "/sys/devices/platform/coretemp.0/temp2_label",
499 + contents: "Core 1",
500 + },
501 + {
502 + name: "/sys/devices/platform/coretemp.0/temp2_max",
503 + contents: "80000",
504 + },
505 + {
506 + name: "/sys/devices/platform/coretemp.1",
507 + info: &memoryFileInfo{
508 + isDir: true,
509 + },
510 + },
511 + {
512 + name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/name",
513 + err: os.ErrNotExist,
514 + },
515 + {
516 + name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device",
517 + info: &memoryFileInfo{
518 + // mode: os.ModeSymlink,
519 + },
520 + },
521 + {
522 + name: "/sys/devices/platform/coretemp.1/name",
523 + contents: "coretemp",
524 + },
525 + {
526 + name: "/sys/devices/platform/coretemp.1/temp1_crit",
527 + contents: "100000",
528 + },
529 + {
530 + name: "/sys/devices/platform/coretemp.1/temp1_crit_alarm",
531 + contents: "0",
532 + },
533 + {
534 + name: "/sys/devices/platform/coretemp.1/temp1_input",
535 + contents: "38000",
536 + },
537 + {
538 + name: "/sys/devices/platform/coretemp.1/temp1_label",
539 + contents: "Core 0",
540 + },
541 + {
542 + name: "/sys/devices/platform/coretemp.1/temp1_max",
543 + contents: "80000",
544 + },
545 + {
546 + name: "/sys/devices/platform/coretemp.1/temp2_crit",
547 + contents: "100000",
548 + },
549 + {
550 + name: "/sys/devices/platform/coretemp.1/temp2_crit_alarm",
551 + contents: "0",
552 + },
553 + {
554 + name: "/sys/devices/platform/coretemp.1/temp2_input",
555 + contents: "37000",
556 + },
557 + {
558 + name: "/sys/devices/platform/coretemp.1/temp2_label",
559 + contents: "Core 1",
560 + },
561 + {
562 + name: "/sys/devices/platform/coretemp.1/temp2_max",
563 + contents: "80000",
564 + },
565 + },
566 + },
567 + devices: []*Device{
568 + {
569 + Name: "coretemp-00",
570 + Sensors: []Sensor{
571 + &TemperatureSensor{
572 + Name: "temp1",
573 + Label: "Core 0",
574 + Input: 40.0,
575 + High: 80.0,
576 + Critical: 100.0,
577 + CriticalAlarm: false,
578 + },
579 + &TemperatureSensor{
580 + Name: "temp2",
581 + Label: "Core 1",
582 + Input: 42.0,
583 + High: 80.0,
584 + Critical: 100.0,
585 + CriticalAlarm: false,
586 + },
587 + },
588 + },
589 + {
590 + Name: "coretemp-01",
591 + Sensors: []Sensor{
592 + &TemperatureSensor{
593 + Name: "temp1",
594 + Label: "Core 0",
595 + Input: 38.0,
596 + High: 80.0,
597 + Critical: 100.0,
598 + CriticalAlarm: false,
599 + },
600 + &TemperatureSensor{
601 + Name: "temp2",
602 + Label: "Core 1",
603 + Input: 37.0,
604 + High: 80.0,
605 + Critical: 100.0,
606 + CriticalAlarm: false,
607 + },
608 + },
609 + },
610 + },
611 + },
612 + {
613 + name: "sfc device",
614 + fs: &memoryFilesystem{
615 + symlinks: map[string]string{
616 + "/sys/class/hwmon/hwmon0": "../../devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0",
617 + "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device": "../../../0000:03:00.0",
618 + },
619 + files: []memoryFile{
620 + {
621 + name: "/sys/class/hwmon",
622 + info: &memoryFileInfo{
623 + isDir: true,
624 + },
625 + },
626 + {
627 + name: "/sys/class/hwmon/hwmon0",
628 + info: &memoryFileInfo{
629 + mode: os.ModeSymlink,
630 + },
631 + },
632 + {
633 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0",
634 + info: &memoryFileInfo{
635 + isDir: true,
636 + },
637 + },
638 + {
639 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/name",
640 + err: os.ErrNotExist,
641 + },
642 + {
643 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device",
644 + info: &memoryFileInfo{
645 + // mode: os.ModeSymlink,
646 + },
647 + },
648 + {
649 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/name",
650 + contents: "sfc",
651 + },
652 + {
653 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_alarm",
654 + contents: "0",
655 + },
656 + {
657 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_crit",
658 + contents: "18000",
659 + },
660 + {
661 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_input",
662 + contents: "7624",
663 + },
664 + {
665 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_label",
666 + contents: "0.9V supply current",
667 + },
668 + {
669 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_max",
670 + contents: "16000",
671 + },
672 + },
673 + },
674 + devices: []*Device{{
675 + Name: "sfc-00",
676 + Sensors: []Sensor{
677 + &CurrentSensor{
678 + Name: "curr1",
679 + Label: "0.9V supply current",
680 + Alarm: false,
681 + Input: 7.624,
682 + Maximum: 16.0,
683 + Critical: 18.0,
684 + },
685 + },
686 + }},
687 + },
688 + }
689 +
690 + for _, tt := range tests {
691 + t.Run(tt.name, func(t *testing.T) {
692 + s := &Scanner{fs: tt.fs}
693 +
694 + devices, err := s.Scan()
695 + if err != nil {
696 + t.Fatalf("unexpected error: %v", err)
697 + }
698 +
699 + if want, got := tt.devices, devices; !reflect.DeepEqual(want, got) {
700 + t.Fatalf("unexpected Devices:\n- want:\n%v\n- got:\n%v",
701 + devicesStr(want), devicesStr(got))
702 + }
703 + })
704 + }
705 +}
706 +
707 +func devicesStr(ds []*Device) string {
708 + var out string
709 + for _, d := range ds {
710 + out += fmt.Sprintf("device: %q [%d sensors]\n", d.Name, len(d.Sensors))
711 +
712 + for _, s := range d.Sensors {
713 + out += fmt.Sprintf(" - sensor: %#v\n", s)
714 + }
715 + }
716 +
717 + return out
718 +}
719 +
720 +var _ filesystem = &memoryFilesystem{}
721 +
722 +// A memoryFilesystem is an in-memory implementation of filesystem, used for
723 +// tests.
724 +type memoryFilesystem struct {
725 + symlinks map[string]string
726 + files []memoryFile
727 +}
728 +
729 +func (fs *memoryFilesystem) ReadFile(filename string) (string, error) {
730 + for _, f := range fs.files {
731 + if f.name == filename {
732 + return f.contents, nil
733 + }
734 + }
735 +
736 + return "", fmt.Errorf("readfile: file %q not in memory", filename)
737 +}
738 +
739 +func (fs *memoryFilesystem) Readlink(name string) (string, error) {
740 + if l, ok := fs.symlinks[name]; ok {
741 + return l, nil
742 + }
743 +
744 + return "", fmt.Errorf("readlink: symlink %q not in memory", name)
745 +}
746 +
747 +func (fs *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
748 + for _, f := range fs.files {
749 + if f.name == name {
750 + info := f.info
751 + if info == nil {
752 + info = &memoryFileInfo{}
753 + }
754 +
755 + return info, f.err
756 + }
757 + }
758 +
759 + return nil, fmt.Errorf("stat: file %q not in memory", name)
760 +}
761 +
762 +func (fs *memoryFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
763 + if _, err := fs.Stat(root); err != nil {
764 + return err
765 + }
766 +
767 + for _, f := range fs.files {
768 + // Only walk paths under the specified root
769 + if !strings.HasPrefix(f.name, root) {
770 + continue
771 + }
772 +
773 + info := f.info
774 + if info == nil {
775 + info = &memoryFileInfo{}
776 + }
777 +
778 + if err := walkFn(f.name, info, nil); err != nil {
779 + return err
780 + }
781 + }
782 +
783 + return nil
784 +}
785 +
786 +// A memoryFile is an in-memory file used by memoryFilesystem.
787 +type memoryFile struct {
788 + name string
789 + contents string
790 + info os.FileInfo
791 + err error
792 +}
793 +
794 +var _ os.FileInfo = &memoryFileInfo{}
795 +
796 +// A memoryFileInfo is an os.FileInfo used by memoryFiles.
797 +type memoryFileInfo struct {
798 + name string
799 + size int64
800 + mode os.FileMode
801 + modTime time.Time
802 + isDir bool
803 +}
804 +
805 +func (fi *memoryFileInfo) Name() string { return fi.name }
806 +func (fi *memoryFileInfo) Size() int64 { return fi.size }
807 +func (fi *memoryFileInfo) Mode() os.FileMode { return fi.mode }
808 +func (fi *memoryFileInfo) ModTime() time.Time { return fi.modTime }
809 +func (fi *memoryFileInfo) IsDir() bool { return fi.isDir }
810 +func (fi *memoryFileInfo) Sys() interface{} { return nil }
src/go/plugin/go.d/modules/sensors/lmsensors/sensor.go new
+68
@@ -0,0 +1,68 @@
1 +package lmsensors
2 +
3 +import (
4 + "sort"
5 + "strings"
6 +)
7 +
8 +// A Device is a physical or virtual device which may have zero or
9 +// more Sensors.
10 +type Device struct {
11 + // The name of the device.
12 + Name string
13 +
14 + // Any Sensors that belong to this Device. Use type assertions to
15 + // check for specific Sensor types and fetch their data.
16 + Sensors []Sensor
17 +}
18 +
19 +// A Sensor is a hardware sensor, used to retrieve device temperatures,
20 +// fan speeds, voltages, etc. Use type assertions to check for specific
21 +// Sensor types and fetch their data.
22 +type Sensor interface {
23 + parse(raw map[string]string) error
24 + name() string
25 + setName(name string)
26 +}
27 +
28 +// parseSensors parses all Sensors from an input raw data slice, produced
29 +// during a filesystem walk.
30 +func parseSensors(raw map[string]map[string]string) ([]Sensor, error) {
31 + sensors := make([]Sensor, 0, len(raw))
32 + for k, v := range raw {
33 + var s Sensor
34 + switch {
35 + case strings.HasPrefix(k, "curr"):
36 + s = new(CurrentSensor)
37 + case strings.HasPrefix(k, "intrusion"):
38 + s = new(IntrusionSensor)
39 + case strings.HasPrefix(k, "in"):
40 + s = new(VoltageSensor)
41 + case strings.HasPrefix(k, "fan"):
42 + s = new(FanSensor)
43 + case strings.HasPrefix(k, "power"):
44 + s = new(PowerSensor)
45 + case strings.HasPrefix(k, "temp"):
46 + s = new(TemperatureSensor)
47 + default:
48 + continue
49 + }
50 +
51 + s.setName(k)
52 + if err := s.parse(v); err != nil {
53 + return nil, err
54 + }
55 +
56 + sensors = append(sensors, s)
57 + }
58 +
59 + sort.Sort(byName(sensors))
60 + return sensors, nil
61 +}
62 +
63 +// byName implements sort.Interface for []Sensor.
64 +type byName []Sensor
65 +
66 +func (b byName) Len() int { return len(b) }
67 +func (b byName) Less(i, j int) bool { return b[i].name() < b[j].name() }
68 +func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
src/go/plugin/go.d/modules/sensors/lmsensors/temperaturesensor.go new
+101
@@ -0,0 +1,101 @@
1 +package lmsensors
2 +
3 +import (
4 + "strconv"
5 +)
6 +
7 +// A TemperatureSensorType is value that indicates the type of a
8 +// TemperatureSensor.
9 +type TemperatureSensorType int
10 +
11 +// All possible TemperatureSensorType constants.
12 +const (
13 + TemperatureSensorUnknown TemperatureSensorType = 0
14 + TemperatureSensorTypePIICeleronDiode TemperatureSensorType = 1
15 + TemperatureSensorType3904Transistor TemperatureSensorType = 2
16 + TemperatureSensorTypeThermalDiode TemperatureSensorType = 3
17 + TemperatureSensorTypeThermistor TemperatureSensorType = 4
18 + TemperatureSensorTypeAMDAMDSI TemperatureSensorType = 5
19 + TemperatureSensorTypeIntelPECI TemperatureSensorType = 6
20 +)
21 +
22 +var _ Sensor = &TemperatureSensor{}
23 +
24 +// A TemperatureSensor is a Sensor that detects temperatures in degrees
25 +// Celsius.
26 +type TemperatureSensor struct {
27 + // The name of the sensor.
28 + Name string
29 +
30 + // A label that describes what the sensor is monitoring. Label may be
31 + // empty.
32 + Label string
33 +
34 + // Whether or not the sensor has an alarm triggered.
35 + Alarm bool
36 +
37 + // Whether or not the sensor will sound an audible alarm if an alarm
38 + // is triggered.
39 + Beep bool
40 +
41 + // The type of sensor used to report tempearatures.
42 + Type TemperatureSensorType
43 +
44 + // The input temperature, in degrees Celsius, indicated by the sensor.
45 + Input float64
46 +
47 + // A high threshold temperature, in degrees Celsius, indicated by the
48 + // sensor.
49 + High float64
50 +
51 + // A critical threshold temperature, in degrees Celsius, indicated by the
52 + // sensor.
53 + Critical float64
54 +
55 + // Whether or not the temperature is past the critical threshold.
56 + CriticalAlarm bool
57 +}
58 +
59 +func (s *TemperatureSensor) name() string { return s.Name }
60 +func (s *TemperatureSensor) setName(name string) { s.Name = name }
61 +
62 +func (s *TemperatureSensor) parse(raw map[string]string) error {
63 + for k, v := range raw {
64 + switch k {
65 + case "input", "crit", "max":
66 + f, err := strconv.ParseFloat(v, 64)
67 + if err != nil {
68 + return err
69 + }
70 +
71 + // Raw temperature values are scaled by 1000
72 + f /= 1000
73 +
74 + switch k {
75 + case "input":
76 + s.Input = f
77 + case "crit":
78 + s.Critical = f
79 + case "max":
80 + s.High = f
81 + }
82 + case "alarm":
83 + s.Alarm = v != "0"
84 + case "beep":
85 + s.Beep = v != "0"
86 + case "type":
87 + t, err := strconv.Atoi(v)
88 + if err != nil {
89 + return err
90 + }
91 +
92 + s.Type = TemperatureSensorType(t)
93 + case "crit_alarm":
94 + s.CriticalAlarm = v != "0"
95 + case "label":
96 + s.Label = v
97 + }
98 + }
99 +
100 + return nil
101 +}
src/go/plugin/go.d/modules/sensors/lmsensors/voltagesensor.go new
+63
@@ -0,0 +1,63 @@
1 +package lmsensors
2 +
3 +import (
4 + "strconv"
5 +)
6 +
7 +var _ Sensor = &VoltageSensor{}
8 +
9 +// A VoltageSensor is a Sensor that detects voltage.
10 +type VoltageSensor struct {
11 + // The name of the sensor.
12 + Name string
13 +
14 + // A label that describes what the sensor is monitoring. Label may be
15 + // empty.
16 + Label string
17 +
18 + // Whether or not the sensor has an alarm triggered.
19 + Alarm bool
20 +
21 + // Whether or not the sensor will sound an audible alarm when an alarm
22 + // is triggered.
23 + Beep bool
24 +
25 + // The input voltage indicated by the sensor.
26 + Input float64
27 +
28 + // The maximum voltage threshold indicated by the sensor.
29 + Maximum float64
30 +}
31 +
32 +func (s *VoltageSensor) name() string { return s.Name }
33 +func (s *VoltageSensor) setName(name string) { s.Name = name }
34 +
35 +func (s *VoltageSensor) parse(raw map[string]string) error {
36 + for k, v := range raw {
37 + switch k {
38 + case "input", "max":
39 + f, err := strconv.ParseFloat(v, 64)
40 + if err != nil {
41 + return err
42 + }
43 +
44 + // Raw temperature values are scaled by 1000
45 + f /= 1000
46 +
47 + switch k {
48 + case "input":
49 + s.Input = f
50 + case "max":
51 + s.Maximum = f
52 + }
53 + case "alarm":
54 + s.Alarm = v != "0"
55 + case "beep":
56 + s.Beep = v != "0"
57 + case "label":
58 + s.Label = v
59 + }
60 + }
61 +
62 + return nil
63 +}