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