1 /* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */ 2 /* $DragonFly: src/usr.sbin/sensorsd/sensorsd.c,v 1.1 2007/10/02 12:57:01 hasso Exp $ */ 3 4 /* 5 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 6 * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com> 7 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/cdefs.h> 23 #include <sys/param.h> 24 #include <sys/sysctl.h> 25 #include <sys/sensors.h> 26 27 #include <err.h> 28 #include <errno.h> 29 #include <signal.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <syslog.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #define RFBUFSIZ 28 /* buffer size for print_sensor */ 38 #define RFBUFCNT 4 /* ring buffers */ 39 #define REPORT_PERIOD 60 /* report every n seconds */ 40 #define CHECK_PERIOD 20 /* check every n seconds */ 41 42 enum sensorsd_s_status { 43 SENSORSD_S_UNSPEC, /* status is unspecified */ 44 SENSORSD_S_INVALID, /* status is invalid, per SENSOR_FINVALID */ 45 SENSORSD_S_WITHIN, /* status is within limits */ 46 SENSORSD_S_OUTSIDE /* status is outside limits */ 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 struct sdlim_t *create_sdlim(struct sensordev *); 80 void check(void); 81 void check_sdlim(struct sdlim_t *); 82 void execute(char *); 83 void report(time_t); 84 void report_sdlim(struct sdlim_t *, time_t); 85 static char *print_sensor(enum sensor_type, int64_t); 86 void parse_config(char *); 87 void parse_config_sdlim(struct sdlim_t *, char **); 88 int64_t get_val(char *, int, enum sensor_type); 89 void reparse_cfg(int); 90 91 TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims); 92 93 char *configfile; 94 volatile sig_atomic_t reload = 0; 95 int debug = 0; 96 97 void 98 usage(void) 99 { 100 extern char *__progname; 101 fprintf(stderr, "usage: %s [-d]\n", __progname); 102 exit(1); 103 } 104 105 int 106 main(int argc, char *argv[]) 107 { 108 struct sensordev sensordev; 109 struct sdlim_t *sdlim; 110 size_t sdlen = sizeof(sensordev); 111 time_t next_report, last_report = 0, next_check; 112 int mib[3], dev; 113 int sleeptime, sensor_cnt = 0, ch; 114 115 while ((ch = getopt(argc, argv, "d")) != -1) { 116 switch (ch) { 117 case 'd': 118 debug = 1; 119 break; 120 default: 121 usage(); 122 } 123 } 124 125 mib[0] = CTL_HW; 126 mib[1] = HW_SENSORS; 127 128 for (dev = 0; dev < MAXSENSORDEVICES; dev++) { 129 mib[2] = dev; 130 if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { 131 if (errno != ENOENT) 132 warn("sysctl"); 133 continue; 134 } 135 sdlim = create_sdlim(&sensordev); 136 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries); 137 sensor_cnt += sdlim->sensor_cnt; 138 } 139 140 if (sensor_cnt == 0) 141 errx(1, "no sensors found"); 142 143 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 144 145 if (configfile == NULL) 146 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1) 147 err(1, "out of memory"); 148 parse_config(configfile); 149 150 if (debug == 0 && daemon(0, 0) == -1) 151 err(1, "unable to fork"); 152 153 signal(SIGHUP, reparse_cfg); 154 signal(SIGCHLD, SIG_IGN); 155 156 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt); 157 158 next_check = next_report = time(NULL); 159 160 for (;;) { 161 if (reload) { 162 parse_config(configfile); 163 syslog(LOG_INFO, "configuration reloaded"); 164 reload = 0; 165 } 166 if (next_check <= time(NULL)) { 167 check(); 168 next_check = time(NULL) + CHECK_PERIOD; 169 } 170 if (next_report <= time(NULL)) { 171 report(last_report); 172 last_report = next_report; 173 next_report = time(NULL) + REPORT_PERIOD; 174 } 175 if (next_report < next_check) 176 sleeptime = next_report - time(NULL); 177 else 178 sleeptime = next_check - time(NULL); 179 if (sleeptime > 0) 180 sleep(sleeptime); 181 } 182 } 183 184 struct sdlim_t * 185 create_sdlim(struct sensordev *snsrdev) 186 { 187 struct sensor sensor; 188 struct sdlim_t *sdlim; 189 struct limits_t *limit; 190 size_t slen = sizeof(sensor); 191 int mib[5], numt; 192 enum sensor_type type; 193 194 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL) 195 err(1, "calloc"); 196 197 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname)); 198 199 mib[0] = CTL_HW; 200 mib[1] = HW_SENSORS; 201 mib[2] = sdlim->dev = snsrdev->num; 202 203 TAILQ_INIT(&sdlim->limits); 204 205 for (type = 0; type < SENSOR_MAX_TYPES; type++) { 206 mib[3] = type; 207 for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) { 208 mib[4] = numt; 209 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { 210 if (errno != ENOENT) 211 warn("sysctl"); 212 continue; 213 } 214 if ((limit = calloc(1, sizeof(struct limits_t))) == 215 NULL) 216 err(1, "calloc"); 217 limit->type = type; 218 limit->numt = numt; 219 TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries); 220 sdlim->sensor_cnt++; 221 } 222 } 223 224 return (sdlim); 225 } 226 227 void 228 check(void) 229 { 230 struct sdlim_t *sdlim; 231 232 TAILQ_FOREACH(sdlim, &sdlims, entries) 233 check_sdlim(sdlim); 234 } 235 236 void 237 check_sdlim(struct sdlim_t *sdlim) 238 { 239 struct sensor sensor; 240 struct limits_t *limit; 241 size_t len; 242 int mib[5]; 243 244 mib[0] = CTL_HW; 245 mib[1] = HW_SENSORS; 246 mib[2] = sdlim->dev; 247 len = sizeof(sensor); 248 249 TAILQ_FOREACH(limit, &sdlim->limits, entries) { 250 if ((limit->flags & SENSORSD_L_ISTATUS) && 251 !(limit->flags & SENSORSD_L_USERLIMIT)) 252 continue; 253 254 mib[3] = limit->type; 255 mib[4] = limit->numt; 256 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1) 257 err(1, "sysctl"); 258 259 if (!(limit->flags & SENSORSD_L_ISTATUS)) { 260 enum sensor_status newastatus = sensor.status; 261 262 if (limit->astatus != newastatus) { 263 if (limit->astatus2 != newastatus) { 264 limit->astatus2 = newastatus; 265 limit->acount = 0; 266 } else if (++limit->acount >= 3) { 267 limit->last_val = sensor.value; 268 limit->astatus2 = 269 limit->astatus = newastatus; 270 limit->astatus_changed = time(NULL); 271 } 272 } 273 } 274 275 if (limit->flags & SENSORSD_L_USERLIMIT) { 276 enum sensorsd_s_status newustatus; 277 278 if (sensor.flags & SENSOR_FINVALID) 279 newustatus = SENSORSD_S_INVALID; 280 else if (sensor.value > limit->upper || 281 sensor.value < limit->lower) 282 newustatus = SENSORSD_S_OUTSIDE; 283 else 284 newustatus = SENSORSD_S_WITHIN; 285 286 if (limit->ustatus != newustatus) { 287 if (limit->ustatus2 != newustatus) { 288 limit->ustatus2 = newustatus; 289 limit->ucount = 0; 290 } else if (++limit->ucount >= 3) { 291 limit->last_val = sensor.value; 292 limit->ustatus2 = 293 limit->ustatus = newustatus; 294 limit->ustatus_changed = time(NULL); 295 } 296 } 297 } 298 } 299 } 300 301 void 302 execute(char *command) 303 { 304 char *argp[] = {"sh", "-c", command, NULL}; 305 306 switch (fork()) { 307 case -1: 308 syslog(LOG_CRIT, "execute: fork() failed"); 309 break; 310 case 0: 311 execv("/bin/sh", argp); 312 _exit(1); 313 /* NOTREACHED */ 314 default: 315 break; 316 } 317 } 318 319 void 320 report(time_t last_report) 321 { 322 struct sdlim_t *sdlim; 323 324 TAILQ_FOREACH(sdlim, &sdlims, entries) 325 report_sdlim(sdlim, last_report); 326 } 327 328 void 329 report_sdlim(struct sdlim_t *sdlim, time_t last_report) 330 { 331 struct limits_t *limit; 332 333 TAILQ_FOREACH(limit, &sdlim->limits, entries) { 334 if ((limit->astatus_changed <= last_report) && 335 (limit->ustatus_changed <= last_report)) 336 continue; 337 338 if (limit->astatus_changed > last_report) { 339 const char *as = NULL; 340 341 switch (limit->astatus) { 342 case SENSOR_S_UNSPEC: 343 as = ""; 344 break; 345 case SENSOR_S_OK: 346 as = ", OK"; 347 break; 348 case SENSOR_S_WARN: 349 as = ", WARN"; 350 break; 351 case SENSOR_S_CRIT: 352 as = ", CRITICAL"; 353 break; 354 case SENSOR_S_UNKNOWN: 355 as = ", UNKNOWN"; 356 break; 357 } 358 syslog(LOG_ALERT, "%s.%s%d: %s%s", 359 sdlim->dxname, sensor_type_s[limit->type], 360 limit->numt, 361 print_sensor(limit->type, limit->last_val), as); 362 } 363 364 if (limit->ustatus_changed > last_report) { 365 char us[BUFSIZ]; 366 367 switch (limit->ustatus) { 368 case SENSORSD_S_UNSPEC: 369 snprintf(us, sizeof(us), 370 "ustatus uninitialised"); 371 break; 372 case SENSORSD_S_INVALID: 373 snprintf(us, sizeof(us), "marked invalid"); 374 break; 375 case SENSORSD_S_WITHIN: 376 snprintf(us, sizeof(us), "within limits: %s", 377 print_sensor(limit->type, limit->last_val)); 378 break; 379 case SENSORSD_S_OUTSIDE: 380 snprintf(us, sizeof(us), "exceeds limits: %s", 381 print_sensor(limit->type, limit->last_val)); 382 break; 383 } 384 syslog(LOG_ALERT, "%s.%s%d: %s", 385 sdlim->dxname, sensor_type_s[limit->type], 386 limit->numt, us); 387 } 388 389 if (limit->command) { 390 int i = 0, n = 0, r; 391 char *cmd = limit->command; 392 char buf[BUFSIZ]; 393 int len = sizeof(buf); 394 395 buf[0] = '\0'; 396 for (i = n = 0; n < len; ++i) { 397 if (cmd[i] == '\0') { 398 buf[n++] = '\0'; 399 break; 400 } 401 if (cmd[i] != '%') { 402 buf[n++] = limit->command[i]; 403 continue; 404 } 405 i++; 406 if (cmd[i] == '\0') { 407 buf[n++] = '\0'; 408 break; 409 } 410 411 switch (cmd[i]) { 412 case 'x': 413 r = snprintf(&buf[n], len - n, "%s", 414 sdlim->dxname); 415 break; 416 case 't': 417 r = snprintf(&buf[n], len - n, "%s", 418 sensor_type_s[limit->type]); 419 break; 420 case 'n': 421 r = snprintf(&buf[n], len - n, "%d", 422 limit->numt); 423 break; 424 case '2': 425 r = snprintf(&buf[n], len - n, "%s", 426 print_sensor(limit->type, 427 limit->last_val)); 428 break; 429 case '3': 430 r = snprintf(&buf[n], len - n, "%s", 431 print_sensor(limit->type, 432 limit->lower)); 433 break; 434 case '4': 435 r = snprintf(&buf[n], len - n, "%s", 436 print_sensor(limit->type, 437 limit->upper)); 438 break; 439 default: 440 r = snprintf(&buf[n], len - n, "%%%c", 441 cmd[i]); 442 break; 443 } 444 if (r < 0 || (r >= len - n)) { 445 syslog(LOG_CRIT, "could not parse " 446 "command"); 447 return; 448 } 449 if (r > 0) 450 n += r; 451 } 452 if (buf[0]) 453 execute(buf); 454 } 455 } 456 } 457 458 const char *drvstat[] = { 459 NULL, "empty", "ready", "powerup", "online", "idle", "active", 460 "rebuild", "powerdown", "fail", "pfail" 461 }; 462 463 static char * 464 print_sensor(enum sensor_type type, int64_t value) 465 { 466 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */ 467 static int idx; 468 char *fbuf; 469 470 fbuf = rfbuf[idx++]; 471 if (idx == RFBUFCNT) 472 idx = 0; 473 474 switch (type) { 475 case SENSOR_TEMP: 476 snprintf(fbuf, RFBUFSIZ, "%.2f degC", 477 (value - 273150000) / 1000000.0); 478 break; 479 case SENSOR_FANRPM: 480 snprintf(fbuf, RFBUFSIZ, "%lld RPM", value); 481 break; 482 case SENSOR_VOLTS_DC: 483 snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0); 484 break; 485 case SENSOR_AMPS: 486 snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0); 487 break; 488 case SENSOR_WATTHOUR: 489 snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0); 490 break; 491 case SENSOR_AMPHOUR: 492 snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0); 493 break; 494 case SENSOR_INDICATOR: 495 snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off"); 496 break; 497 case SENSOR_INTEGER: 498 snprintf(fbuf, RFBUFSIZ, "%lld", value); 499 break; 500 case SENSOR_PERCENT: 501 snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0); 502 break; 503 case SENSOR_LUX: 504 snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0); 505 break; 506 case SENSOR_DRIVE: 507 if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0])) 508 snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]); 509 else 510 snprintf(fbuf, RFBUFSIZ, "%lld ???", value); 511 break; 512 case SENSOR_TIMEDELTA: 513 snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0); 514 break; 515 default: 516 snprintf(fbuf, RFBUFSIZ, "%lld ???", value); 517 } 518 519 return (fbuf); 520 } 521 522 void 523 parse_config(char *cf) 524 { 525 struct sdlim_t *sdlim; 526 char **cfa; 527 528 if ((cfa = calloc(2, sizeof(char *))) == NULL) 529 err(1, "calloc"); 530 cfa[0] = cf; 531 cfa[1] = NULL; 532 533 TAILQ_FOREACH(sdlim, &sdlims, entries) 534 parse_config_sdlim(sdlim, cfa); 535 free(cfa); 536 } 537 538 void 539 parse_config_sdlim(struct sdlim_t *sdlim, char **cfa) 540 { 541 struct limits_t *p; 542 char *buf = NULL, *ebuf = NULL; 543 char node[48]; 544 545 TAILQ_FOREACH(p, &sdlim->limits, entries) { 546 snprintf(node, sizeof(node), "hw.sensors.%s.%s%d", 547 sdlim->dxname, sensor_type_s[p->type], p->numt); 548 p->flags = 0; 549 if (cgetent(&buf, cfa, node) != 0) 550 if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0) 551 continue; 552 if (cgetcap(buf, "istatus", ':')) 553 p->flags |= SENSORSD_L_ISTATUS; 554 if (cgetstr(buf, "low", &ebuf) < 0) 555 ebuf = NULL; 556 p->lower = get_val(ebuf, 0, p->type); 557 if (cgetstr(buf, "high", &ebuf) < 0) 558 ebuf = NULL; 559 p->upper = get_val(ebuf, 1, p->type); 560 if (cgetstr(buf, "command", &ebuf) < 0) 561 ebuf = NULL; 562 if (ebuf) 563 asprintf(&(p->command), "%s", ebuf); 564 free(buf); 565 buf = NULL; 566 if (p->lower != LLONG_MIN || p->upper != LLONG_MAX) 567 p->flags |= SENSORSD_L_USERLIMIT; 568 } 569 } 570 571 int64_t 572 get_val(char *buf, int upper, enum sensor_type type) 573 { 574 double val; 575 int64_t rval = 0; 576 char *p; 577 578 if (buf == NULL) { 579 if (upper) 580 return (LLONG_MAX); 581 else 582 return (LLONG_MIN); 583 } 584 585 val = strtod(buf, &p); 586 if (buf == p) 587 err(1, "incorrect value: %s", buf); 588 589 switch(type) { 590 case SENSOR_TEMP: 591 switch(*p) { 592 case 'C': 593 printf("C"); 594 rval = (val + 273.16) * 1000 * 1000; 595 break; 596 case 'F': 597 printf("F"); 598 rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000; 599 break; 600 default: 601 errx(1, "unknown unit %s for temp sensor", p); 602 } 603 break; 604 case SENSOR_FANRPM: 605 rval = val; 606 break; 607 case SENSOR_VOLTS_DC: 608 if (*p != 'V') 609 errx(1, "unknown unit %s for voltage sensor", p); 610 rval = val * 1000 * 1000; 611 break; 612 case SENSOR_PERCENT: 613 rval = val * 1000.0; 614 break; 615 case SENSOR_INDICATOR: 616 case SENSOR_INTEGER: 617 case SENSOR_DRIVE: 618 rval = val; 619 break; 620 case SENSOR_AMPS: 621 case SENSOR_WATTHOUR: 622 case SENSOR_AMPHOUR: 623 case SENSOR_LUX: 624 rval = val * 1000 * 1000; 625 break; 626 case SENSOR_TIMEDELTA: 627 rval = val * 1000 * 1000 * 1000; 628 break; 629 default: 630 errx(1, "unsupported sensor type"); 631 /* not reached */ 632 } 633 free(buf); 634 return (rval); 635 } 636 637 /* ARGSUSED */ 638 void 639 reparse_cfg(int signo) 640 { 641 reload = 1; 642 } 643