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