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