envstat.c revision 1.9 1 /* $NetBSD: envstat.c,v 1.9 2003/01/05 01:56:42 chris 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.9 2003/01/05 01:56:42 chris 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(1);
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 [-c] [-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