1 /* $NetBSD: envstat.c,v 1.58 2007/11/16 08:01:38 xtraeme Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 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 /* 29 * TODO 30 * 31 * o Some checks should be added to ensure that the user does not 32 * set unwanted values for the critical limits. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __RCSID("$NetBSD: envstat.c,v 1.58 2007/11/16 08:01:38 xtraeme Exp $"); 38 #endif /* not lint */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <stdbool.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <fcntl.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <syslog.h> 49 #include <prop/proplib.h> 50 #include <sys/envsys.h> 51 #include <sys/types.h> 52 53 #include "envstat.h" 54 55 #define _PATH_DEV_SYSMON "/dev/sysmon" 56 57 #define ENVSYS_DFLAG 0x00000001 /* list registered devices */ 58 #define ENVSYS_FFLAG 0x00000002 /* show temp in farenheit */ 59 #define ENVSYS_LFLAG 0x00000004 /* list sensors */ 60 #define ENVSYS_XFLAG 0x00000008 /* externalize dictionary */ 61 #define ENVSYS_IFLAG 0x00000010 /* skips invalid sensors */ 62 #define ENVSYS_SFLAG 0x00000020 /* removes all properties set */ 63 64 struct envsys_sensor { 65 bool invalid; 66 bool visible; 67 bool percentage; 68 int32_t cur_value; 69 int32_t max_value; 70 int32_t min_value; 71 int32_t avg_value; 72 int32_t critcap_value; 73 int32_t critmin_value; 74 int32_t critmax_value; 75 char desc[ENVSYS_DESCLEN]; 76 char type[ENVSYS_DESCLEN]; 77 char drvstate[ENVSYS_DESCLEN]; 78 char battcap[ENVSYS_DESCLEN]; 79 char dvname[ENVSYS_DESCLEN]; 80 }; 81 82 struct envsys_dvprops { 83 uint64_t refresh_timo; 84 char refresh_units[ENVSYS_DESCLEN]; 85 /* more values could be added in the future */ 86 }; 87 88 static unsigned int interval, flags, width; 89 static char *mydevname, *sensors; 90 static struct envsys_sensor *gesen; 91 static size_t gnelems, newsize; 92 93 static int parse_dictionary(int); 94 static int send_dictionary(FILE *, int); 95 static int find_sensors(prop_array_t, const char *, struct envsys_dvprops *); 96 static void print_sensors(struct envsys_sensor *, size_t, const char *); 97 static int check_sensors(struct envsys_sensor *, char *, size_t); 98 static int usage(void); 99 100 101 int main(int argc, char **argv) 102 { 103 prop_dictionary_t dict; 104 int c, fd, rval; 105 char *endptr, *configfile = NULL; 106 FILE *cf; 107 108 rval = flags = interval = width = 0; 109 newsize = gnelems = 0; 110 gesen = NULL; 111 112 setprogname(argv[0]); 113 114 while ((c = getopt(argc, argv, "c:Dd:fIi:lrSs:w:x")) != -1) { 115 switch (c) { 116 case 'c': /* configuration file */ 117 configfile = strdup(optarg); 118 if (configfile == NULL) 119 err(EXIT_FAILURE, "strdup"); 120 break; 121 case 'D': /* list registered devices */ 122 flags |= ENVSYS_DFLAG; 123 break; 124 case 'd': /* show sensors of a specific device */ 125 mydevname = strdup(optarg); 126 if (mydevname == NULL) 127 err(EXIT_FAILURE, "strdup"); 128 break; 129 case 'f': /* display temperature in Farenheit */ 130 flags |= ENVSYS_FFLAG; 131 break; 132 case 'I': /* Skips invalid sensors */ 133 flags |= ENVSYS_IFLAG; 134 break; 135 case 'i': /* wait time between intervals */ 136 interval = (unsigned int)strtoul(optarg, &endptr, 10); 137 if (*endptr != '\0') 138 errx(EXIT_FAILURE, "bad interval '%s'", optarg); 139 break; 140 case 'l': /* list sensors */ 141 flags |= ENVSYS_LFLAG; 142 break; 143 case 'r': 144 /* 145 * This flag doesn't do anything... it's only here for 146 * compatibility with the old implementation. 147 */ 148 break; 149 case 'S': 150 flags |= ENVSYS_SFLAG; 151 break; 152 case 's': /* only show specified sensors */ 153 sensors = strdup(optarg); 154 if (sensors == NULL) 155 err(EXIT_FAILURE, "strdup"); 156 break; 157 case 'w': /* width value for the lines */ 158 width = strtoul(optarg, &endptr, 10); 159 if (*endptr != '\0') 160 errx(EXIT_FAILURE, "bad width '%s'", optarg); 161 break; 162 case 'x': /* print the dictionary in raw format */ 163 flags |= ENVSYS_XFLAG; 164 break; 165 case '?': 166 default: 167 usage(); 168 /* NOTREACHED */ 169 } 170 } 171 172 argc -= optind; 173 argv += optind; 174 175 if (argc > 0) 176 usage(); 177 178 if ((fd = open(_PATH_DEV_SYSMON, O_RDONLY)) == -1) 179 err(EXIT_FAILURE, "%s", _PATH_DEV_SYSMON); 180 181 if (flags & ENVSYS_XFLAG) { 182 rval = prop_dictionary_recv_ioctl(fd, 183 ENVSYS_GETDICTIONARY, 184 &dict); 185 if (rval) 186 errx(EXIT_FAILURE, "%s", strerror(rval)); 187 188 config_dict_dump(dict); 189 190 } else if (flags & ENVSYS_SFLAG) { 191 (void)close(fd); 192 193 if ((fd = open(_PATH_DEV_SYSMON, O_RDWR)) == -1) 194 err(EXIT_FAILURE, "%s", _PATH_DEV_SYSMON); 195 196 dict = prop_dictionary_create(); 197 if (!dict) 198 err(EXIT_FAILURE, "prop_dictionary_create"); 199 200 rval = prop_dictionary_set_bool(dict, 201 "envsys-remove-props", 202 true); 203 if (!rval) 204 err(EXIT_FAILURE, "prop_dict_set_bool"); 205 206 rval = prop_dictionary_send_ioctl(dict, fd, ENVSYS_REMOVEPROPS); 207 if (rval) 208 warnx("%s", strerror(rval)); 209 210 } else if (configfile) { 211 /* 212 * Parse the configuration file. 213 */ 214 if ((cf = fopen(configfile, "r")) == NULL) { 215 syslog(LOG_ERR, "fopen failed: %s", strerror(errno)); 216 errx(EXIT_FAILURE, "%s", strerror(errno)); 217 } 218 219 rval = send_dictionary(cf, fd); 220 (void)fclose(cf); 221 222 #define MISSING_FLAG() \ 223 do { \ 224 if (sensors && !mydevname) \ 225 errx(EXIT_FAILURE, "-s requires -d"); \ 226 } while (/* CONSTCOND */ 0) 227 228 } else if (interval) { 229 MISSING_FLAG(); 230 for (;;) { 231 rval = parse_dictionary(fd); 232 if (rval) 233 break; 234 235 (void)fflush(stdout); 236 (void)sleep(interval); 237 } 238 } else { 239 MISSING_FLAG(); 240 rval = parse_dictionary(fd); 241 } 242 243 if (sensors) 244 free(sensors); 245 if (mydevname) 246 free(mydevname); 247 (void)close(fd); 248 249 return rval ? EXIT_FAILURE : EXIT_SUCCESS; 250 } 251 252 static int 253 send_dictionary(FILE *cf, int fd) 254 { 255 prop_dictionary_t kdict, udict; 256 int error = 0; 257 258 error = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &kdict); 259 if (error) 260 return error; 261 262 config_parse(cf, kdict); 263 264 /* 265 * Dictionary built by the parser from the configuration file. 266 */ 267 udict = config_dict_parsed(); 268 269 /* 270 * Close the read only descriptor and open a new one read write. 271 */ 272 (void)close(fd); 273 if ((fd = open(_PATH_DEV_SYSMON, O_RDWR)) == -1) { 274 error = errno; 275 warn("%s", _PATH_DEV_SYSMON); 276 return error; 277 } 278 279 /* 280 * Send our sensor properties dictionary to the kernel then. 281 */ 282 error = prop_dictionary_send_ioctl(udict, fd, ENVSYS_SETDICTIONARY); 283 if (error) 284 warnx("%s", strerror(error)); 285 286 prop_object_release(udict); 287 return error; 288 } 289 290 static int 291 parse_dictionary(int fd) 292 { 293 struct envsys_dvprops *edp = NULL; 294 prop_array_t array; 295 prop_dictionary_t dict; 296 prop_object_iterator_t iter; 297 prop_object_t obj; 298 const char *dnp = NULL; 299 int rval = 0; 300 301 /* receive dictionary from kernel */ 302 rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict); 303 if (rval) 304 return rval; 305 306 if (prop_dictionary_count(dict) == 0) { 307 warnx("no drivers registered"); 308 goto out; 309 } 310 311 if (mydevname) { 312 obj = prop_dictionary_get(dict, mydevname); 313 if (prop_object_type(obj) != PROP_TYPE_ARRAY) { 314 warnx("unknown device `%s'", mydevname); 315 rval = EINVAL; 316 goto out; 317 } 318 319 rval = find_sensors(obj, mydevname, NULL); 320 if (rval) 321 goto out; 322 323 if ((flags & ENVSYS_LFLAG) == 0) 324 print_sensors(gesen, gnelems, mydevname); 325 if (interval) 326 (void)printf("\n"); 327 } else { 328 iter = prop_dictionary_iterator(dict); 329 if (iter == NULL) { 330 rval = EINVAL; 331 goto out; 332 } 333 334 /* iterate over the dictionary returned by the kernel */ 335 while ((obj = prop_object_iterator_next(iter)) != NULL) { 336 337 array = prop_dictionary_get_keysym(dict, obj); 338 if (prop_object_type(array) != PROP_TYPE_ARRAY) { 339 warnx("no sensors found"); 340 rval = EINVAL; 341 goto out; 342 } 343 344 edp = (struct envsys_dvprops *)malloc(sizeof(*edp)); 345 if (!edp) { 346 rval = ENOMEM; 347 goto out; 348 } 349 350 dnp = prop_dictionary_keysym_cstring_nocopy(obj); 351 rval = find_sensors(array, dnp, edp); 352 if (rval) 353 goto out; 354 355 if (((flags & ENVSYS_LFLAG) == 0) && 356 (flags & ENVSYS_DFLAG)) { 357 (void)printf("%s (checking events every ", 358 dnp); 359 if (edp->refresh_timo == 1) 360 (void)printf("second)\n"); 361 else 362 (void)printf("%d seconds)\n", 363 (int)edp->refresh_timo); 364 continue; 365 } 366 367 if ((flags & ENVSYS_LFLAG) == 0) { 368 (void)printf("[%s]\n", dnp); 369 print_sensors(gesen, gnelems, dnp); 370 } 371 372 if (interval) 373 (void)printf("\n"); 374 375 free(edp); 376 edp = NULL; 377 } 378 379 prop_object_iterator_release(iter); 380 } 381 382 out: 383 if (gesen) { 384 free(gesen); 385 gesen = NULL; 386 gnelems = 0; 387 newsize = 0; 388 } 389 if (edp) 390 free(edp); 391 prop_object_release(dict); 392 return rval; 393 } 394 395 static int 396 find_sensors(prop_array_t array, const char *dvname, struct envsys_dvprops *edp) 397 { 398 prop_object_iterator_t iter; 399 prop_object_t obj, obj1, obj2; 400 prop_string_t state, desc = NULL; 401 struct envsys_sensor *esen = NULL; 402 int rval = 0; 403 char *str = NULL; 404 405 newsize += prop_array_count(array) * sizeof(*gesen); 406 esen = realloc(gesen, newsize); 407 if (esen == NULL) { 408 if (gesen) 409 free(gesen); 410 gesen = NULL; 411 return ENOMEM; 412 } 413 gesen = esen; 414 415 iter = prop_array_iterator(array); 416 if (!iter) 417 return EINVAL; 418 419 /* iterate over the array of dictionaries */ 420 while ((obj = prop_object_iterator_next(iter)) != NULL) { 421 422 /* get the refresh-timeout property */ 423 obj2 = prop_dictionary_get(obj, "device-properties"); 424 if (obj2) { 425 if (!edp) 426 continue; 427 if (!prop_dictionary_get_uint64(obj2, 428 "refresh-timeout", 429 &edp->refresh_timo)) 430 continue; 431 } 432 433 /* copy device name */ 434 (void)strlcpy(gesen[gnelems].dvname, dvname, 435 sizeof(gesen[gnelems].dvname)); 436 437 gesen[gnelems].visible = false; 438 439 /* check sensor's state */ 440 state = prop_dictionary_get(obj, "state"); 441 442 /* mark sensors with invalid/unknown state */ 443 if ((prop_string_equals_cstring(state, "invalid") || 444 prop_string_equals_cstring(state, "unknown"))) 445 gesen[gnelems].invalid = true; 446 else 447 gesen[gnelems].invalid = false; 448 449 /* description string */ 450 desc = prop_dictionary_get(obj, "description"); 451 if (desc) { 452 /* copy description */ 453 (void)strlcpy(gesen[gnelems].desc, 454 prop_string_cstring_nocopy(desc), 455 sizeof(gesen[gnelems].desc)); 456 } else 457 continue; 458 459 /* type string */ 460 obj1 = prop_dictionary_get(obj, "type"); 461 /* copy type */ 462 (void)strlcpy(gesen[gnelems].type, 463 prop_string_cstring_nocopy(obj1), 464 sizeof(gesen[gnelems].type)); 465 466 /* get current drive state string */ 467 obj1 = prop_dictionary_get(obj, "drive-state"); 468 if (obj1) 469 (void)strlcpy(gesen[gnelems].drvstate, 470 prop_string_cstring_nocopy(obj1), 471 sizeof(gesen[gnelems].drvstate)); 472 473 /* get current battery capacity string */ 474 obj1 = prop_dictionary_get(obj, "battery-capacity"); 475 if (obj1) 476 (void)strlcpy(gesen[gnelems].battcap, 477 prop_string_cstring_nocopy(obj1), 478 sizeof(gesen[gnelems].battcap)); 479 480 /* get current value */ 481 obj1 = prop_dictionary_get(obj, "cur-value"); 482 gesen[gnelems].cur_value = prop_number_integer_value(obj1); 483 484 /* get max value */ 485 obj1 = prop_dictionary_get(obj, "max-value"); 486 if (obj1) 487 gesen[gnelems].max_value = 488 prop_number_integer_value(obj1); 489 else 490 gesen[gnelems].max_value = 0; 491 492 /* get min value */ 493 obj1 = prop_dictionary_get(obj, "min-value"); 494 if (obj1) 495 gesen[gnelems].min_value = 496 prop_number_integer_value(obj1); 497 else 498 gesen[gnelems].min_value = 0; 499 500 /* get avg value */ 501 obj1 = prop_dictionary_get(obj, "avg-value"); 502 if (obj1) 503 gesen[gnelems].avg_value = 504 prop_number_integer_value(obj1); 505 else 506 gesen[gnelems].avg_value = 0; 507 508 /* get percentage flag */ 509 obj1 = prop_dictionary_get(obj, "want-percentage"); 510 if (obj1) 511 gesen[gnelems].percentage = prop_bool_true(obj1); 512 513 /* get critical max value if available */ 514 obj1 = prop_dictionary_get(obj, "critical-max"); 515 if (obj1) { 516 gesen[gnelems].critmax_value = 517 prop_number_integer_value(obj1); 518 } else 519 gesen[gnelems].critmax_value = 0; 520 521 /* get critical min value if available */ 522 obj1 = prop_dictionary_get(obj, "critical-min"); 523 if (obj1) { 524 gesen[gnelems].critmin_value = 525 prop_number_integer_value(obj1); 526 } else 527 gesen[gnelems].critmin_value = 0; 528 529 /* get critical capacity value if available */ 530 obj1 = prop_dictionary_get(obj, "critical-capacity"); 531 if (obj1) { 532 gesen[gnelems].critcap_value = 533 prop_number_integer_value(obj1); 534 } else 535 gesen[gnelems].critcap_value = 0; 536 537 /* pass to the next struct and increase the counter */ 538 gnelems++; 539 540 /* print sensor names if -l was given */ 541 if (flags & ENVSYS_LFLAG) { 542 if (width) 543 (void)printf("%*s\n", width, 544 prop_string_cstring_nocopy(desc)); 545 else 546 (void)printf("%s\n", 547 prop_string_cstring_nocopy(desc)); 548 } 549 } 550 551 /* free memory */ 552 prop_object_iterator_release(iter); 553 554 /* 555 * if -s was specified, we need a way to mark if a sensor 556 * was found. 557 */ 558 if (sensors) { 559 str = strdup(sensors); 560 if (!str) 561 return ENOMEM; 562 563 rval = check_sensors(gesen, str, gnelems); 564 free(str); 565 } 566 567 return rval; 568 } 569 570 static int 571 check_sensors(struct envsys_sensor *es, char *str, size_t nelems) 572 { 573 int i; 574 char *sname; 575 576 sname = strtok(str, ","); 577 while (sname) { 578 for (i = 0; i < nelems; i++) { 579 if (strcmp(sname, es[i].desc) == 0) { 580 es[i].visible = true; 581 break; 582 } 583 } 584 if (i >= nelems) { 585 if (mydevname) { 586 warnx("unknown sensor `%s' for device `%s'", 587 sname, mydevname); 588 return EINVAL; 589 } 590 } 591 sname = strtok(NULL, ","); 592 } 593 594 /* check if all sensors were ok, and error out if not */ 595 for (i = 0; i < nelems; i++) { 596 if (es[i].visible) 597 return 0; 598 } 599 600 warnx("no sensors selected to display"); 601 return EINVAL; 602 } 603 604 static void 605 print_sensors(struct envsys_sensor *es, size_t nelems, const char *dvname) 606 { 607 size_t maxlen = 0; 608 double temp = 0; 609 const char *invalid = "N/A"; 610 const char *degrees = NULL; 611 int i; 612 613 /* find the longest description */ 614 for (i = 0; i < nelems; i++) { 615 if (strlen(es[i].desc) > maxlen) 616 maxlen = strlen(es[i].desc); 617 } 618 619 if (width) 620 maxlen = width; 621 622 /* print the sensors */ 623 for (i = 0; i < nelems; i++) { 624 /* skip sensors that don't belong to device 'dvname' */ 625 if (strcmp(es[i].dvname, dvname)) 626 continue; 627 628 /* skip sensors that were not marked as visible */ 629 if (sensors && !es[i].visible) 630 continue; 631 632 /* Do not print invalid sensors if -I is set */ 633 if ((flags & ENVSYS_IFLAG) && es[i].invalid) 634 continue; 635 636 (void)printf("%s%*.*s", mydevname ? "" : " ", (int)maxlen, 637 (int)maxlen, es[i].desc); 638 639 if (es[i].invalid) { 640 (void)printf(": %10s\n", invalid); 641 continue; 642 } 643 644 /* 645 * Indicator and Battery charge sensors. 646 */ 647 if ((strcmp(es[i].type, "Indicator") == 0) || 648 (strcmp(es[i].type, "Battery charge") == 0)) { 649 650 (void)printf(": %10s", es[i].cur_value ? "ON" : "OFF"); 651 652 /* converts the value to degC or degF */ 653 #define CONVERTTEMP(a, b, c) \ 654 do { \ 655 if (b) \ 656 (a) = ((b) / 1000000.0) - 273.15; \ 657 if (flags & ENVSYS_FFLAG) { \ 658 if (b) \ 659 (a) = (9.0 / 5.0) * (a) + 32.0; \ 660 (c) = "degF"; \ 661 } else \ 662 (c) = "degC"; \ 663 } while (/* CONSTCOND */ 0) 664 665 666 /* temperatures */ 667 } else if (strcmp(es[i].type, "Temperature") == 0) { 668 669 CONVERTTEMP(temp, es[i].cur_value, degrees); 670 (void)printf(": %10.3f %s", temp, degrees); 671 672 if (es[i].critmax_value || es[i].critmin_value) 673 (void)printf(" "); 674 675 if (es[i].critmax_value) { 676 CONVERTTEMP(temp, es[i].critmax_value, degrees); 677 (void)printf("max: %8.3f %s ", temp, degrees); 678 } 679 680 if (es[i].critmin_value) { 681 CONVERTTEMP(temp, es[i].critmin_value, degrees); 682 (void)printf("min: %8.3f %s", temp, degrees); 683 } 684 #undef CONVERTTEMP 685 686 /* fans */ 687 } else if (strcmp(es[i].type, "Fan") == 0) { 688 689 (void)printf(": %10u RPM", es[i].cur_value); 690 691 if (es[i].critmax_value || es[i].critmin_value) 692 (void)printf(" "); 693 if (es[i].critmax_value) 694 (void)printf("max: %8u RPM ", 695 es[i].critmax_value); 696 if (es[i].critmin_value) 697 (void)printf("min: %8u RPM", 698 es[i].critmin_value); 699 700 /* integers */ 701 } else if (strcmp(es[i].type, "Integer") == 0) { 702 703 (void)printf(": %10d", es[i].cur_value); 704 705 /* drives */ 706 } else if (strcmp(es[i].type, "Drive") == 0) { 707 708 (void)printf(": %10s", es[i].drvstate); 709 710 /* Battery capacity */ 711 } else if (strcmp(es[i].type, "Battery capacity") == 0) { 712 713 (void)printf(": %10s", es[i].battcap); 714 715 /* everything else */ 716 } else { 717 const char *type; 718 719 if (strcmp(es[i].type, "Voltage DC") == 0) 720 type = "V"; 721 else if (strcmp(es[i].type, "Voltage AC") == 0) 722 type = "VAC"; 723 else if (strcmp(es[i].type, "Ampere") == 0) 724 type = "A"; 725 else if (strcmp(es[i].type, "Watts") == 0) 726 type = "W"; 727 else if (strcmp(es[i].type, "Ohms") == 0) 728 type = "Ohms"; 729 else if (strcmp(es[i].type, "Watt hour") == 0) 730 type = "Wh"; 731 else if (strcmp(es[i].type, "Ampere hour") == 0) 732 type = "Ah"; 733 else 734 type = NULL; 735 736 (void)printf(": %10.3f %s", 737 es[i].cur_value / 1000000.0, type); 738 739 if (es[i].percentage && es[i].max_value) { 740 (void)printf(" (%5.2f%%)", 741 (es[i].cur_value * 100.0) / 742 es[i].max_value); 743 } 744 745 if (es[i].critcap_value) { 746 (void)printf(" critical (%5.2f%%)", 747 (es[i].critcap_value * 100.0) / 748 es[i].max_value); 749 } 750 751 if (es[i].critmax_value || es[i].critmin_value) 752 (void)printf(" "); 753 if (es[i].critmax_value) 754 (void)printf("max: %8.3f %s ", 755 es[i].critmax_value / 1000000.0, 756 type); 757 if (es[i].critmin_value) 758 (void)printf("min: %8.3f %s", 759 es[i].critmin_value / 1000000.0, 760 type); 761 762 } 763 764 (void)printf("\n"); 765 } 766 } 767 768 static int 769 usage(void) 770 { 771 (void)fprintf(stderr, "Usage: %s [-DfIlrSx] ", getprogname()); 772 (void)fprintf(stderr, "[-c file] [-d device] [-i interval] "); 773 (void)fprintf(stderr, "[-s sensor,...] [-w width]\n"); 774 exit(EXIT_FAILURE); 775 /* NOTREACHED */ 776 } 777