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