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