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