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