envstat.c revision 1.82 1 /* $NetBSD: envstat.c,v 1.82 2010/11/05 13:52:42 pooka Exp $ */
2
3 /*-
4 * Copyright (c) 2007, 2008 Juan Romero Pardines.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: envstat.c,v 1.82 2010/11/05 13:52:42 pooka Exp $");
31 #endif /* not lint */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdbool.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <paths.h>
43 #include <syslog.h>
44 #include <sys/envsys.h>
45 #include <sys/ioctl.h>
46 #include <sys/types.h>
47 #include <sys/queue.h>
48 #include <prop/proplib.h>
49
50 #include "envstat.h"
51
52 #ifdef RUMP_ACTION
53 #include <rump/rump.h>
54 #include <rump/rumpclient.h>
55 #include <rump/rump_syscalls.h>
56 #endif
57
58 #define ENVSYS_DFLAG 0x00000001 /* list registered devices */
59 #define ENVSYS_FFLAG 0x00000002 /* show temp in farenheit */
60 #define ENVSYS_LFLAG 0x00000004 /* list sensors */
61 #define ENVSYS_XFLAG 0x00000008 /* externalize dictionary */
62 #define ENVSYS_IFLAG 0x00000010 /* skip invalid sensors */
63 #define ENVSYS_SFLAG 0x00000020 /* remove all properties set */
64 #define ENVSYS_TFLAG 0x00000040 /* make statistics */
65 #define ENVSYS_KFLAG 0x00000100 /* show temp in kelvin */
66
67 /* Sensors */
68 typedef struct envsys_sensor {
69 SIMPLEQ_ENTRY(envsys_sensor) entries;
70 int32_t cur_value;
71 int32_t max_value;
72 int32_t min_value;
73 int32_t avg_value;
74 int32_t critmin_value;
75 int32_t critmax_value;
76 int32_t warnmin_value;
77 int32_t warnmax_value;
78 char desc[ENVSYS_DESCLEN];
79 char type[ENVSYS_DESCLEN];
80 char drvstate[ENVSYS_DESCLEN];
81 char battcap[ENVSYS_DESCLEN];
82 char dvname[ENVSYS_DESCLEN];
83 bool invalid;
84 bool visible;
85 bool percentage;
86 } *sensor_t;
87
88 /* Sensor statistics */
89 typedef struct envsys_sensor_stats {
90 SIMPLEQ_ENTRY(envsys_sensor_stats) entries;
91 int32_t max;
92 int32_t min;
93 int32_t avg;
94 char desc[ENVSYS_DESCLEN];
95 } *sensor_stats_t;
96
97 /* Device properties */
98 typedef struct envsys_dvprops {
99 uint64_t refresh_timo;
100 /* more members could be added in the future */
101 } *dvprops_t;
102
103 /* A simple queue to manage all sensors */
104 static SIMPLEQ_HEAD(, envsys_sensor) sensors_list =
105 SIMPLEQ_HEAD_INITIALIZER(sensors_list);
106
107 /* A simple queue to manage statistics for all sensors */
108 static SIMPLEQ_HEAD(, envsys_sensor_stats) sensor_stats_list =
109 SIMPLEQ_HEAD_INITIALIZER(sensor_stats_list);
110
111 static unsigned int interval, flags, width;
112 static char *mydevname, *sensors;
113 static bool statistics;
114 static u_int header_passes;
115
116 static int parse_dictionary(int);
117 static int send_dictionary(FILE *);
118 static int find_sensors(prop_array_t, const char *, dvprops_t);
119 static void print_sensors(void);
120 static int check_sensors(char *);
121 static int usage(void);
122
123 static int sysmonfd; /* fd of /dev/sysmon */
124
125 /* sneak in between ioctl() */
126 #ifdef RUMP_ACTION
127 #include <sys/syscall.h>
128 int
129 ioctl(int fd, unsigned long request, ...)
130 {
131 va_list ap;
132 int rv;
133
134 va_start(ap, request);
135 if (fd == sysmonfd)
136 rv = rump_sys_ioctl(fd, request, va_arg(ap, void *));
137 else
138 rv = syscall(SYS_ioctl, fd, request, va_arg(ap, void *));
139 va_end(ap);
140
141 return rv;
142 }
143 #endif
144
145 int main(int argc, char **argv)
146 {
147 prop_dictionary_t dict;
148 int c, rval = 0;
149 char *endptr, *configfile = NULL;
150 FILE *cf;
151
152 #ifdef RUMP_ACTION
153 if (rumpclient_init() == -1)
154 err(1, "rumpclient init failed");
155 #endif
156
157 setprogname(argv[0]);
158
159 while ((c = getopt(argc, argv, "c:Dd:fIi:klrSs:Tw:Wx")) != -1) {
160 switch (c) {
161 case 'c': /* configuration file */
162 configfile = strdup(optarg);
163 if (configfile == NULL)
164 err(EXIT_FAILURE, "strdup");
165 break;
166 case 'D': /* list registered devices */
167 flags |= ENVSYS_DFLAG;
168 break;
169 case 'd': /* show sensors of a specific device */
170 mydevname = strdup(optarg);
171 if (mydevname == NULL)
172 err(EXIT_FAILURE, "strdup");
173 break;
174 case 'f': /* display temperature in Farenheit */
175 flags |= ENVSYS_FFLAG;
176 break;
177 case 'I': /* Skips invalid sensors */
178 flags |= ENVSYS_IFLAG;
179 break;
180 case 'i': /* wait time between intervals */
181 interval = (unsigned int)strtoul(optarg, &endptr, 10);
182 if (*endptr != '\0')
183 errx(EXIT_FAILURE, "bad interval '%s'", optarg);
184 break;
185 case 'k': /* display temperature in Kelvin */
186 flags |= ENVSYS_KFLAG;
187 break;
188 case 'l': /* list sensors */
189 flags |= ENVSYS_LFLAG;
190 break;
191 case 'r':
192 /*
193 * This flag is noop.. it's only here for
194 * compatibility with the old implementation.
195 */
196 break;
197 case 'S':
198 flags |= ENVSYS_SFLAG;
199 break;
200 case 's': /* only show specified sensors */
201 sensors = strdup(optarg);
202 if (sensors == NULL)
203 err(EXIT_FAILURE, "strdup");
204 break;
205 case 'T': /* make statistics */
206 flags |= ENVSYS_TFLAG;
207 break;
208 case 'w': /* width value for the lines */
209 width = (unsigned int)strtoul(optarg, &endptr, 10);
210 if (*endptr != '\0')
211 errx(EXIT_FAILURE, "bad width '%s'", optarg);
212 break;
213 case 'x': /* print the dictionary in raw format */
214 flags |= ENVSYS_XFLAG;
215 break;
216 case 'W': /* No longer used, retained for campatability */
217 break;
218 case '?':
219 default:
220 usage();
221 /* NOTREACHED */
222 }
223 }
224
225 argc -= optind;
226 argv += optind;
227
228 if (argc > 0)
229 usage();
230
231 /* Check if we want to make statistics */
232 if (flags & ENVSYS_TFLAG) {
233 if (!interval)
234 errx(EXIT_FAILURE,
235 "-T cannot be used without an interval (-i)");
236 else
237 statistics = true;
238 }
239
240 if (mydevname && sensors)
241 errx(EXIT_FAILURE, "-d flag cannot be used with -s");
242
243 /* Open the device in ro mode */
244 if ((sysmonfd = open(_PATH_SYSMON, O_RDONLY)) == -1)
245 err(EXIT_FAILURE, "%s", _PATH_SYSMON);
246
247 /* Print dictionary in raw mode */
248 if (flags & ENVSYS_XFLAG) {
249 rval = prop_dictionary_recv_ioctl(sysmonfd,
250 ENVSYS_GETDICTIONARY,
251 &dict);
252 if (rval)
253 errx(EXIT_FAILURE, "%s", strerror(rval));
254
255 config_dict_dump(dict);
256
257 /* Remove all properties set in dictionary */
258 } else if (flags & ENVSYS_SFLAG) {
259 /* Close the ro descriptor */
260 (void)close(sysmonfd);
261
262 /* open the fd in rw mode */
263 if ((sysmonfd = open(_PATH_SYSMON, O_RDWR)) == -1)
264 err(EXIT_FAILURE, "%s", _PATH_SYSMON);
265
266 dict = prop_dictionary_create();
267 if (!dict)
268 err(EXIT_FAILURE, "prop_dictionary_create");
269
270 rval = prop_dictionary_set_bool(dict,
271 "envsys-remove-props",
272 true);
273 if (!rval)
274 err(EXIT_FAILURE, "prop_dict_set_bool");
275
276 /* send the dictionary to the kernel now */
277 rval = prop_dictionary_send_ioctl(dict, sysmonfd,
278 ENVSYS_REMOVEPROPS);
279 if (rval)
280 warnx("%s", strerror(rval));
281
282 /* Set properties in dictionary */
283 } else if (configfile) {
284 /*
285 * Parse the configuration file.
286 */
287 if ((cf = fopen(configfile, "r")) == NULL) {
288 syslog(LOG_ERR, "fopen failed: %s", strerror(errno));
289 errx(EXIT_FAILURE, "%s", strerror(errno));
290 }
291
292 rval = send_dictionary(cf);
293 (void)fclose(cf);
294
295 /* Show sensors with interval */
296 } else if (interval) {
297 for (;;) {
298 rval = parse_dictionary(sysmonfd);
299 if (rval)
300 break;
301
302 (void)fflush(stdout);
303 (void)sleep(interval);
304 }
305 /* Show sensors without interval */
306 } else {
307 rval = parse_dictionary(sysmonfd);
308 }
309
310 if (sensors)
311 free(sensors);
312 if (mydevname)
313 free(mydevname);
314 (void)close(sysmonfd);
315
316 return rval ? EXIT_FAILURE : EXIT_SUCCESS;
317 }
318
319 static int
320 send_dictionary(FILE *cf)
321 {
322 prop_dictionary_t kdict, udict;
323 int error = 0;
324
325 /* Retrieve dictionary from kernel */
326 error = prop_dictionary_recv_ioctl(sysmonfd,
327 ENVSYS_GETDICTIONARY, &kdict);
328 if (error)
329 return error;
330
331 config_parse(cf, kdict);
332
333 /*
334 * Dictionary built by the parser from the configuration file.
335 */
336 udict = config_dict_parsed();
337
338 /*
339 * Close the read only descriptor and open a new one read write.
340 */
341 (void)close(sysmonfd);
342 if ((sysmonfd = open(_PATH_SYSMON, O_RDWR)) == -1) {
343 error = errno;
344 warn("%s", _PATH_SYSMON);
345 return error;
346 }
347
348 /*
349 * Send our sensor properties dictionary to the kernel then.
350 */
351 error = prop_dictionary_send_ioctl(udict,
352 sysmonfd, ENVSYS_SETDICTIONARY);
353 if (error)
354 warnx("%s", strerror(error));
355
356 prop_object_release(udict);
357 return error;
358 }
359
360 static sensor_stats_t
361 find_stats_sensor(const char *desc)
362 {
363 sensor_stats_t stats;
364
365 /*
366 * If we matched a sensor by its description return it, otherwise
367 * allocate a new one.
368 */
369 SIMPLEQ_FOREACH(stats, &sensor_stats_list, entries)
370 if (strcmp(stats->desc, desc) == 0)
371 return stats;
372
373 stats = calloc(1, sizeof(*stats));
374 if (stats == NULL)
375 return NULL;
376
377 (void)strlcpy(stats->desc, desc, sizeof(stats->desc));
378 SIMPLEQ_INSERT_TAIL(&sensor_stats_list, stats, entries);
379
380 return stats;
381 }
382
383 static int
384 parse_dictionary(int fd)
385 {
386 sensor_t sensor = NULL;
387 dvprops_t edp = NULL;
388 prop_array_t array;
389 prop_dictionary_t dict;
390 prop_object_iterator_t iter;
391 prop_object_t obj;
392 const char *dnp = NULL;
393 int rval = 0;
394
395 /* receive dictionary from kernel */
396 rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
397 if (rval)
398 return rval;
399
400 /* No drivers registered? */
401 if (prop_dictionary_count(dict) == 0) {
402 warnx("no drivers registered");
403 goto out;
404 }
405
406 if (mydevname) {
407 /* -d flag specified, print sensors only for this device */
408 obj = prop_dictionary_get(dict, mydevname);
409 if (prop_object_type(obj) != PROP_TYPE_ARRAY) {
410 warnx("unknown device `%s'", mydevname);
411 rval = EINVAL;
412 goto out;
413 }
414
415 rval = find_sensors(obj, mydevname, NULL);
416 if (rval)
417 goto out;
418
419 } else {
420 /* print sensors for all devices registered */
421 iter = prop_dictionary_iterator(dict);
422 if (iter == NULL) {
423 rval = EINVAL;
424 goto out;
425 }
426
427 /* iterate over the dictionary returned by the kernel */
428 while ((obj = prop_object_iterator_next(iter)) != NULL) {
429 array = prop_dictionary_get_keysym(dict, obj);
430 if (prop_object_type(array) != PROP_TYPE_ARRAY) {
431 warnx("no sensors found");
432 rval = EINVAL;
433 goto out;
434 }
435
436 edp = calloc(1, sizeof(*edp));
437 if (!edp) {
438 rval = ENOMEM;
439 goto out;
440 }
441
442 dnp = prop_dictionary_keysym_cstring_nocopy(obj);
443 rval = find_sensors(array, dnp, edp);
444 if (rval)
445 goto out;
446
447 if (((flags & ENVSYS_LFLAG) == 0) &&
448 (flags & ENVSYS_DFLAG)) {
449 (void)printf("%s (checking events every ",
450 dnp);
451 if (edp->refresh_timo == 1)
452 (void)printf("second)\n");
453 else
454 (void)printf("%d seconds)\n",
455 (int)edp->refresh_timo);
456 }
457
458 free(edp);
459 edp = NULL;
460 }
461 prop_object_iterator_release(iter);
462 }
463
464 /* print sensors now */
465 if (sensors) {
466 char *str = strdup(sensors);
467 if (!str) {
468 rval = ENOMEM;
469 goto out;
470 }
471 rval = check_sensors(str);
472 free(str);
473 }
474 if ((flags & ENVSYS_LFLAG) == 0 && (flags & ENVSYS_DFLAG) == 0)
475 print_sensors();
476 if (interval)
477 (void)printf("\n");
478
479 out:
480 while ((sensor = SIMPLEQ_FIRST(&sensors_list))) {
481 SIMPLEQ_REMOVE_HEAD(&sensors_list, entries);
482 free(sensor);
483 }
484 if (edp)
485 free(edp);
486 prop_object_release(dict);
487 return rval;
488 }
489
490 static int
491 find_sensors(prop_array_t array, const char *dvname, dvprops_t edp)
492 {
493 prop_object_iterator_t iter;
494 prop_object_t obj, obj1, obj2;
495 prop_string_t state, desc = NULL;
496 sensor_t sensor = NULL;
497 sensor_stats_t stats = NULL;
498
499 iter = prop_array_iterator(array);
500 if (!iter)
501 return ENOMEM;
502
503 /* iterate over the array of dictionaries */
504 while ((obj = prop_object_iterator_next(iter)) != NULL) {
505 /* get the refresh-timeout property */
506 obj2 = prop_dictionary_get(obj, "device-properties");
507 if (obj2) {
508 if (!edp)
509 continue;
510 if (!prop_dictionary_get_uint64(obj2,
511 "refresh-timeout",
512 &edp->refresh_timo))
513 continue;
514 }
515
516 /* new sensor coming */
517 sensor = calloc(1, sizeof(*sensor));
518 if (sensor == NULL) {
519 prop_object_iterator_release(iter);
520 return ENOMEM;
521 }
522
523 /* copy device name */
524 (void)strlcpy(sensor->dvname, dvname, sizeof(sensor->dvname));
525
526 /* description string */
527 desc = prop_dictionary_get(obj, "description");
528 if (desc) {
529 /* copy description */
530 (void)strlcpy(sensor->desc,
531 prop_string_cstring_nocopy(desc),
532 sizeof(sensor->desc));
533 } else {
534 free(sensor);
535 continue;
536 }
537
538 /* type string */
539 obj1 = prop_dictionary_get(obj, "type");
540 if (obj1) {
541 /* copy type */
542 (void)strlcpy(sensor->type,
543 prop_string_cstring_nocopy(obj1),
544 sizeof(sensor->type));
545 } else {
546 free(sensor);
547 continue;
548 }
549
550 /* check sensor's state */
551 state = prop_dictionary_get(obj, "state");
552
553 /* mark sensors with invalid/unknown state */
554 if ((prop_string_equals_cstring(state, "invalid") ||
555 prop_string_equals_cstring(state, "unknown")))
556 sensor->invalid = true;
557
558 /* get current drive state string */
559 obj1 = prop_dictionary_get(obj, "drive-state");
560 if (obj1) {
561 (void)strlcpy(sensor->drvstate,
562 prop_string_cstring_nocopy(obj1),
563 sizeof(sensor->drvstate));
564 }
565
566 /* get current battery capacity string */
567 obj1 = prop_dictionary_get(obj, "battery-capacity");
568 if (obj1) {
569 (void)strlcpy(sensor->battcap,
570 prop_string_cstring_nocopy(obj1),
571 sizeof(sensor->battcap));
572 }
573
574 /* get current value */
575 obj1 = prop_dictionary_get(obj, "cur-value");
576 if (obj1)
577 sensor->cur_value = prop_number_integer_value(obj1);
578
579 /* get max value */
580 obj1 = prop_dictionary_get(obj, "max-value");
581 if (obj1)
582 sensor->max_value = prop_number_integer_value(obj1);
583
584 /* get min value */
585 obj1 = prop_dictionary_get(obj, "min-value");
586 if (obj1)
587 sensor->min_value = prop_number_integer_value(obj1);
588
589 /* get avg value */
590 obj1 = prop_dictionary_get(obj, "avg-value");
591 if (obj1)
592 sensor->avg_value = prop_number_integer_value(obj1);
593
594 /* get percentage flag */
595 obj1 = prop_dictionary_get(obj, "want-percentage");
596 if (obj1)
597 sensor->percentage = prop_bool_true(obj1);
598
599 /* get critical max value if available */
600 obj1 = prop_dictionary_get(obj, "critical-max");
601 if (obj1)
602 sensor->critmax_value = prop_number_integer_value(obj1);
603
604 /* get maximum capacity value if available */
605 obj1 = prop_dictionary_get(obj, "maximum-capacity");
606 if (obj1)
607 sensor->critmax_value = prop_number_integer_value(obj1);
608
609 /* get critical min value if available */
610 obj1 = prop_dictionary_get(obj, "critical-min");
611 if (obj1)
612 sensor->critmin_value = prop_number_integer_value(obj1);
613
614 /* get critical capacity value if available */
615 obj1 = prop_dictionary_get(obj, "critical-capacity");
616 if (obj1)
617 sensor->critmin_value = prop_number_integer_value(obj1);
618
619 /* get warning max value if available */
620 obj1 = prop_dictionary_get(obj, "warning-max");
621 if (obj1)
622 sensor->warnmax_value = prop_number_integer_value(obj1);
623
624 /* get high capacity value if available */
625 obj1 = prop_dictionary_get(obj, "high-capacity");
626 if (obj1)
627 sensor->warnmax_value = prop_number_integer_value(obj1);
628
629 /* get warning min value if available */
630 obj1 = prop_dictionary_get(obj, "warning-min");
631 if (obj1)
632 sensor->warnmin_value = prop_number_integer_value(obj1);
633
634 /* get warning capacity value if available */
635 obj1 = prop_dictionary_get(obj, "warning-capacity");
636 if (obj1)
637 sensor->warnmin_value = prop_number_integer_value(obj1);
638
639 /* print sensor names if -l was given */
640 if (flags & ENVSYS_LFLAG) {
641 if (width)
642 (void)printf("%*s\n", width,
643 prop_string_cstring_nocopy(desc));
644 else
645 (void)printf("%s\n",
646 prop_string_cstring_nocopy(desc));
647 }
648
649 /* Add the sensor into the list */
650 SIMPLEQ_INSERT_TAIL(&sensors_list, sensor, entries);
651
652 /* Collect statistics if flag enabled */
653 if (statistics) {
654 /* ignore sensors not relevant for statistics */
655 if ((strcmp(sensor->type, "Indicator") == 0) ||
656 (strcmp(sensor->type, "Battery charge") == 0) ||
657 (strcmp(sensor->type, "Drive") == 0))
658 continue;
659
660 /* ignore invalid data */
661 if (sensor->invalid || !sensor->cur_value)
662 continue;
663
664 /* find or allocate a new statistics sensor */
665 stats = find_stats_sensor(sensor->desc);
666 if (stats == NULL) {
667 free(sensor);
668 prop_object_iterator_release(iter);
669 return ENOMEM;
670 }
671
672 /* collect data */
673 if (!stats->max)
674 stats->max = sensor->cur_value;
675 if (!stats->min)
676 stats->min = sensor->cur_value;
677
678 if (sensor->cur_value > stats->max)
679 stats->max = sensor->cur_value;
680
681 if (sensor->cur_value < stats->min)
682 stats->min = sensor->cur_value;
683
684 /* compute avg value */
685 if (stats->max && stats->min)
686 stats->avg =
687 (sensor->cur_value + stats->max +
688 stats->min) / 3;
689 }
690 }
691
692 /* free memory */
693 prop_object_iterator_release(iter);
694 return 0;
695 }
696
697 static int
698 check_sensors(char *str)
699 {
700 sensor_t sensor = NULL;
701 char *dvstring, *sstring, *p, *last;
702 bool sensor_found = false;
703
704 /*
705 * Parse device name and sensor description and find out
706 * if the sensor is valid.
707 */
708 for ((p = strtok_r(str, ",", &last)); p;
709 (p = strtok_r(NULL, ",", &last))) {
710 /* get device name */
711 dvstring = strtok(p, ":");
712 if (dvstring == NULL) {
713 warnx("missing device name");
714 return EINVAL;
715 }
716
717 /* get sensor description */
718 sstring = strtok(NULL, ":");
719 if (sstring == NULL) {
720 warnx("missing sensor description");
721 return EINVAL;
722 }
723
724 SIMPLEQ_FOREACH(sensor, &sensors_list, entries) {
725 /* skip until we match device */
726 if (strcmp(dvstring, sensor->dvname))
727 continue;
728 if (strcmp(sstring, sensor->desc) == 0) {
729 sensor->visible = true;
730 sensor_found = true;
731 break;
732 }
733 }
734 if (sensor_found == false) {
735 warnx("unknown sensor `%s' for device `%s'",
736 sstring, dvstring);
737 return EINVAL;
738 }
739 sensor_found = false;
740 }
741
742 /* check if all sensors were ok, and error out if not */
743 SIMPLEQ_FOREACH(sensor, &sensors_list, entries)
744 if (sensor->visible)
745 return 0;
746
747 warnx("no sensors selected to display");
748 return EINVAL;
749 }
750
751 static void
752 print_sensors(void)
753 {
754 sensor_t sensor;
755 sensor_stats_t stats = NULL;
756 size_t maxlen = 0, ilen;
757 double temp = 0;
758 const char *invalid = "N/A", *degrees, *tmpstr, *stype;
759 const char *a, *b, *c, *d, *e, *units;
760
761 tmpstr = stype = d = e = NULL;
762
763 /* find the longest description */
764 SIMPLEQ_FOREACH(sensor, &sensors_list, entries)
765 if (strlen(sensor->desc) > maxlen)
766 maxlen = strlen(sensor->desc);
767
768 if (width)
769 maxlen = width;
770
771 /*
772 * Print a header at the bottom only once showing different
773 * members if the statistics flag is set or not.
774 *
775 * As bonus if -s is set, only print this header every 10 iterations
776 * to avoid redundancy... like vmstat(1).
777 */
778
779 a = "Current";
780 units = "Unit";
781 if (statistics) {
782 b = "Max";
783 c = "Min";
784 d = "Avg";
785 } else {
786 b = "CritMax";
787 c = "WarnMax";
788 d = "WarnMin";
789 e = "CritMin";
790 }
791
792 if (!sensors || (!header_passes && sensors) ||
793 (header_passes == 10 && sensors)) {
794 if (statistics)
795 (void)printf("%s%*s %9s %8s %8s %8s %6s\n",
796 mydevname ? "" : " ", (int)maxlen,
797 "", a, b, c, d, units);
798 else
799 (void)printf("%s%*s %9s %8s %8s %8s %8s %4s\n",
800 mydevname ? "" : " ", (int)maxlen,
801 "", a, b, c, d, e, units);
802 if (sensors && header_passes == 10)
803 header_passes = 0;
804 }
805 if (sensors)
806 header_passes++;
807
808 /* print the sensors */
809 SIMPLEQ_FOREACH(sensor, &sensors_list, entries) {
810 /* skip sensors that were not marked as visible */
811 if (sensors && !sensor->visible)
812 continue;
813
814 /* skip invalid sensors if -I is set */
815 if ((flags & ENVSYS_IFLAG) && sensor->invalid)
816 continue;
817
818 /* print device name */
819 if (!mydevname) {
820 if (tmpstr == NULL || strcmp(tmpstr, sensor->dvname))
821 printf("[%s]\n", sensor->dvname);
822
823 tmpstr = sensor->dvname;
824 }
825
826 /* find out the statistics sensor */
827 if (statistics) {
828 stats = find_stats_sensor(sensor->desc);
829 if (stats == NULL) {
830 /* No statistics for this sensor */
831 continue;
832 }
833 }
834
835 /* print sensor description */
836 (void)printf("%s%*.*s", mydevname ? "" : " ", (int)maxlen,
837 (int)maxlen, sensor->desc);
838
839 /* print invalid string */
840 if (sensor->invalid) {
841 (void)printf(": %9s\n", invalid);
842 continue;
843 }
844
845 /*
846 * Indicator and Battery charge sensors.
847 */
848 if ((strcmp(sensor->type, "Indicator") == 0) ||
849 (strcmp(sensor->type, "Battery charge") == 0)) {
850
851 (void)printf(":%10s", sensor->cur_value ? "ON" : "OFF");
852
853 /* convert and print a temp value in degC, degF, or Kelvin */
854 #define PRINTTEMP(a) \
855 do { \
856 if (a) { \
857 temp = ((a) / 1000000.0); \
858 if (flags & ENVSYS_FFLAG) { \
859 temp = temp * (9.0 / 5.0) - 459.67; \
860 degrees = "degF"; \
861 } else if (flags & ENVSYS_KFLAG) { \
862 degrees = "K"; \
863 } else { \
864 temp = temp - 273.15; \
865 degrees = "degC"; \
866 } \
867 (void)printf("%*.3f ", (int)ilen, temp); \
868 ilen = 8; \
869 } else \
870 ilen += 9; \
871 } while (/* CONSTCOND */ 0)
872
873 /* temperatures */
874 } else if (strcmp(sensor->type, "Temperature") == 0) {
875
876 ilen = 10;
877 degrees = "";
878 (void)printf(":");
879 PRINTTEMP(sensor->cur_value);
880 stype = degrees;
881
882 ilen = 8;
883 if (statistics) {
884 /* show statistics if flag set */
885 PRINTTEMP(stats->max);
886 PRINTTEMP(stats->min);
887 PRINTTEMP(stats->avg);
888 ilen += 2;
889 } else {
890 PRINTTEMP(sensor->critmax_value);
891 PRINTTEMP(sensor->warnmax_value);
892 PRINTTEMP(sensor->warnmin_value);
893 PRINTTEMP(sensor->critmin_value);
894 }
895 (void)printf("%*s", (int)ilen - 4, stype);
896 #undef PRINTTEMP
897
898 /* fans */
899 } else if (strcmp(sensor->type, "Fan") == 0) {
900 stype = "RPM";
901
902 (void)printf(":%10u ", sensor->cur_value);
903
904 ilen = 8;
905 if (statistics) {
906 /* show statistics if flag set */
907 (void)printf("%8u %8u %8u ",
908 stats->max, stats->min, stats->avg);
909 ilen += 2;
910 } else {
911 if (sensor->critmax_value) {
912 (void)printf("%*u ", (int)ilen,
913 sensor->critmax_value);
914 ilen = 8;
915 } else
916 ilen += 9;
917
918 if (sensor->warnmax_value) {
919 (void)printf("%*u ", (int)ilen,
920 sensor->warnmax_value);
921 ilen = 8;
922 } else
923 ilen += 9;
924
925 if (sensor->warnmin_value) {
926 (void)printf("%*u ", (int)ilen,
927 sensor->warnmin_value);
928 ilen = 8;
929 } else
930 ilen += 9;
931
932 if (sensor->critmin_value) {
933 (void)printf( "%*u ", (int)ilen,
934 sensor->critmin_value);
935 ilen = 8;
936 } else
937 ilen += 9;
938
939 }
940
941 (void)printf("%*s", (int)ilen - 4, stype);
942
943 /* integers */
944 } else if (strcmp(sensor->type, "Integer") == 0) {
945
946 stype = "none";
947
948 (void)printf(":%10d ", sensor->cur_value);
949
950 ilen = 8;
951 if (statistics) {
952 /* show statistics if flag set */
953 (void)printf("%8u %8u %8u ",
954 stats->max, stats->min, stats->avg);
955 ilen += 2;
956 } else {
957 if (sensor->critmax_value) {
958 (void)printf("%*u ", (int)ilen,
959 sensor->critmax_value);
960 ilen = 8;
961 } else
962 ilen += 9;
963
964 if (sensor->warnmax_value) {
965 (void)printf("%*u ", (int)ilen,
966 sensor->warnmax_value);
967 ilen = 8;
968 } else
969 ilen += 9;
970
971 if (sensor->warnmin_value) {
972 (void)printf("%*u ", (int)ilen,
973 sensor->warnmin_value);
974 ilen = 8;
975 } else
976 ilen += 9;
977
978 if (sensor->critmin_value) {
979 (void)printf( "%*u ", (int)ilen,
980 sensor->critmin_value);
981 ilen = 8;
982 } else
983 ilen += 9;
984
985 }
986
987 (void)printf("%*s", (int)ilen - 4, stype);
988
989 /* drives */
990 } else if (strcmp(sensor->type, "Drive") == 0) {
991
992 (void)printf(":%10s", sensor->drvstate);
993
994 /* Battery capacity */
995 } else if (strcmp(sensor->type, "Battery capacity") == 0) {
996
997 (void)printf(":%10s", sensor->battcap);
998
999 /* everything else */
1000 } else {
1001 if (strcmp(sensor->type, "Voltage DC") == 0)
1002 stype = "V";
1003 else if (strcmp(sensor->type, "Voltage AC") == 0)
1004 stype = "VAC";
1005 else if (strcmp(sensor->type, "Ampere") == 0)
1006 stype = "A";
1007 else if (strcmp(sensor->type, "Watts") == 0)
1008 stype = "W";
1009 else if (strcmp(sensor->type, "Ohms") == 0)
1010 stype = "Ohms";
1011 else if (strcmp(sensor->type, "Watt hour") == 0)
1012 stype = "Wh";
1013 else if (strcmp(sensor->type, "Ampere hour") == 0)
1014 stype = "Ah";
1015
1016 (void)printf(":%10.3f ",
1017 sensor->cur_value / 1000000.0);
1018
1019 ilen = 8;
1020 if (!statistics) {
1021
1022 /* Print percentage of max_value */
1023 #define PRINTPCT(a) \
1024 do { \
1025 if (sensor->a && sensor->max_value) { \
1026 (void)printf("%*.3f%%", (int)ilen, \
1027 (sensor->a * 100.0) / sensor->max_value); \
1028 ilen = 8; \
1029 } else \
1030 ilen += 9; \
1031 } while ( /* CONSTCOND*/ 0 )
1032
1033 /* Print a generic sensor value */
1034 #define PRINTVAL(a) \
1035 do { \
1036 if (sensor->a) { \
1037 (void)printf("%*.3f ", (int)ilen, sensor->a / 1000000.0); \
1038 ilen = 8; \
1039 } else \
1040 ilen += 9; \
1041 } while ( /* CONSTCOND*/ 0 )
1042
1043
1044 if (sensor->percentage) {
1045 PRINTPCT(critmax_value);
1046 PRINTPCT(warnmax_value);
1047 PRINTPCT(warnmin_value);
1048 PRINTPCT(critmin_value);
1049 } else {
1050
1051 PRINTVAL(critmax_value);
1052 PRINTVAL(warnmax_value);
1053 PRINTVAL(warnmin_value);
1054 PRINTVAL(critmin_value);
1055 #undef PRINTPCT
1056 #undef PRINTVAL
1057 }
1058 }
1059
1060 if (statistics && !sensor->percentage) {
1061 /* show statistics if flag set */
1062 (void)printf("%8.3f %8.3f %8.3f ",
1063 stats->max / 1000000.0,
1064 stats->min / 1000000.0,
1065 stats->avg / 1000000.0);
1066 ilen += 2;
1067 }
1068
1069 (void)printf("%*s", (int)ilen - 4, stype);
1070
1071 if (sensor->percentage && sensor->max_value) {
1072 (void)printf(" (%5.2f%%)",
1073 (sensor->cur_value * 100.0) /
1074 sensor->max_value);
1075 }
1076 }
1077 (void)printf("\n");
1078 }
1079 }
1080
1081 static int
1082 usage(void)
1083 {
1084 (void)fprintf(stderr, "Usage: %s [-DfIklrSTx] ", getprogname());
1085 (void)fprintf(stderr, "[-c file] [-d device] [-i interval] ");
1086 (void)fprintf(stderr, "[-s device:sensor,...] [-w width]\n");
1087 exit(EXIT_FAILURE);
1088 /* NOTREACHED */
1089 }
1090