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