xref: /netbsd-src/usr.sbin/envstat/envstat.c (revision b8c616269f5ebf18ab2e35cb8099d683130a177c)
1 /*	$NetBSD: envstat.c,v 1.11 2003/01/11 23:44:47 christos Exp $ */
2 
3 /*-
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Bill Squier.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: envstat.c,v 1.11 2003/01/11 23:44:47 christos Exp $");
42 #endif
43 
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <err.h>
50 #include <paths.h>
51 
52 #include <sys/envsys.h>
53 #include <sys/ioctl.h>
54 
55 const char E_CANTENUM[] = "cannot enumerate sensors";
56 
57 int main(int, char **);
58 void listsensors(envsys_basic_info_t *, int);
59 int numsensors(int);
60 int fillsensors(int, envsys_tre_data_t *, envsys_basic_info_t *, int);
61 int longestname(envsys_basic_info_t *, int);
62 int marksensors(envsys_basic_info_t *, int *, char *, int);
63 int strtosnum(envsys_basic_info_t *, const char *, int);
64 void header(unsigned, int, envsys_basic_info_t *, const int * const, int);
65 void values(unsigned, int, envsys_tre_data_t *, const int * const, int);
66 void usage(void);
67 
68 int rflag = 0;
69 
70 static const char *unit_str[] = {"degC", "RPM", "VAC", "V", "Ohms", "W",
71 				 "A", "Wh", "Ah", "bool", "Unk"};
72 
73 int
74 main(int argc, char **argv)
75 {
76 	int c, fd, ns, ls, celsius;
77 	unsigned int interval, width, headrep, headcnt;
78 	envsys_tre_data_t *etds;
79 	envsys_basic_info_t *ebis;
80 	int *cetds;
81 	char *sensors;
82 	const char *dev;
83 
84 	fd = -1;
85 	ls = 0;
86 	celsius = 1;
87 	interval = 0;
88 	width = 0;
89 	sensors = NULL;
90 	headrep = 22;
91 
92 	while ((c = getopt(argc, argv, "cfi:ln:rs:w:r")) != -1) {
93 		switch(c) {
94 		case 'r':
95 			rflag= 1;
96 			break;
97 		case 'i':	/* wait time between displays */
98 			interval = atoi(optarg);
99 			break;
100 		case 'w':	/* minimum column width */
101 			width = atoi(optarg);
102 			break;
103 		case 'l':	/* list sensor names */
104 			ls = 1;
105 			break;
106 		case 'n':	/* repeat header every headrep lines */
107 			headrep = atoi(optarg);
108 			break;
109 		case 's':	/* restrict display to named sensors */
110 			sensors = (char *)malloc(strlen(optarg) + 1);
111 			if (sensors == NULL)
112 				exit(1);
113 			strcpy(sensors, optarg);
114 			break;
115 		case 'f':	/* display temp in degF */
116 			celsius = 0;
117 			break;
118 		case '?':
119 		default:
120 			usage();
121 			/* NOTREACHED */
122 		}
123 	}
124 
125 	if (optind < argc)
126 		dev = argv[optind];
127 	else
128 		dev = _PATH_SYSMON;
129 
130 	if ((fd = open(dev, O_RDONLY)) == -1)
131 		err(1, "unable to open %s", dev);
132 
133 	/*
134 	 * Determine number of sensors, allocate and fill arrays with
135 	 * initial information.  Determine column width
136 	 */
137 	if ((ns = numsensors(fd)) <= 0)
138 		errx(1, E_CANTENUM);
139 
140 	cetds = (int *)malloc(ns * sizeof(int));
141 	etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t));
142 	ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t));
143 
144 	if ((cetds == NULL) || (etds == NULL) || (ebis == NULL))
145 		errx(1, "cannot allocate memory");
146 
147 	if (fillsensors(fd, etds, ebis, ns) == -1)
148 		errx(1, E_CANTENUM);
149 
150 	if (ls) {
151 		listsensors(ebis, ns);
152 		exit(0);
153 	}
154 
155 
156 #define MAX(x, y)  (((x) > (y)) ? (x) : (y))
157 	if (!width) {
158 		width = longestname(ebis, ns);
159 		width = MAX(((79 - ns) / ns), width);
160 	}
161 
162 	/* Mark which sensor numbers are to be displayed */
163 	if (marksensors(ebis, cetds, sensors, ns) == -1)
164 		exit(1);
165 
166 	if (rflag) {
167 		int i;
168 
169 		for (i = 0 ; i < ns ; i++) {
170 			if (ebis[i].units == ENVSYS_INDICATOR) {
171 				if (etds[i].cur.data_s) {
172 					printf("%*.*s\n",
173 					    (int)width,
174 					    (int)width,
175 					    ebis[i].desc);
176 				}
177 				continue;
178 			}
179 
180 			if (ebis[i].units == ENVSYS_INTEGER) {
181 				printf("%*.*s:", (int)width, (int)width,
182 				       ebis[i].desc);
183 				printf(" %10d\n", etds[i].cur.data_s);
184 				continue;
185 			}
186 
187 			printf("%*.*s:", (int)width, (int)width, ebis[i].desc);
188 			/* different units need some magic */
189 			switch (ebis[i].units)
190 			{
191 			     	case ENVSYS_INDICATOR:
192 					printf(" %10s", etds[i].cur.data_us ?
193 					    "ON" : "OFF");
194 					break;
195 				case ENVSYS_STEMP:
196 					{
197 					     	double temp =
198 						    (etds[i].cur.data_s / 1000000.0)
199 						    - 273.15;
200 						if (celsius)
201 							printf(" %10.3f degC",
202 							    temp);
203 						else
204 						{
205 							temp = (9.0 / 5.0) * temp + 32.0;
206 							printf(" %10.3f degF",
207 							    temp);
208 						}
209 					}
210 					break;
211 				case ENVSYS_SFANRPM:
212 					printf(" %10u RPM",
213 					    etds[i].cur.data_us);
214 					break;
215 				default:
216 					printf(" %10.3f %s",
217 					    etds[i].cur.data_s / 1000000.0,
218 					    unit_str[ebis[i].units]);
219 					break;
220 			}
221 			if (etds[i].validflags & ENVSYS_FFRACVALID) {
222 				printf(" (%5.2f%%)",
223 				    (etds[i].cur.data_s * 100.0) /
224 				    etds[i].max.data_s);
225 			}
226 
227 			printf("\n");
228 		}
229 		exit(0);
230 	}
231 
232 
233 
234 	/* If we didn't specify an interval value, print the sensors once */
235 	if (!interval) {
236 		if (headrep)
237 			header(width, celsius, ebis, cetds, ns);
238 		values(width, celsius, etds, cetds, ns);
239 
240 		exit (0);
241 	}
242 
243 	headcnt = 0;
244 	if (headrep)
245 		header(width, celsius, ebis, cetds, ns);
246 
247         for (;;) {
248 		values(width, celsius, etds, cetds, ns);
249 		if (headrep && (++headcnt == headrep)) {
250 			headcnt = 0;
251 			header(width, celsius, ebis, cetds, ns);
252 		}
253 
254 		sleep(interval);
255 
256 		if (fillsensors(fd, etds, ebis, ns) == -1)
257 			errx(1, E_CANTENUM);
258 	}
259 
260 	/* NOTREACHED */
261 	return (0);
262 }
263 
264 
265 /*
266  * pre:  cetds[i] != 0 iff sensor i should appear in the output
267  * post: a column header line is displayed on stdout
268  */
269 void
270 header(unsigned width, int celsius, envsys_basic_info_t *ebis,
271        const int * const cetds, int ns)
272 {
273 	int i;
274 	const char *s;
275 
276 	/* sensor names */
277 	for (i = 0; i < ns; ++i)
278 		if (cetds[i])
279 			printf(" %*.*s", (int)width, (int)width, ebis[i].desc);
280 
281 	printf("\n");
282 
283 	/* units */
284 	for (i = 0; i < ns; ++i)
285 		if (cetds[i]) {
286 			if ((ebis[i].units == ENVSYS_STEMP) &&
287 			    !celsius)
288 				s = "degF";
289 			else if (ebis[i].units >= ENVSYS_NSENSORS)
290 				s = unit_str[ENVSYS_NSENSORS];
291 			else
292 				s = unit_str[ebis[i].units];
293 
294 			printf(" %*.*s", (int)width, (int)width, s);
295 		}
296 	printf("\n");
297 }
298 
299 void
300 values(unsigned width, int celsius, envsys_tre_data_t *etds,
301        const int * const cetds, int ns)
302 {
303 	int i;
304 	double temp;
305 
306 	for (i = 0; i < ns; ++i)
307 		if (cetds[i]) {
308 
309 			/* * sensors without valid data */
310 			if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) {
311 				printf(" %*.*s", (int)width, (int)width, "*");
312 				continue;
313 			}
314 
315 			switch(etds[i].units) {
316 			case ENVSYS_INDICATOR:
317 				printf(" %*.*s", (int)width, (int)width,
318 				    etds[i].cur.data_us ? "ON" : "OFF");
319 				break;
320 			case ENVSYS_STEMP:
321 				temp = (etds[i].cur.data_us / 1000000.0) -
322 				    273.15;
323 				if (!celsius)
324 					temp = (9.0 / 5.0) * temp + 32.0;
325 				printf(" %*.2f", width, temp);
326 				break;
327 			case ENVSYS_SFANRPM:
328 				printf(" %*u", width, etds[i].cur.data_us);
329 				break;
330 			default:
331 				printf(" %*.2f", width, etds[i].cur.data_s /
332 				       1000000.0);
333 				break;
334 			}
335 		}
336 	printf("\n");
337 }
338 
339 
340 /*
341  * post: displays usage on stderr
342  */
343 void
344 usage(void)
345 {
346 
347 	fprintf(stderr, "Usage: %s [-cr] [-s s1,s2,...]", getprogname());
348 	fprintf(stderr, " [-i interval] [-n headrep] [-w width] [device]\n");
349 	fprintf(stderr, "       envstat -l [device]\n");
350 	exit(1);
351 }
352 
353 
354 /*
355  * post: a list of sensor names supported by the device is displayed on stdout
356  */
357 void
358 listsensors(envsys_basic_info_t *ebis, int ns)
359 {
360 	int i;
361 
362 	for (i = 0; i < ns; ++i)
363 		if (ebis[i].validflags & ENVSYS_FVALID)
364 			printf("%s\n", ebis[i].desc);
365 }
366 
367 
368 /*
369  * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
370  * post: returns the number of valid sensors provided by the device
371  *       or -1 on error
372  */
373 int
374 numsensors(int fd)
375 {
376 	int count = 0, valid = 1;
377 	envsys_tre_data_t etd;
378 	etd.sensor = 0;
379 
380 	while (valid) {
381 		if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1) {
382 			fprintf(stderr, E_CANTENUM);
383 			exit(1);
384 		}
385 
386 		valid = etd.validflags & ENVSYS_FVALID;
387 		if (valid)
388 			++count;
389 
390 		++etd.sensor;
391 	}
392 
393 	return count;
394 }
395 
396 /*
397  * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
398  *       && ns is the number of sensors
399  *       && etds and ebis are arrays of sufficient size
400  * post: returns 0 and etds and ebis arrays are filled with sensor info
401  *       or returns -1 on failure
402  */
403 int
404 fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis, int ns)
405 {
406 	int i;
407 
408 	for (i = 0; i < ns; ++i) {
409 		ebis[i].sensor = i;
410 		if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1)
411 			return -1;
412 
413 		etds[i].sensor = i;
414 		if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1)
415 			return -1;
416 	}
417 
418 	return 0;
419 }
420 
421 
422 /*
423  * post: returns the strlen() of the longest sensor name
424  */
425 int
426 longestname(envsys_basic_info_t *ebis, int ns)
427 {
428 	int i, maxlen, cur;
429 
430 	maxlen = 0;
431 
432 	for (i = 0; i < ns; ++i) {
433 		cur = strlen(ebis[i].desc);
434 		if (cur > maxlen)
435 			maxlen = cur;
436 	}
437 
438 	return maxlen;
439 }
440 
441 /*
442  * post: returns 0 and cetds[i] != 0 iff sensor i should appear in the output
443  *       or returns -1
444  */
445 int
446 marksensors(envsys_basic_info_t *ebis, int *cetds, char *sensors, int ns)
447 {
448 	int i;
449 	char *s;
450 
451 	if (sensors == NULL) {
452 		/* No sensors specified, include them all */
453 		for (i = 0; i < ns; ++i)
454 			cetds[i] = 1;
455 
456 		return 0;
457 	}
458 
459 	/* Assume no sensors in display */
460 	memset(cetds, 0, ns * sizeof(int));
461 
462 	s = strtok(sensors, ",");
463 	while (s != NULL) {
464 		if ((i = strtosnum(ebis, s, ns)) != -1)
465 			cetds[i] = 1;
466 		else {
467 			fprintf(stderr, "envstat: unknown sensor %s\n", s);
468 			return (-1);
469 		}
470 
471 		s = strtok(NULL, ",");
472 	}
473 
474 	/* Check if we have at least one sensor selected for output */
475 	for (i = 0; i < ns; ++i)
476 		if (cetds[i] == 1)
477 			return (0);
478 
479 	fprintf(stderr, "envstat: no sensors selected for display\n");
480 	return (-1);
481 }
482 
483 
484 /*
485  * returns -1 if s is not a valid sensor name for the device
486  *       or the sensor number of a sensor which has that name
487  */
488 int
489 strtosnum(envsys_basic_info_t *ebis, const char *s, int ns)
490 {
491 	int i;
492 
493 	for (i = 0; i < ns; ++i) {
494 		if((ebis[i].validflags & ENVSYS_FVALID) &&
495 		   !strcmp(s, ebis[i].desc))
496 			return ebis[i].sensor;
497 	}
498 
499 	return -1;
500 }
501