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