MFC:
[dragonfly.git] / usr.sbin / sensorsd / sensorsd.c
blob31207f2ffcfd7f01378cc73743c80483bc246c8d
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 $ */
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>
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.
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.
22 #include <sys/cdefs.h>
23 #include <sys/param.h>
24 #include <sys/sysctl.h>
25 #include <sys/sensors.h>
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>
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 */
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 */
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 */
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;
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);
91 TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
93 char *configfile;
94 volatile sig_atomic_t reload = 0;
95 int debug = 0;
97 void
98 usage(void)
100 extern char *__progname;
101 fprintf(stderr, "usage: %s [-d]\n", __progname);
102 exit(1);
106 main(int argc, char *argv[])
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;
115 while ((ch = getopt(argc, argv, "d")) != -1) {
116 switch (ch) {
117 case 'd':
118 debug = 1;
119 break;
120 default:
121 usage();
125 mib[0] = CTL_HW;
126 mib[1] = HW_SENSORS;
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;
135 sdlim = create_sdlim(&sensordev);
136 TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
137 sensor_cnt += sdlim->sensor_cnt;
140 if (sensor_cnt == 0)
141 errx(1, "no sensors found");
143 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
145 if (configfile == NULL)
146 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
147 err(1, "out of memory");
148 parse_config(configfile);
150 if (debug == 0 && daemon(0, 0) == -1)
151 err(1, "unable to fork");
153 signal(SIGHUP, reparse_cfg);
154 signal(SIGCHLD, SIG_IGN);
156 syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
158 next_check = next_report = time(NULL);
160 for (;;) {
161 if (reload) {
162 parse_config(configfile);
163 syslog(LOG_INFO, "configuration reloaded");
164 reload = 0;
166 if (next_check <= time(NULL)) {
167 check();
168 next_check = time(NULL) + CHECK_PERIOD;
170 if (next_report <= time(NULL)) {
171 report(last_report);
172 last_report = next_report;
173 next_report = time(NULL) + REPORT_PERIOD;
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);
184 struct sdlim_t *
185 create_sdlim(struct sensordev *snsrdev)
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;
194 if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
195 err(1, "calloc");
197 strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
199 mib[0] = CTL_HW;
200 mib[1] = HW_SENSORS;
201 mib[2] = sdlim->dev = snsrdev->num;
203 TAILQ_INIT(&sdlim->limits);
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;
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++;
224 return (sdlim);
227 void
228 check(void)
230 struct sdlim_t *sdlim;
232 TAILQ_FOREACH(sdlim, &sdlims, entries)
233 check_sdlim(sdlim);
236 void
237 check_sdlim(struct sdlim_t *sdlim)
239 struct sensor sensor;
240 struct limits_t *limit;
241 size_t len;
242 int mib[5];
244 mib[0] = CTL_HW;
245 mib[1] = HW_SENSORS;
246 mib[2] = sdlim->dev;
247 len = sizeof(sensor);
249 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
250 if ((limit->flags & SENSORSD_L_ISTATUS) &&
251 !(limit->flags & SENSORSD_L_USERLIMIT))
252 continue;
254 mib[3] = limit->type;
255 mib[4] = limit->numt;
256 if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
257 err(1, "sysctl");
259 if (!(limit->flags & SENSORSD_L_ISTATUS)) {
260 enum sensor_status newastatus = sensor.status;
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);
275 if (limit->flags & SENSORSD_L_USERLIMIT) {
276 enum sensorsd_s_status newustatus;
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;
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);
301 void
302 execute(char *command)
304 char *argp[] = {"sh", "-c", command, NULL};
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;
319 void
320 report(time_t last_report)
322 struct sdlim_t *sdlim;
324 TAILQ_FOREACH(sdlim, &sdlims, entries)
325 report_sdlim(sdlim, last_report);
328 void
329 report_sdlim(struct sdlim_t *sdlim, time_t last_report)
331 struct limits_t *limit;
333 TAILQ_FOREACH(limit, &sdlim->limits, entries) {
334 if ((limit->astatus_changed <= last_report) &&
335 (limit->ustatus_changed <= last_report))
336 continue;
338 if (limit->astatus_changed > last_report) {
339 const char *as = NULL;
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;
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);
364 if (limit->ustatus_changed > last_report) {
365 char us[BUFSIZ];
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;
384 syslog(LOG_ALERT, "%s.%s%d: %s",
385 sdlim->dxname, sensor_type_s[limit->type],
386 limit->numt, us);
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);
395 buf[0] = '\0';
396 for (i = n = 0; n < len; ++i) {
397 if (cmd[i] == '\0') {
398 buf[n++] = '\0';
399 break;
401 if (cmd[i] != '%') {
402 buf[n++] = limit->command[i];
403 continue;
405 i++;
406 if (cmd[i] == '\0') {
407 buf[n++] = '\0';
408 break;
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;
444 if (r < 0 || (r >= len - n)) {
445 syslog(LOG_CRIT, "could not parse "
446 "command");
447 return;
449 if (r > 0)
450 n += r;
452 if (buf[0])
453 execute(buf);
458 const char *drvstat[] = {
459 NULL, "empty", "ready", "powerup", "online", "idle", "active",
460 "rebuild", "powerdown", "fail", "pfail"
463 static char *
464 print_sensor(enum sensor_type type, int64_t value)
466 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
467 static int idx;
468 char *fbuf;
470 fbuf = rfbuf[idx++];
471 if (idx == RFBUFCNT)
472 idx = 0;
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);
519 return (fbuf);
522 void
523 parse_config(char *cf)
525 struct sdlim_t *sdlim;
526 char **cfa;
528 if ((cfa = calloc(2, sizeof(char *))) == NULL)
529 err(1, "calloc");
530 cfa[0] = cf;
531 cfa[1] = NULL;
533 TAILQ_FOREACH(sdlim, &sdlims, entries)
534 parse_config_sdlim(sdlim, cfa);
535 free(cfa);
538 void
539 parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
541 struct limits_t *p;
542 char *buf = NULL, *ebuf = NULL;
543 char node[48];
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;
571 int64_t
572 get_val(char *buf, int upper, enum sensor_type type)
574 double val;
575 int64_t rval = 0;
576 char *p;
578 if (buf == NULL) {
579 if (upper)
580 return (LLONG_MAX);
581 else
582 return (LLONG_MIN);
585 val = strtod(buf, &p);
586 if (buf == p)
587 err(1, "incorrect value: %s", buf);
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);
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 */
633 free(buf);
634 return (rval);
637 /* ARGSUSED */
638 void
639 reparse_cfg(int signo)
641 reload = 1;