1 /* $NetBSD: envstat.c,v 1.101 2021/11/27 22:30:25 rillig 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.101 2021/11/27 22:30:25 rillig 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_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 Farenheit */ 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 /* everything else */ 1178 } else { 1179 if (strcmp(sensor->type, "Voltage DC") == 0) 1180 stype = "V"; 1181 else if (strcmp(sensor->type, "Voltage AC") == 0) 1182 stype = "VAC"; 1183 else if (strcmp(sensor->type, "Ampere") == 0) 1184 stype = "A"; 1185 else if (strcmp(sensor->type, "Watts") == 0) 1186 stype = "W"; 1187 else if (strcmp(sensor->type, "Ohms") == 0) 1188 stype = "Ohms"; 1189 else if (strcmp(sensor->type, "Watt hour") == 0) 1190 stype = "Wh"; 1191 else if (strcmp(sensor->type, "Ampere hour") == 0) 1192 stype = "Ah"; 1193 else if (strcmp(sensor->type, "relative Humidity") == 0) 1194 stype = "%rH"; 1195 else 1196 stype = "?"; 1197 1198 (void)printf("%s%*.3f", sep, flen, 1199 sensor->cur_value / 1000000.0); 1200 1201 ilen = 9; 1202 1203 /* Print percentage of max_value */ 1204 #define PRINTPCT(a) \ 1205 do { \ 1206 if ((a) && sensor->max_value) { \ 1207 (void)printf("%*.3f%%", (int)ilen, \ 1208 ((a) * 100.0) / sensor->max_value); \ 1209 ilen = 8; \ 1210 } else \ 1211 ilen += 9; \ 1212 } while ( /* CONSTCOND*/ 0 ) 1213 1214 /* Print a generic sensor value */ 1215 #define PRINTVAL(a) \ 1216 do { \ 1217 if ((a)) { \ 1218 (void)printf("%*.3f", (int)ilen, (a) / 1000000.0); \ 1219 ilen = 9; \ 1220 } else \ 1221 ilen += 9; \ 1222 } while ( /* CONSTCOND*/ 0 ) 1223 1224 if (statistics) { 1225 if (sensor->percentage) { 1226 PRINTPCT(stats->max); 1227 PRINTPCT(stats->min); 1228 PRINTPCT(stats->avg); 1229 } else { 1230 PRINTVAL(stats->max); 1231 PRINTVAL(stats->min); 1232 PRINTVAL(stats->avg); 1233 } 1234 ilen += 2; 1235 } else if (!nflag) { 1236 if (sensor->percentage) { 1237 PRINTPCT(sensor->critmax_value); 1238 PRINTPCT(sensor->warnmax_value); 1239 PRINTPCT(sensor->warnmin_value); 1240 PRINTPCT(sensor->critmin_value); 1241 } else { 1242 1243 PRINTVAL(sensor->critmax_value); 1244 PRINTVAL(sensor->warnmax_value); 1245 PRINTVAL(sensor->warnmin_value); 1246 PRINTVAL(sensor->critmin_value); 1247 } 1248 } 1249 #undef PRINTPCT 1250 #undef PRINTVAL 1251 1252 if (!nflag) { 1253 (void)printf(" %*s", (int)ilen - 4, stype); 1254 if (sensor->percentage && sensor->max_value) { 1255 (void)printf(" (%5.2f%%)", 1256 (sensor->cur_value * 100.0) / 1257 sensor->max_value); 1258 } 1259 } 1260 } 1261 (void)printf("\n"); 1262 } 1263 } 1264 1265 static int 1266 usage(void) 1267 { 1268 (void)fprintf(stderr, "Usage: %s [-DfIklnrST] ", getprogname()); 1269 (void)fprintf(stderr, "[-c file] [-d device] [-i interval] "); 1270 (void)fprintf(stderr, "[-s device:sensor,...] [-w width]\n"); 1271 (void)fprintf(stderr, " %s ", getprogname()); 1272 (void)fprintf(stderr, "[-d device] "); 1273 (void)fprintf(stderr, "[-s device:sensor,...] "); 1274 (void)fprintf(stderr, "-x [property]\n"); 1275 exit(EXIT_FAILURE); 1276 /* NOTREACHED */ 1277 } 1278