xref: /netbsd-src/usr.sbin/envstat/envstat.c (revision d20841bb642898112fe68f0ad3f7b26dddf56f07)
1 /*	$NetBSD: envstat.c,v 1.17 2004/02/02 10:36:19 soren 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.17 2004/02/02 10:36:19 soren 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 #define ENVSYSUNITNAMES
53 #include <sys/envsys.h>
54 #include <sys/ioctl.h>
55 
56 int main(int, char **);
57 void listsensors(envsys_basic_info_t *, size_t);
58 size_t numsensors(int);
59 void fillsensors(int, envsys_tre_data_t *, envsys_basic_info_t *, size_t);
60 size_t longestname(envsys_basic_info_t *, size_t);
61 int marksensors(envsys_basic_info_t *, int *, char *, size_t);
62 int strtosnum(envsys_basic_info_t *, const char *, size_t);
63 void header(size_t, int, envsys_basic_info_t *, const int * const, size_t);
64 void values(size_t, int, envsys_tre_data_t *, const int * const, size_t);
65 void usage(void);
66 
67 int rflag = 0;
68 
69 int
70 main(int argc, char **argv)
71 {
72 	int c, fd, ls, celsius;
73 	size_t ns;
74 	size_t interval, width, headrep, headcnt;
75 	envsys_tre_data_t *etds;
76 	envsys_basic_info_t *ebis;
77 	int *cetds;
78 	char *sensors;
79 	const char *dev;
80 
81 	fd = -1;
82 	ls = 0;
83 	celsius = 1;
84 	interval = 0;
85 	width = 0;
86 	sensors = NULL;
87 	headrep = 22;
88 
89 	while ((c = getopt(argc, argv, "cfi:ln:rs:w:r")) != -1) {
90 		switch(c) {
91 		case 'r':
92 			rflag= 1;
93 			break;
94 		case 'i':	/* wait time between displays */
95 			interval = atoi(optarg);
96 			break;
97 		case 'w':	/* minimum column width */
98 			width = atoi(optarg);
99 			break;
100 		case 'l':	/* list sensor names */
101 			ls = 1;
102 			break;
103 		case 'n':	/* repeat header every headrep lines */
104 			headrep = atoi(optarg);
105 			break;
106 		case 's':	/* restrict display to named sensors */
107 			sensors = (char *)malloc(strlen(optarg) + 1);
108 			if (sensors == NULL)
109 				exit(1);
110 			strcpy(sensors, optarg);
111 			break;
112 		case 'f':	/* display temp in degF */
113 			celsius = 0;
114 			break;
115 		case '?':
116 		default:
117 			usage();
118 			/* NOTREACHED */
119 		}
120 	}
121 
122 	if (optind < argc)
123 		dev = argv[optind];
124 	else
125 		dev = _PATH_SYSMON;
126 
127 	if ((fd = open(dev, O_RDONLY)) == -1)
128 		err(1, "unable to open %s", dev);
129 
130 	/*
131 	 * Determine number of sensors, allocate and fill arrays with
132 	 * initial information.  Determine column width
133 	 */
134 	if ((ns = numsensors(fd)) == 0)
135 		errx(1, "No sensors found");
136 
137 	cetds = (int *)malloc(ns * sizeof(int));
138 	etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t));
139 	ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t));
140 
141 	if ((cetds == NULL) || (etds == NULL) || (ebis == NULL))
142 		err(1, "Out of memory");
143 
144 	fillsensors(fd, etds, ebis, ns);
145 
146 	if (ls) {
147 		listsensors(ebis, ns);
148 		exit(0);
149 	}
150 
151 
152 #define MAX(x, y)  (((x) > (y)) ? (x) : (y))
153 	if (!width) {
154 		width = longestname(ebis, ns);
155 		width = MAX(((79 - ns) / ns), width);
156 	}
157 
158 	/* Mark which sensor numbers are to be displayed */
159 	if (marksensors(ebis, cetds, sensors, ns) == -1)
160 		exit(1);
161 
162 	if (rflag) {
163 		int i;
164 
165 		for (i = 0 ; i < ns ; i++) {
166 			if ((etds[i].validflags & ENVSYS_FCURVALID) == 0)
167 				continue;
168 
169 			if (ebis[i].units == ENVSYS_INDICATOR &&
170 			    etds[i].cur.data_s == 0)
171 				continue;
172 
173 			printf("%*.*s", (int)width, (int)width, ebis[i].desc);
174 			/* different units need some magic */
175 			switch (ebis[i].units)
176 			{
177 			case ENVSYS_INDICATOR:
178 				break;
179 			case ENVSYS_INTEGER:
180 				printf(": %10d", etds[i].cur.data_s);
181 				break;
182 			case ENVSYS_STEMP: {
183 			     	double temp = (etds[i].cur.data_s / 1000000.0)
184 				    - 273.15;
185 				if (celsius)
186 					printf(": %10.3f degC", temp);
187 				else {
188 					temp = (9.0 / 5.0) * temp + 32.0;
189 					printf(": %10.3f degF", temp);
190 				}
191 				break;
192 			}
193 			case ENVSYS_SFANRPM:
194 				printf(": %10u RPM", etds[i].cur.data_us);
195 				break;
196 			default:
197 				printf(": %10.3f %s",
198 				    etds[i].cur.data_s / 1000000.0,
199 				    envsysunitnames[ebis[i].units]);
200 				break;
201 			}
202 
203 			if (etds[i].validflags & ENVSYS_FFRACVALID) {
204 				printf(" (%5.2f%%)",
205 				    (etds[i].cur.data_s * 100.0) /
206 				    etds[i].max.data_s);
207 			}
208 
209 			printf("\n");
210 		}
211 		exit(0);
212 	}
213 
214 
215 
216 	/* If we didn't specify an interval value, print the sensors once */
217 	if (!interval) {
218 		if (headrep)
219 			header(width, celsius, ebis, cetds, ns);
220 		values(width, celsius, etds, cetds, ns);
221 
222 		exit (0);
223 	}
224 
225 	headcnt = 0;
226 	if (headrep)
227 		header(width, celsius, ebis, cetds, ns);
228 
229         for (;;) {
230 		values(width, celsius, etds, cetds, ns);
231 		if (headrep && (++headcnt == headrep)) {
232 			headcnt = 0;
233 			header(width, celsius, ebis, cetds, ns);
234 		}
235 
236 		sleep(interval);
237 
238 		fillsensors(fd, etds, ebis, ns);
239 	}
240 
241 	/* NOTREACHED */
242 	return (0);
243 }
244 
245 
246 /*
247  * pre:  cetds[i] != 0 iff sensor i should appear in the output
248  * post: a column header line is displayed on stdout
249  */
250 void
251 header(size_t width, int celsius, envsys_basic_info_t *ebis,
252        const int * const cetds, size_t ns)
253 {
254 	int i;
255 	const char *s;
256 
257 	/* sensor names */
258 	for (i = 0; i < ns; ++i)
259 		if (cetds[i])
260 			printf(" %*.*s", (int)width, (int)width, ebis[i].desc);
261 
262 	printf("\n");
263 
264 	/* units */
265 	for (i = 0; i < ns; ++i)
266 		if (cetds[i]) {
267 			if ((ebis[i].units == ENVSYS_STEMP) &&
268 			    !celsius)
269 				s = "degF";
270 			else if (ebis[i].units >= ENVSYS_NSENSORS)
271 				s = envsysunitnames[ENVSYS_NSENSORS];
272 			else
273 				s = envsysunitnames[ebis[i].units];
274 
275 			printf(" %*.*s", (int)width, (int)width, s);
276 		}
277 	printf("\n");
278 }
279 
280 void
281 values(size_t width, int celsius, envsys_tre_data_t *etds,
282        const int * const cetds, size_t ns)
283 {
284 	int i;
285 	double temp;
286 
287 	for (i = 0; i < ns; ++i)
288 		if (cetds[i]) {
289 
290 			/* * sensors without valid data */
291 			if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) {
292 				printf(" %*.*s", (int)width, (int)width, "*");
293 				continue;
294 			}
295 
296 			switch(etds[i].units) {
297 			case ENVSYS_INDICATOR:
298 				printf(" %*.*s", (int)width, (int)width,
299 				    etds[i].cur.data_us ? "ON" : "OFF");
300 				break;
301 			case ENVSYS_STEMP:
302 				temp = (etds[i].cur.data_us / 1000000.0) -
303 				    273.15;
304 				if (!celsius)
305 					temp = (9.0 / 5.0) * temp + 32.0;
306 				printf(" %*.2f", (int)width, temp);
307 				break;
308 			case ENVSYS_SFANRPM:
309 				printf(" %*u", (int)width, etds[i].cur.data_us);
310 				break;
311 			default:
312 				printf(" %*.2f", (int)width, etds[i].cur.data_s /
313 				       1000000.0);
314 				break;
315 			}
316 		}
317 	printf("\n");
318 }
319 
320 
321 /*
322  * post: displays usage on stderr
323  */
324 void
325 usage(void)
326 {
327 
328 	(void)fprintf(stderr, "usage: %s [-cr] [-s s1,s2,...]", getprogname());
329 	(void)fprintf(stderr, " [-i interval] [-n headrep] [-w width]");
330 	(void)fprintf(stderr, " [device]\n");
331 	(void)fprintf(stderr, "       %s -l [device]\n", getprogname());
332 	exit(1);
333 }
334 
335 
336 /*
337  * post: a list of sensor names supported by the device is displayed on stdout
338  */
339 void
340 listsensors(envsys_basic_info_t *ebis, size_t ns)
341 {
342 	int i;
343 
344 	for (i = 0; i < ns; ++i)
345 		if (ebis[i].validflags & ENVSYS_FVALID)
346 			printf("%s\n", ebis[i].desc);
347 }
348 
349 
350 /*
351  * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
352  * post: returns the number of valid sensors provided by the device
353  *       or -1 on error
354  */
355 size_t
356 numsensors(int fd)
357 {
358 	int count = 0, valid = 1;
359 	envsys_tre_data_t etd;
360 	etd.sensor = 0;
361 
362 	while (valid) {
363 		if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1)
364 			err(1, "Can't get sensor data");
365 
366 		valid = etd.validflags & ENVSYS_FVALID;
367 		if (valid)
368 			++count;
369 
370 		++etd.sensor;
371 	}
372 
373 	return count;
374 }
375 
376 /*
377  * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
378  *       && ns is the number of sensors
379  *       && etds and ebis are arrays of sufficient size
380  * post: returns 0 and etds and ebis arrays are filled with sensor info
381  *       or returns -1 on failure
382  */
383 void
384 fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis,
385     size_t ns)
386 {
387 	int i;
388 
389 	for (i = 0; i < ns; ++i) {
390 		ebis[i].sensor = i;
391 		if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1)
392 			err(1, "Can't get sensor info for sensor %d", i);
393 
394 		etds[i].sensor = i;
395 		if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1)
396 			err(1, "Can't get sensor data for sensor %d", i);
397 	}
398 }
399 
400 
401 /*
402  * post: returns the strlen() of the longest sensor name
403  */
404 size_t
405 longestname(envsys_basic_info_t *ebis, size_t ns)
406 {
407 	size_t i, maxlen, cur;
408 
409 	maxlen = 0;
410 
411 	for (i = 0; i < ns; ++i) {
412 		cur = strlen(ebis[i].desc);
413 		if (cur > maxlen)
414 			maxlen = cur;
415 	}
416 
417 	return maxlen;
418 }
419 
420 /*
421  * post: returns 0 and cetds[i] != 0 iff sensor i should appear in the output
422  *       or returns -1
423  */
424 int
425 marksensors(envsys_basic_info_t *ebis, int *cetds, char *sensors, size_t ns)
426 {
427 	size_t i;
428 	char *s;
429 
430 	if (sensors == NULL) {
431 		/* No sensors specified, include them all */
432 		for (i = 0; i < ns; ++i)
433 			cetds[i] = 1;
434 
435 		return 0;
436 	}
437 
438 	/* Assume no sensors in display */
439 	memset(cetds, 0, ns * sizeof(int));
440 
441 	s = strtok(sensors, ",");
442 	while (s != NULL) {
443 		int snum;
444 		if ((snum = strtosnum(ebis, s, ns)) != -1)
445 			cetds[snum] = 1;
446 		else {
447 			warnx("Unknown sensor %s", s);
448 			return (-1);
449 		}
450 
451 		s = strtok(NULL, ",");
452 	}
453 
454 	/* Check if we have at least one sensor selected for output */
455 	for (i = 0; i < ns; ++i)
456 		if (cetds[i] == 1)
457 			return (0);
458 
459 	warnx("No sensors selected for display");
460 	return (-1);
461 }
462 
463 
464 /*
465  * returns -1 if s is not a valid sensor name for the device
466  *       or the sensor number of a sensor which has that name
467  */
468 int
469 strtosnum(envsys_basic_info_t *ebis, const char *s, size_t ns)
470 {
471 	size_t i;
472 
473 	for (i = 0; i < ns; ++i) {
474 		if((ebis[i].validflags & ENVSYS_FVALID) &&
475 		   !strcmp(s, ebis[i].desc))
476 			return ebis[i].sensor;
477 	}
478 
479 	return -1;
480 }
481