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 */ 65typedef 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 */ 85typedef 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 */ 94typedef 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 */ 100static SIMPLEQ_HEAD(, envsys_sensor) sensors_list = 101 SIMPLEQ_HEAD_INITIALIZER(sensors_list); 102 103/* A simple queue to manage statistics for all sensors */ 104static SIMPLEQ_HEAD(, envsys_sensor_stats) sensor_stats_list = 105 SIMPLEQ_HEAD_INITIALIZER(sensor_stats_list); 106 107static unsigned int interval, flags, width; 108static char *mydevname, *sensors; 109static bool statistics; 110static u_int header_passes; 111 112static int parse_dictionary(int); 113static int add_sensors(prop_dictionary_t, prop_dictionary_t, const char *, const char *); 114static int send_dictionary(FILE *); 115static int find_sensors(prop_array_t, const char *, dvprops_t); 116static void print_sensors(void); 117static int check_sensors(const char *); 118static int usage(void); 119 120static int sysmonfd; /* fd of /dev/sysmon */ 121 122int 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 347static int 348send_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 388static sensor_stats_t 389find_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 413static int 414add_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 493fail: 494 prop_dictionary_remove(ndict, dev); 495 prop_object_release(narray); 496 return -1; 497} 498 499static int 500parse_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 588out: 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 599static int 600find_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 794static int 795check_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"); 850out: 851 free(s); 852 return EINVAL; 853} 854 855static void 856print_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) \ 974do { \ 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) \ 1074do { \ 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) \ 1085do { \ 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) \ 1253do { \ 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) \ 1264do { \ 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 1313static int 1314usage(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