1 /* $OpenBSD: sensorsd.c,v 1.54 2015/01/16 06:40:20 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com> 6 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/sysctl.h> 23 #include <sys/queue.h> 24 #include <sys/sensors.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <time.h> 34 #include <unistd.h> 35 #include <limits.h> 36 37 #define RFBUFSIZ 28 /* buffer size for print_sensor */ 38 #define RFBUFCNT 4 /* ring buffers */ 39 #define CHECK_PERIOD 20 /* check every n seconds */ 40 41 enum sensorsd_s_status { 42 SENSORSD_S_UNSPEC, /* status is unspecified */ 43 SENSORSD_S_INVALID, /* status is invalid, per SENSOR_FINVALID */ 44 SENSORSD_S_WITHIN, /* status is within limits */ 45 SENSORSD_S_ABOVE, /* status is above the higher limit */ 46 SENSORSD_S_BELOW /* status is below the lower limit */ 47 }; 48 49 struct limits_t { 50 TAILQ_ENTRY(limits_t) entries; 51 enum sensor_type type; /* sensor type */ 52 int numt; /* sensor number */ 53 int64_t last_val; 54 int64_t lower; /* lower limit */ 55 int64_t upper; /* upper limit */ 56 char *command; /* failure command */ 57 time_t astatus_changed; 58 time_t ustatus_changed; 59 enum sensor_status astatus; /* last automatic status */ 60 enum sensor_status astatus2; 61 enum sensorsd_s_status ustatus; /* last user-limit status */ 62 enum sensorsd_s_status ustatus2; 63 int acount; /* stat change counter */ 64 int ucount; /* stat change counter */ 65 u_int8_t flags; /* sensorsd limit flags */ 66 #define SENSORSD_L_USERLIMIT 0x0001 /* user specified limit */ 67 #define SENSORSD_L_ISTATUS 0x0002 /* ignore automatic status */ 68 }; 69 70 struct sdlim_t { 71 TAILQ_ENTRY(sdlim_t) entries; 72 char dxname[16]; /* device unix name */ 73 int dev; /* device number */ 74 int sensor_cnt; 75 TAILQ_HEAD(, limits_t) limits; 76 }; 77 78 void usage(void); 79 void create(void); 80 struct sdlim_t *create_sdlim(struct sensordev *); 81 void destroy_sdlim(struct sdlim_t *); 82 void check(time_t); 83 void check_sdlim(struct sdlim_t *, time_t); 84 void execute(char *); 85 void report(time_t); 86 void report_sdlim(struct sdlim_t *, time_t); 87 static char *print_sensor(enum sensor_type, int64_t); 88 void parse_config(char *); 89 void parse_config_sdlim(struct sdlim_t *, char *); 90 int64_t get_val(char *, int, enum sensor_type); 91 void reparse_cfg(int); 92 93 TAILQ_HEAD(sdlimhead_t, sdlim_t); 94 struct sdlimhead_t sdlims = TAILQ_HEAD_INITIALIZER(sdlims); 95 96 char *configfile; 97 volatile sig_atomic_t reload = 0; 98 int debug = 0; 99 100 void 101 usage(void) 102 { 103 extern char *__progname; 104 fprintf(stderr, "usage: %s [-d] [-c check]\n", __progname); 105 exit(1); 106 } 107 108 int 109 main(int argc, char *argv[]) 110 { 111 time_t last_report = 0, this_check; 112 int ch, check_period = CHECK_PERIOD; 113 const char *errstr; 114 115 while ((ch = getopt(argc, argv, "c:d")) != -1) { 116 switch (ch) { 117 case 'c': 118 check_period = strtonum(optarg, 1, 600, &errstr); 119 if (errstr) 120 errx(1, "check %s", errstr); 121 break; 122 case 'd': 123 debug = 1; 124 break; 125 default: 126 usage(); 127 } 128 } 129 130 argc -= optind; 131 argv += optind; 132 if (argc > 0) 133 usage(); 134 135 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 136 137 create(); 138 139 if (configfile == NULL) 140 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1) 141 err(1, "out of memory"); 142 parse_config(configfile); 143 144 if (debug == 0 && daemon(0, 0) == -1) 145 err(1, "unable to fork"); 146 147 signal(SIGHUP, reparse_cfg); 148 signal(SIGCHLD, SIG_IGN); 149 150 for (;;) { 151 if (reload) { 152 parse_config(configfile); 153 syslog(LOG_INFO, "configuration reloaded"); 154 reload = 0; 155 } 156 this_check = time(NULL); 157 if (!(last_report < this_check)) 158 this_check = last_report + 1; 159 check(this_check); 160 report(last_report); 161 last_report = this_check; 162 sleep(check_period); 163 } 164 } 165 166 void 167 create(void) 168 { 169 struct sensordev sensordev; 170 struct sdlim_t *sdlim; 171 size_t sdlen = sizeof(sensordev); 172 int mib[3], dev, sensor_cnt = 0; 173 174 mib[0] = CTL_HW; 175 mib[1] = HW_SENSORS; 176 177 for (dev = 0; ; dev++) { 178 mib[2] = dev; 179 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { 180 if (errno == ENXIO) 181 continue; 182 if (errno == ENOENT) 183 break; 184 warn("sysctl"); 185 } 186 sdlim = create_sdlim(&sensordev); 187 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries); 188 sensor_cnt += sdlim->sensor_cnt; 189 } 190 191 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt); 192 } 193 194 struct sdlim_t * 195 create_sdlim(struct sensordev *snsrdev) 196 { 197 struct sensor sensor; 198 struct sdlim_t *sdlim; 199 struct limits_t *limit; 200 size_t slen = sizeof(sensor); 201 int mib[5], numt; 202 enum sensor_type type; 203 204 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL) 205 err(1, "calloc"); 206 207 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname)); 208 209 mib[0] = CTL_HW; 210 mib[1] = HW_SENSORS; 211 mib[2] = sdlim->dev = snsrdev->num; 212 213 TAILQ_INIT(&sdlim->limits); 214 215 for (type = 0; type < SENSOR_MAX_TYPES; type++) { 216 mib[3] = type; 217 for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) { 218 mib[4] = numt; 219 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { 220 if (errno != ENOENT) 221 warn("sysctl"); 222 continue; 223 } 224 if ((limit = calloc(1, sizeof(struct limits_t))) == 225 NULL) 226 err(1, "calloc"); 227 limit->type = type; 228 limit->numt = numt; 229 TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries); 230 sdlim->sensor_cnt++; 231 } 232 } 233 234 return (sdlim); 235 } 236 237 void 238 destroy_sdlim(struct sdlim_t *sdlim) 239 { 240 struct limits_t *limit; 241 242 while((limit = TAILQ_FIRST(&sdlim->limits)) != NULL) { 243 TAILQ_REMOVE(&sdlim->limits, limit, entries); 244 if (limit->command != NULL) 245 free(limit->command); 246 free(limit); 247 } 248 free(sdlim); 249 } 250 251 void 252 check(time_t this_check) 253 { 254 struct sensordev sensordev; 255 struct sdlim_t *sdlim, *next; 256 int mib[3]; 257 int h, t, i; 258 size_t sdlen = sizeof(sensordev); 259 260 if (TAILQ_EMPTY(&sdlims)) { 261 h = 0; 262 t = -1; 263 } else { 264 h = TAILQ_FIRST(&sdlims)->dev; 265 t = TAILQ_LAST(&sdlims, sdlimhead_t)->dev; 266 } 267 sdlim = TAILQ_FIRST(&sdlims); 268 269 mib[0] = CTL_HW; 270 mib[1] = HW_SENSORS; 271 /* look ahead for 4 more sensordevs */ 272 for (i = h; i <= t + 4; i++) { 273 if (sdlim != NULL && i > sdlim->dev) 274 sdlim = TAILQ_NEXT(sdlim, entries); 275 if (sdlim == NULL && i <= t) 276 syslog(LOG_ALERT, "inconsistent sdlim logic"); 277 mib[2] = i; 278 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { 279 if (errno != ENOENT) 280 warn("sysctl"); 281 if (sdlim != NULL && i == sdlim->dev) { 282 next = TAILQ_NEXT(sdlim, entries); 283 TAILQ_REMOVE(&sdlims, sdlim, entries); 284 syslog(LOG_INFO, "%s has disappeared", 285 sdlim->dxname); 286 destroy_sdlim(sdlim); 287 sdlim = next; 288 } 289 continue; 290 } 291 if (sdlim != NULL && i == sdlim->dev) { 292 if (strcmp(sdlim->dxname, sensordev.xname) == 0) { 293 check_sdlim(sdlim, this_check); 294 continue; 295 } else { 296 next = TAILQ_NEXT(sdlim, entries); 297 TAILQ_REMOVE(&sdlims, sdlim, entries); 298 syslog(LOG_INFO, "%s has been replaced", 299 sdlim->dxname); 300 destroy_sdlim(sdlim); 301 sdlim = next; 302 } 303 } 304 next = create_sdlim(&sensordev); 305 /* inserting next before sdlim */ 306 if (sdlim != NULL) 307 TAILQ_INSERT_BEFORE(sdlim, next, entries); 308 else 309 TAILQ_INSERT_TAIL(&sdlims, next, entries); 310 syslog(LOG_INFO, "%s has appeared", next->dxname); 311 sdlim = next; 312 parse_config_sdlim(sdlim, configfile); 313 check_sdlim(sdlim, this_check); 314 } 315 316 if (TAILQ_EMPTY(&sdlims)) 317 return; 318 /* Ensure that our queue is consistent. */ 319 for (sdlim = TAILQ_FIRST(&sdlims); 320 (next = TAILQ_NEXT(sdlim, entries)) != NULL; 321 sdlim = next) 322 if (sdlim->dev > next->dev) 323 syslog(LOG_ALERT, "inconsistent sdlims queue"); 324 } 325 326 void 327 check_sdlim(struct sdlim_t *sdlim, time_t this_check) 328 { 329 struct sensor sensor; 330 struct limits_t *limit; 331 size_t len; 332 int mib[5]; 333 334 mib[0] = CTL_HW; 335 mib[1] = HW_SENSORS; 336 mib[2] = sdlim->dev; 337 len = sizeof(sensor); 338 339 TAILQ_FOREACH(limit, &sdlim->limits, entries) { 340 if ((limit->flags & SENSORSD_L_ISTATUS) && 341 !(limit->flags & SENSORSD_L_USERLIMIT)) 342 continue; 343 344 mib[3] = limit->type; 345 mib[4] = limit->numt; 346 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1) 347 err(1, "sysctl"); 348 349 if (!(limit->flags & SENSORSD_L_ISTATUS)) { 350 enum sensor_status newastatus = sensor.status; 351 352 if (limit->astatus != newastatus) { 353 if (limit->astatus2 != newastatus) { 354 limit->astatus2 = newastatus; 355 limit->acount = 0; 356 } else if (++limit->acount >= 3) { 357 limit->last_val = sensor.value; 358 limit->astatus2 = 359 limit->astatus = newastatus; 360 limit->astatus_changed = this_check; 361 } 362 } 363 } 364 365 if (limit->flags & SENSORSD_L_USERLIMIT) { 366 enum sensorsd_s_status newustatus; 367 368 if (sensor.flags & SENSOR_FINVALID) 369 newustatus = SENSORSD_S_INVALID; 370 else if (sensor.value > limit->upper) 371 newustatus = SENSORSD_S_ABOVE; 372 else if (sensor.value < limit->lower) 373 newustatus = SENSORSD_S_BELOW; 374 else 375 newustatus = SENSORSD_S_WITHIN; 376 377 if (limit->ustatus != newustatus) { 378 if (limit->ustatus2 != newustatus) { 379 limit->ustatus2 = newustatus; 380 limit->ucount = 0; 381 } else if (++limit->ucount >= 3) { 382 limit->last_val = sensor.value; 383 limit->ustatus2 = 384 limit->ustatus = newustatus; 385 limit->ustatus_changed = this_check; 386 } 387 } 388 } 389 } 390 } 391 392 void 393 execute(char *command) 394 { 395 char *argp[] = {"sh", "-c", command, NULL}; 396 397 switch (fork()) { 398 case -1: 399 syslog(LOG_CRIT, "execute: fork() failed"); 400 break; 401 case 0: 402 execv("/bin/sh", argp); 403 _exit(1); 404 /* NOTREACHED */ 405 default: 406 break; 407 } 408 } 409 410 void 411 report(time_t last_report) 412 { 413 struct sdlim_t *sdlim; 414 415 TAILQ_FOREACH(sdlim, &sdlims, entries) 416 report_sdlim(sdlim, last_report); 417 } 418 419 void 420 report_sdlim(struct sdlim_t *sdlim, time_t last_report) 421 { 422 struct limits_t *limit; 423 424 TAILQ_FOREACH(limit, &sdlim->limits, entries) { 425 if ((limit->astatus_changed <= last_report) && 426 (limit->ustatus_changed <= last_report)) 427 continue; 428 429 if (limit->astatus_changed > last_report) { 430 const char *as = NULL; 431 432 switch (limit->astatus) { 433 case SENSOR_S_UNSPEC: 434 as = ""; 435 break; 436 case SENSOR_S_OK: 437 as = ", OK"; 438 break; 439 case SENSOR_S_WARN: 440 as = ", WARN"; 441 break; 442 case SENSOR_S_CRIT: 443 as = ", CRITICAL"; 444 break; 445 case SENSOR_S_UNKNOWN: 446 as = ", UNKNOWN"; 447 break; 448 } 449 syslog(limit->astatus == SENSOR_S_OK ? LOG_INFO : 450 LOG_ALERT, "%s.%s%d: %s%s", 451 sdlim->dxname, sensor_type_s[limit->type], 452 limit->numt, 453 print_sensor(limit->type, limit->last_val), as); 454 } 455 456 if (limit->ustatus_changed > last_report) { 457 char us[BUFSIZ]; 458 459 switch (limit->ustatus) { 460 case SENSORSD_S_UNSPEC: 461 snprintf(us, sizeof(us), 462 "ustatus uninitialised"); 463 break; 464 case SENSORSD_S_INVALID: 465 snprintf(us, sizeof(us), "marked invalid"); 466 break; 467 case SENSORSD_S_WITHIN: 468 snprintf(us, sizeof(us), "within limits: %s", 469 print_sensor(limit->type, limit->last_val)); 470 break; 471 case SENSORSD_S_ABOVE: 472 snprintf(us, sizeof(us), "exceeds limits: %s is above %s", 473 print_sensor(limit->type, limit->last_val), 474 print_sensor(limit->type, limit->upper)); 475 break; 476 case SENSORSD_S_BELOW: 477 snprintf(us, sizeof(us), "exceeds limits: %s is below %s", 478 print_sensor(limit->type, limit->last_val), 479 print_sensor(limit->type, limit->lower)); 480 break; 481 } 482 syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO : 483 LOG_ALERT, "%s.%s%d: %s", 484 sdlim->dxname, sensor_type_s[limit->type], 485 limit->numt, us); 486 } 487 488 if (limit->command) { 489 int i = 0, n = 0, r; 490 char *cmd = limit->command; 491 char buf[BUFSIZ]; 492 int len = sizeof(buf); 493 494 buf[0] = '\0'; 495 for (i = n = 0; n < len; ++i) { 496 if (cmd[i] == '\0') { 497 buf[n++] = '\0'; 498 break; 499 } 500 if (cmd[i] != '%') { 501 buf[n++] = limit->command[i]; 502 continue; 503 } 504 i++; 505 if (cmd[i] == '\0') { 506 buf[n++] = '\0'; 507 break; 508 } 509 510 switch (cmd[i]) { 511 case 'x': 512 r = snprintf(&buf[n], len - n, "%s", 513 sdlim->dxname); 514 break; 515 case 't': 516 r = snprintf(&buf[n], len - n, "%s", 517 sensor_type_s[limit->type]); 518 break; 519 case 'n': 520 r = snprintf(&buf[n], len - n, "%d", 521 limit->numt); 522 break; 523 case 'l': 524 { 525 char *s = ""; 526 switch(limit->ustatus){ 527 case SENSORSD_S_UNSPEC: 528 s = "uninitialised"; 529 break; 530 case SENSORSD_S_INVALID: 531 s = "invalid"; 532 break; 533 case SENSORSD_S_WITHIN: 534 s = "within"; 535 break; 536 case SENSORSD_S_ABOVE: 537 s = "above"; 538 break; 539 case SENSORSD_S_BELOW: 540 s = "below"; 541 break; 542 } 543 r = snprintf(&buf[n], len - n, "%s", 544 s); 545 break; 546 } 547 case 's': 548 { 549 char *s; 550 switch(limit->astatus){ 551 case SENSOR_S_UNSPEC: 552 s = "UNSPEC"; 553 break; 554 case SENSOR_S_OK: 555 s = "OK"; 556 break; 557 case SENSOR_S_WARN: 558 s = "WARNING"; 559 break; 560 case SENSOR_S_CRIT: 561 s = "CRITICAL"; 562 break; 563 default: 564 s = "UNKNOWN"; 565 } 566 r = snprintf(&buf[n], len - n, "%s", 567 s); 568 break; 569 } 570 case '2': 571 r = snprintf(&buf[n], len - n, "%s", 572 print_sensor(limit->type, 573 limit->last_val)); 574 break; 575 case '3': 576 r = snprintf(&buf[n], len - n, "%s", 577 print_sensor(limit->type, 578 limit->lower)); 579 break; 580 case '4': 581 r = snprintf(&buf[n], len - n, "%s", 582 print_sensor(limit->type, 583 limit->upper)); 584 break; 585 default: 586 r = snprintf(&buf[n], len - n, "%%%c", 587 cmd[i]); 588 break; 589 } 590 if (r < 0 || (r >= len - n)) { 591 syslog(LOG_CRIT, "could not parse " 592 "command"); 593 return; 594 } 595 if (r > 0) 596 n += r; 597 } 598 if (buf[0]) 599 execute(buf); 600 } 601 } 602 } 603 604 const char *drvstat[] = { 605 NULL, "empty", "ready", "powerup", "online", "idle", "active", 606 "rebuild", "powerdown", "fail", "pfail" 607 }; 608 609 static char * 610 print_sensor(enum sensor_type type, int64_t value) 611 { 612 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */ 613 static int idx; 614 char *fbuf; 615 616 fbuf = rfbuf[idx++]; 617 if (idx == RFBUFCNT) 618 idx = 0; 619 620 switch (type) { 621 case SENSOR_TEMP: 622 snprintf(fbuf, RFBUFSIZ, "%.2f degC", 623 (value - 273150000) / 1000000.0); 624 break; 625 case SENSOR_FANRPM: 626 snprintf(fbuf, RFBUFSIZ, "%lld RPM", value); 627 break; 628 case SENSOR_VOLTS_DC: 629 snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0); 630 break; 631 case SENSOR_VOLTS_AC: 632 snprintf(fbuf, RFBUFSIZ, "%.2f V AC", value / 1000000.0); 633 break; 634 case SENSOR_WATTS: 635 snprintf(fbuf, RFBUFSIZ, "%.2f W", value / 1000000.0); 636 break; 637 case SENSOR_AMPS: 638 snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0); 639 break; 640 case SENSOR_WATTHOUR: 641 snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0); 642 break; 643 case SENSOR_AMPHOUR: 644 snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0); 645 break; 646 case SENSOR_INDICATOR: 647 snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off"); 648 break; 649 case SENSOR_INTEGER: 650 snprintf(fbuf, RFBUFSIZ, "%lld", value); 651 break; 652 case SENSOR_PERCENT: 653 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0); 654 break; 655 case SENSOR_LUX: 656 snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0); 657 break; 658 case SENSOR_DRIVE: 659 if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0])) 660 snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]); 661 else 662 snprintf(fbuf, RFBUFSIZ, "%lld ???", value); 663 break; 664 case SENSOR_TIMEDELTA: 665 snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0); 666 break; 667 case SENSOR_HUMIDITY: 668 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0); 669 break; 670 case SENSOR_FREQ: 671 snprintf(fbuf, RFBUFSIZ, "%.2f Hz", value / 1000000.0); 672 break; 673 case SENSOR_ANGLE: 674 snprintf(fbuf, RFBUFSIZ, "%lld", value); 675 break; 676 case SENSOR_DISTANCE: 677 snprintf(fbuf, RFBUFSIZ, "%.2f mm", value / 1000.0); 678 break; 679 case SENSOR_PRESSURE: 680 snprintf(fbuf, RFBUFSIZ, "%.2f Pa", value / 1000.0); 681 break; 682 case SENSOR_ACCEL: 683 snprintf(fbuf, RFBUFSIZ, "%2.4f m/s^2", value / 1000000.0); 684 break; 685 default: 686 snprintf(fbuf, RFBUFSIZ, "%lld ???", value); 687 } 688 689 return (fbuf); 690 } 691 692 void 693 parse_config(char *cf) 694 { 695 struct sdlim_t *sdlim; 696 697 TAILQ_FOREACH(sdlim, &sdlims, entries) 698 parse_config_sdlim(sdlim, cf); 699 } 700 701 void 702 parse_config_sdlim(struct sdlim_t *sdlim, char *cf) 703 { 704 struct limits_t *p; 705 char *buf = NULL, *ebuf = NULL; 706 char node[48]; 707 char *cfa[2]; 708 709 cfa[0] = cf; 710 cfa[1] = NULL; 711 712 TAILQ_FOREACH(p, &sdlim->limits, entries) { 713 snprintf(node, sizeof(node), "hw.sensors.%s.%s%d", 714 sdlim->dxname, sensor_type_s[p->type], p->numt); 715 p->flags = 0; 716 if (cgetent(&buf, cfa, node) != 0) 717 if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0) 718 continue; 719 if (cgetcap(buf, "istatus", ':')) 720 p->flags |= SENSORSD_L_ISTATUS; 721 if (cgetstr(buf, "low", &ebuf) < 0) 722 ebuf = NULL; 723 p->lower = get_val(ebuf, 0, p->type); 724 if (cgetstr(buf, "high", &ebuf) < 0) 725 ebuf = NULL; 726 p->upper = get_val(ebuf, 1, p->type); 727 if (cgetstr(buf, "command", &ebuf) < 0) 728 ebuf = NULL; 729 if (ebuf) 730 asprintf(&(p->command), "%s", ebuf); 731 free(buf); 732 buf = NULL; 733 if (p->lower != LLONG_MIN || p->upper != LLONG_MAX) 734 p->flags |= SENSORSD_L_USERLIMIT; 735 } 736 } 737 738 int64_t 739 get_val(char *buf, int upper, enum sensor_type type) 740 { 741 double val; 742 int64_t rval = 0; 743 char *p; 744 745 if (buf == NULL) { 746 if (upper) 747 return (LLONG_MAX); 748 else 749 return (LLONG_MIN); 750 } 751 752 val = strtod(buf, &p); 753 if (buf == p) 754 err(1, "incorrect value: %s", buf); 755 756 switch(type) { 757 case SENSOR_TEMP: 758 switch(*p) { 759 case 'C': 760 printf("C"); 761 rval = val * 1000 * 1000 + 273150000; 762 break; 763 case 'F': 764 printf("F"); 765 rval = (val * 1000 * 1000 + 459670000) / 9 * 5; 766 break; 767 default: 768 errx(1, "unknown unit %s for temp sensor", p); 769 } 770 break; 771 case SENSOR_FANRPM: 772 rval = val; 773 break; 774 case SENSOR_VOLTS_DC: 775 case SENSOR_VOLTS_AC: 776 if (*p != 'V') 777 errx(1, "unknown unit %s for voltage sensor", p); 778 rval = val * 1000 * 1000; 779 break; 780 case SENSOR_PERCENT: 781 rval = val * 1000.0; 782 break; 783 case SENSOR_INDICATOR: 784 case SENSOR_INTEGER: 785 case SENSOR_DRIVE: 786 case SENSOR_ANGLE: 787 rval = val; 788 break; 789 case SENSOR_WATTS: 790 case SENSOR_AMPS: 791 case SENSOR_WATTHOUR: 792 case SENSOR_AMPHOUR: 793 case SENSOR_LUX: 794 case SENSOR_FREQ: 795 case SENSOR_ACCEL: 796 rval = val * 1000 * 1000; 797 break; 798 case SENSOR_TIMEDELTA: 799 rval = val * 1000 * 1000 * 1000; 800 break; 801 case SENSOR_HUMIDITY: 802 case SENSOR_DISTANCE: 803 case SENSOR_PRESSURE: 804 rval = val * 1000.0; 805 break; 806 default: 807 errx(1, "unsupported sensor type"); 808 /* not reached */ 809 } 810 free(buf); 811 return (rval); 812 } 813 814 /* ARGSUSED */ 815 void 816 reparse_cfg(int signo) 817 { 818 reload = 1; 819 } 820