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