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