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