1 /* $OpenBSD: sensors.c,v 1.33 2024/11/08 08:45:47 matthieu Exp $ */
2
3 /*
4 * Copyright (c) 2007 Deanna Phillips <deanna@openbsd.org>
5 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
6 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 */
21
22 #include <sys/types.h>
23 #include <sys/signal.h>
24 #include <sys/sysctl.h>
25 #include <sys/sensors.h>
26
27 #include <err.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <util.h>
33 #include "systat.h"
34
35 struct sensor sensor;
36 struct sensordev sensordev;
37
38 struct sensinfo {
39 int sn_dev;
40 struct sensor sn_sensor;
41 };
42 #define sn_type sn_sensor.type
43 #define sn_numt sn_sensor.numt
44 #define sn_desc sn_sensor.desc
45 #define sn_status sn_sensor.status
46 #define sn_value sn_sensor.value
47
48 #define SYSTAT_MAXSENSORDEVICES 1024
49 char *devnames[SYSTAT_MAXSENSORDEVICES];
50
51 #define ADD_ALLOC 100
52 static size_t sensor_cnt = 0;
53 static size_t num_alloc = 0;
54 static struct sensinfo *sensors = NULL;
55
56 static char *fmttime(double);
57 static void showsensor(struct sensinfo *s);
58
59 void print_sn(void);
60 int read_sn(void);
61 int select_sn(void);
62
63 const char *drvstat[] = {
64 NULL,
65 "empty", "ready", "powering up", "online", "idle", "active",
66 "rebuilding", "powering down", "failed", "degraded"
67 };
68
69
70 field_def fields_sn[] = {
71 {"SENSOR", 16, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
72 {"VALUE", 16, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
73 {"STATUS", 5, 8, 1, FLD_ALIGN_CENTER, -1, 0, 0, 0},
74 {"DESCRIPTION", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}
75 };
76
77 #define FLD_SN_SENSOR FIELD_ADDR(fields_sn,0)
78 #define FLD_SN_VALUE FIELD_ADDR(fields_sn,1)
79 #define FLD_SN_STATUS FIELD_ADDR(fields_sn,2)
80 #define FLD_SN_DESCR FIELD_ADDR(fields_sn,3)
81
82 /* Define views */
83 field_def *view_sn_0[] = {
84 FLD_SN_SENSOR, FLD_SN_VALUE, FLD_SN_STATUS, FLD_SN_DESCR, NULL
85 };
86
87
88 /* Define view managers */
89 struct view_manager sensors_mgr = {
90 "Sensors", select_sn, read_sn, NULL, print_header,
91 print_sn, keyboard_callback, NULL, NULL
92 };
93
94 field_view views_sn[] = {
95 {view_sn_0, "sensors", '3', &sensors_mgr},
96 {NULL, NULL, 0, NULL}
97 };
98
99 struct sensinfo *
next_sn(void)100 next_sn(void)
101 {
102 if (num_alloc <= sensor_cnt) {
103 struct sensinfo *s;
104 size_t a = num_alloc + ADD_ALLOC;
105 if (a < num_alloc)
106 return NULL;
107 s = reallocarray(sensors, a, sizeof(struct sensinfo));
108 if (s == NULL)
109 return NULL;
110 sensors = s;
111 num_alloc = a;
112 }
113
114 return &sensors[sensor_cnt++];
115 }
116
117
118 int
select_sn(void)119 select_sn(void)
120 {
121 num_disp = sensor_cnt;
122 return (0);
123 }
124
125 int
read_sn(void)126 read_sn(void)
127 {
128 enum sensor_type type;
129 size_t slen, sdlen;
130 int mib[5], dev, numt;
131 struct sensinfo *s;
132
133 mib[0] = CTL_HW;
134 mib[1] = HW_SENSORS;
135
136 sensor_cnt = 0;
137
138 for (dev = 0; dev < SYSTAT_MAXSENSORDEVICES; dev++) {
139 mib[2] = dev;
140 sdlen = sizeof(struct sensordev);
141 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
142 if (errno == ENOENT)
143 break;
144 if (errno == ENXIO)
145 continue;
146 error("sysctl: %s", strerror(errno));
147 }
148
149 if (devnames[dev] && strcmp(devnames[dev], sensordev.xname)) {
150 free(devnames[dev]);
151 devnames[dev] = NULL;
152 }
153 if (devnames[dev] == NULL)
154 devnames[dev] = strdup(sensordev.xname);
155
156 for (type = 0; type < SENSOR_MAX_TYPES; type++) {
157 mib[3] = type;
158 for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
159 mib[4] = numt;
160 slen = sizeof(struct sensor);
161 if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
162 == -1) {
163 if (errno != ENOENT)
164 error("sysctl: %s", strerror(errno));
165 continue;
166 }
167 if (sensor.flags & SENSOR_FINVALID)
168 continue;
169
170 s = next_sn();
171 s->sn_sensor = sensor;
172 s->sn_dev = dev;
173 }
174 }
175 }
176
177 num_disp = sensor_cnt;
178 return 0;
179 }
180
181
182 void
print_sn(void)183 print_sn(void)
184 {
185 int n, count = 0;
186
187 for (n = dispstart; n < num_disp; n++) {
188 showsensor(sensors + n);
189 count++;
190 if (maxprint > 0 && count >= maxprint)
191 break;
192 }
193 }
194
195 int
initsensors(void)196 initsensors(void)
197 {
198 field_view *v;
199
200 memset(devnames, 0, sizeof(devnames));
201
202 for (v = views_sn; v->name != NULL; v++)
203 add_view(v);
204
205 return(1);
206 }
207
208 static void
showsensor(struct sensinfo * s)209 showsensor(struct sensinfo *s)
210 {
211 tb_start();
212 tbprintf("%s.%s%d", devnames[s->sn_dev],
213 sensor_type_s[s->sn_type], s->sn_numt);
214 print_fld_tb(FLD_SN_SENSOR);
215
216 if (s->sn_desc[0] != '\0')
217 print_fld_str(FLD_SN_DESCR, s->sn_desc);
218
219 tb_start();
220
221 switch (s->sn_type) {
222 case SENSOR_TEMP:
223 tbprintf("%10.2f degC",
224 (s->sn_value - 273150000) / 1000000.0);
225 break;
226 case SENSOR_FANRPM:
227 tbprintf("%11lld RPM", s->sn_value);
228 break;
229 case SENSOR_VOLTS_DC:
230 tbprintf("%10.2f V DC",
231 s->sn_value / 1000000.0);
232 break;
233 case SENSOR_VOLTS_AC:
234 tbprintf("%10.2f V AC",
235 s->sn_value / 1000000.0);
236 break;
237 case SENSOR_OHMS:
238 tbprintf("%11lld ohm", s->sn_value);
239 break;
240 case SENSOR_WATTS:
241 tbprintf("%10.2f W", s->sn_value / 1000000.0);
242 break;
243 case SENSOR_AMPS:
244 tbprintf("%10.2f A", s->sn_value / 1000000.0);
245 break;
246 case SENSOR_WATTHOUR:
247 tbprintf("%12.2f Wh", s->sn_value / 1000000.0);
248 break;
249 case SENSOR_AMPHOUR:
250 tbprintf("%10.2f Ah", s->sn_value / 1000000.0);
251 break;
252 case SENSOR_INDICATOR:
253 tbprintf("%15s", s->sn_value ? "On" : "Off");
254 break;
255 case SENSOR_INTEGER:
256 tbprintf("%11lld raw", s->sn_value);
257 break;
258 case SENSOR_PERCENT:
259 tbprintf("%14.2f%%", s->sn_value / 1000.0);
260 break;
261 case SENSOR_LUX:
262 tbprintf("%15.2f lx", s->sn_value / 1000000.0);
263 break;
264 case SENSOR_DRIVE:
265 if (0 < s->sn_value &&
266 s->sn_value < sizeof(drvstat)/sizeof(drvstat[0])) {
267 tbprintf("%15s", drvstat[s->sn_value]);
268 break;
269 }
270 break;
271 case SENSOR_TIMEDELTA:
272 tbprintf("%15s", fmttime(s->sn_value / 1000000000.0));
273 break;
274 case SENSOR_HUMIDITY:
275 tbprintf("%3.2f%%", s->sn_value / 1000.0);
276 break;
277 case SENSOR_FREQ:
278 if (humanreadable) {
279 char buf[FMT_SCALED_STRSIZE];
280 fmt_scaled(s->sn_value / 1000000.0, buf);
281 tbprintf("%sHz", buf);
282 } else
283 tbprintf("%11.2f Hz", s->sn_value / 1000000.0);
284 break;
285 case SENSOR_ANGLE:
286 tbprintf("%3.4f degrees", s->sn_value / 1000000.0);
287 break;
288 case SENSOR_DISTANCE:
289 tbprintf("%.3f m", s->sn_value / 1000000.0);
290 break;
291 case SENSOR_PRESSURE:
292 tbprintf("%.2f Pa", s->sn_value / 1000.0);
293 break;
294 case SENSOR_ACCEL:
295 tbprintf("%2.4f m/s^2", s->sn_value / 1000000.0);
296 break;
297 case SENSOR_VELOCITY:
298 tbprintf("%4.3f m/s", s->sn_value / 1000000.0);
299 break;
300 case SENSOR_ENERGY:
301 tbprintf("%.2f J", s->sn_value / 1000000.0);
302 break;
303 default:
304 tbprintf("%10lld", s->sn_value);
305 break;
306 }
307
308 print_fld_tb(FLD_SN_VALUE);
309
310 switch (s->sn_status) {
311 case SENSOR_S_UNSPEC:
312 break;
313 case SENSOR_S_UNKNOWN:
314 print_fld_str(FLD_SN_STATUS, "unknown");
315 break;
316 case SENSOR_S_WARN:
317 print_fld_str(FLD_SN_STATUS, "WARNING");
318 break;
319 case SENSOR_S_CRIT:
320 print_fld_str(FLD_SN_STATUS, "CRITICAL");
321 break;
322 case SENSOR_S_OK:
323 print_fld_str(FLD_SN_STATUS, "OK");
324 break;
325 }
326 end_line();
327 }
328
329 #define SECS_PER_DAY 86400
330 #define SECS_PER_HOUR 3600
331 #define SECS_PER_MIN 60
332
333 static char *
fmttime(double in)334 fmttime(double in)
335 {
336 int signbit = 1;
337 int tiny = 0;
338 char *unit;
339 #define LEN 32
340 static char outbuf[LEN];
341
342 if (in < 0){
343 signbit = -1;
344 in *= -1;
345 }
346
347 if (in >= SECS_PER_DAY ){
348 unit = "days";
349 in /= SECS_PER_DAY;
350 } else if (in >= SECS_PER_HOUR ){
351 unit = "hr";
352 in /= SECS_PER_HOUR;
353 } else if (in >= SECS_PER_MIN ){
354 unit = "min";
355 in /= SECS_PER_MIN;
356 } else if (in >= 1 ){
357 unit = "s";
358 /* in *= 1; */ /* no op */
359 } else if (in == 0 ){ /* direct comparisons to floats are scary */
360 unit = "s";
361 } else if (in >= 1e-3 ){
362 unit = "ms";
363 in *= 1e3;
364 } else if (in >= 1e-6 ){
365 unit = "us";
366 in *= 1e6;
367 } else if (in >= 1e-9 ){
368 unit = "ns";
369 in *= 1e9;
370 } else {
371 unit = "ps";
372 if (in < 1e-13)
373 tiny = 1;
374 in *= 1e12;
375 }
376
377 snprintf(outbuf, LEN,
378 tiny ? "%s%f %s" : "%s%.3f %s",
379 signbit == -1 ? "-" : "", in, unit);
380
381 return outbuf;
382 }
383