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