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