xref: /dragonfly/usr.bin/systat/sensors.c (revision e5a92d33)
1 /* $OpenBSD: sensors.c,v 1.11 2007/03/23 14:48:22 ckuethe 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 #include <sys/param.h>
22 #include <sys/sysctl.h>
23 #include <sys/sensors.h>
24 
25 #include <ctype.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <libutil.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "systat.h"
34 #include "extern.h"
35 
36 struct sensor sensor;
37 struct sensordev sensordev;
38 int row, sensor_cnt;
39 void printline(void);
40 static char * fmttime(double);
41 
42 struct sensordev_xname {
43 	char	xname[24];
44 	int	xname_len;
45 	u_int	flags;	/* XNAME_FLAG_ */
46 };
47 
48 #define XNAME_FLAG_WILDCARD	0x1
49 
50 static int	sensors_enabled[SENSOR_MAX_TYPES];
51 
52 #define XNAME_MAX		64
53 
54 static int	sensordev_xname_cnt;
55 static struct sensordev_xname sensordev_xname[XNAME_MAX];
56 
57 WINDOW *
58 opensensors(void)
59 {
60 	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
61 }
62 
63 void
64 closesensors(WINDOW *w)
65 {
66 	if (w == NULL)
67 		return;
68 	wclear(w);
69 	wrefresh(w);
70 	delwin(w);
71 }
72 
73 void
74 labelsensors(void)
75 {
76 	wmove(wnd, 0, 0);
77 	wclrtobot(wnd);
78 	mvwaddstr(wnd, 0, 0, "Sensor");
79 	mvwaddstr(wnd, 0, 34, "Value");
80 	mvwaddstr(wnd, 0, 45, "Status");
81 	mvwaddstr(wnd, 0, 58, "Description");
82 }
83 
84 void
85 fetchsensors(void)
86 {
87 	enum sensor_type type;
88 	size_t		 slen, sdlen, idmax_len;
89 	int		 mib[5], dev, numt, idmax;
90 	int		 maxsensordevices;
91 
92 	maxsensordevices = MAXSENSORDEVICES;
93 	idmax_len = sizeof(idmax);
94 	if (sysctlbyname("hw.sensors.dev_idmax", &idmax, &idmax_len,
95 	    NULL, 0) == 0)
96 		maxsensordevices = idmax;
97 
98 	mib[0] = CTL_HW;
99 	mib[1] = HW_SENSORS;
100 	slen = sizeof(struct sensor);
101 	sdlen = sizeof(struct sensordev);
102 
103 	row = 1;
104 	sensor_cnt = 0;
105 
106 	wmove(wnd, row, 0);
107 	wclrtobot(wnd);
108 
109 	for (dev = 0; dev < maxsensordevices; dev++) {
110 		mib[2] = dev;
111 		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
112 			if (errno != ENOENT)
113 				warn("sysctl");
114 			continue;
115 		}
116 
117 		if (sensordev_xname_cnt > 0) {
118 			int i, match = 0, xname_len;
119 
120 			xname_len = strlen(sensordev.xname);
121 			for (i = 0; i < sensordev_xname_cnt; ++i) {
122 				const struct sensordev_xname *x;
123 
124 				x = &sensordev_xname[i];
125 				if (x->flags & XNAME_FLAG_WILDCARD) {
126 					if (xname_len <= x->xname_len)
127 						continue;
128 					if (!isdigit(
129 					    sensordev.xname[x->xname_len]))
130 						continue;
131 					if (strncmp(x->xname, sensordev.xname,
132 					    x->xname_len) == 0) {
133 						match = 1;
134 						break;
135 					}
136 				} else if (xname_len == x->xname_len &&
137 				    strcmp(x->xname, sensordev.xname) == 0) {
138 					match = 1;
139 					break;
140 				}
141 			}
142 			if (!match)
143 				continue;
144 		}
145 
146 		for (type = 0; type < SENSOR_MAX_TYPES; type++) {
147 			if (!sensors_enabled[type])
148 				continue;
149 
150 			mib[3] = type;
151 			for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
152 				mib[4] = numt;
153 				if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
154 				    == -1) {
155 					if (errno != ENOENT)
156 						warn("sysctl");
157 					continue;
158 				}
159 				if (sensor.flags & SENSOR_FINVALID)
160 					continue;
161 				sensor_cnt++;
162 				printline();
163 			}
164 		}
165 	}
166 }
167 
168 const char *drvstat[] = {
169 	NULL,
170 	"empty", "ready", "powerup", "online", "idle", "active",
171 	"rebuild", "powerdown", "fail", "pfail"
172 };
173 
174 void
175 showsensors(void)
176 {
177 	if (sensor_cnt == 0)
178 		mvwaddstr(wnd, row, 0, "No sensors found.");
179 }
180 
181 int
182 initsensors(void)
183 {
184 	int i;
185 
186 	for (i = 0; i < SENSOR_MAX_TYPES; ++i)
187 		sensors_enabled[i] = 1;
188 	return (1);
189 }
190 
191 void
192 printline(void)
193 {
194 	char buf[9];
195 
196 	mvwprintw(wnd, row, 0, "%s.%s%d", sensordev.xname,
197 	    sensor_type_s[sensor.type], sensor.numt);
198 	switch (sensor.type) {
199 	case SENSOR_TEMP:
200 		mvwprintw(wnd, row, 24, "%10.2f degC",
201 		    (sensor.value - 273150000) / 1000000.0);
202 		break;
203 	case SENSOR_FANRPM:
204 		mvwprintw(wnd, row, 24, "%11lld RPM", sensor.value);
205 		break;
206 	case SENSOR_VOLTS_DC:
207 		mvwprintw(wnd, row, 24, "%10.2f V DC",
208 		    sensor.value / 1000000.0);
209 		break;
210 	case SENSOR_WATTS:
211 		mvwprintw(wnd, row, 24, "%13.2f W", sensor.value / 1000000.0);
212 		break;
213 	case SENSOR_AMPS:
214 		mvwprintw(wnd, row, 24, "%10.2f A", sensor.value / 1000000.0);
215 		break;
216 	case SENSOR_INDICATOR:
217 		mvwprintw(wnd, row, 24, "%15s", sensor.value? "On" : "Off");
218 		break;
219 	case SENSOR_INTEGER:
220 		mvwprintw(wnd, row, 24, "%11lld raw", sensor.value);
221 		break;
222 	case SENSOR_PERCENT:
223 		mvwprintw(wnd, row, 24, "%14.2f%%", sensor.value / 1000.0);
224 		break;
225 	case SENSOR_LUX:
226 		mvwprintw(wnd, row, 24, "%15.2f lx", sensor.value / 1000000.0);
227 		break;
228 	case SENSOR_DRIVE:
229 		if (0 < sensor.value &&
230 		    (size_t)sensor.value < NELEM(drvstat)) {
231 			mvwprintw(wnd, row, 24, "%15s", drvstat[sensor.value]);
232 			break;
233 		}
234 		break;
235 	case SENSOR_TIMEDELTA:
236 		mvwprintw(wnd, row, 24, "%15s", fmttime(sensor.value / 1000000000.0));
237 		break;
238 	case SENSOR_WATTHOUR:
239 		mvwprintw(wnd, row, 24, "%12.2f Wh", sensor.value / 1000000.0);
240 		break;
241 	case SENSOR_AMPHOUR:
242 		mvwprintw(wnd, row, 24, "%10.2f Ah", sensor.value / 1000000.0);
243 		break;
244 	case SENSOR_FREQ:
245 		humanize_number(buf, sizeof(buf), sensor.value, "Hz",
246 		    HN_AUTOSCALE, HN_DIVISOR_1000 | HN_DECIMAL);
247 		mvwprintw(wnd, row, 24, "%15s", buf);
248 		break;
249 	default:
250 		mvwprintw(wnd, row, 24, "%10lld", sensor.value);
251 		break;
252 	}
253 	if (sensor.desc[0] != '\0')
254 		mvwprintw(wnd, row, 58, "(%s)", sensor.desc);
255 
256 	switch (sensor.status) {
257 	case SENSOR_S_UNSPEC:
258 		break;
259 	case SENSOR_S_UNKNOWN:
260 		mvwaddstr(wnd, row, 45, "unknown");
261 		break;
262 	case SENSOR_S_WARN:
263 		mvwaddstr(wnd, row, 45, "WARNING");
264 		break;
265 	case SENSOR_S_CRIT:
266 		mvwaddstr(wnd, row, 45, "CRITICAL");
267 		break;
268 	case SENSOR_S_OK:
269 		mvwaddstr(wnd, row, 45, "OK");
270 		break;
271 	}
272 	row++;
273 }
274 
275 #define SECS_PER_DAY 86400
276 #define SECS_PER_HOUR 3600
277 #define SECS_PER_MIN 60
278 
279 static char *
280 fmttime(double in)
281 {
282 	int signbit = 1;
283 	int tiny = 0;
284 	const char *unit;
285 #define LEN 32
286 	static char outbuf[LEN];
287 
288 	if (in < 0){
289 		signbit = -1;
290 		in *= -1;
291 	}
292 
293 	if (in >= SECS_PER_DAY ){
294 		unit = "days";
295 		in /= SECS_PER_DAY;
296 	} else if (in >= SECS_PER_HOUR ){
297 		unit = "hr";
298 		in /= SECS_PER_HOUR;
299 	} else if (in >= SECS_PER_MIN ){
300 		unit = "min";
301 		in /= SECS_PER_MIN;
302 	} else if (in >= 1 ){
303 		unit = "s";
304 		/* in *= 1; */ /* no op */
305 	} else if (in == 0 ){ /* direct comparisons to floats are scary */
306 		unit = "s";
307 	} else if (in >= 1e-3 ){
308 		unit = "ms";
309 		in *= 1e3;
310 	} else if (in >= 1e-6 ){
311 		unit = "us";
312 		in *= 1e6;
313 	} else if (in >= 1e-9 ){
314 		unit = "ns";
315 		in *= 1e9;
316 	} else {
317 		unit = "ps";
318 		if (in < 1e-13)
319 			tiny = 1;
320 		in *= 1e12;
321 	}
322 
323 	snprintf(outbuf, LEN,
324 	    tiny ? "%s%lf %s" : "%s%.3lf %s",
325 	    signbit == -1 ? "-" : "", in, unit);
326 
327 	return outbuf;
328 }
329 
330 int
331 cmdsensors(const char *cmd, char *args)
332 {
333 	if (prefix(cmd, "type")) {
334 		const char *t;
335 		int i, has_type = 0;
336 
337 		for (i = 0; i < SENSOR_MAX_TYPES; ++i)
338 			sensors_enabled[i] = 0;
339 
340 		while ((t = strsep(&args, " ")) != NULL) {
341 			if (*t == '\0')
342 				continue;
343 
344 			has_type = 1;
345 			for (i = 0; i < SENSOR_MAX_TYPES; ++i) {
346 				if (strcmp(t, sensor_type_s[i]) == 0) {
347 					sensors_enabled[i] = 1;
348 					break;
349 				}
350 			}
351 		}
352 
353 		if (!has_type) {
354 			for (i = 0; i < SENSOR_MAX_TYPES; ++i)
355 				sensors_enabled[i] = 1;
356 		}
357 	} else if (prefix(cmd, "match")) {
358 		const char *xname;
359 
360 		sensordev_xname_cnt = 0;
361 		while ((xname = strsep(&args, " ")) != NULL) {
362 			struct sensordev_xname *x;
363 			int xname_len, cp_len;
364 
365 			xname_len = strlen(xname);
366 			if (xname_len == 0)
367 				continue;
368 
369 			x = &sensordev_xname[sensordev_xname_cnt];
370 			x->flags = 0;
371 
372 			if (xname[xname_len - 1] == '*') {
373 				--xname_len;
374 				if (xname_len == 0)
375 					continue;
376 				x->flags |= XNAME_FLAG_WILDCARD;
377 			}
378 			cp_len = xname_len;
379 			if (cp_len >= (int)sizeof(x->xname))
380 				cp_len = sizeof(x->xname) - 1;
381 
382 			memcpy(x->xname, xname, cp_len);
383 			x->xname[cp_len] = '\0';
384 			x->xname_len = strlen(x->xname);
385 
386 			sensordev_xname_cnt++;
387 			if (sensordev_xname_cnt == XNAME_MAX)
388 				break;
389 		}
390 	}
391 
392 	wclear(wnd);
393 	labelsensors();
394 	refresh();
395 	return (1);
396 }
397