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