1 /* $OpenBSD: sensorsd.c,v 1.10 2004/04/11 20:02:00 otto Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/sysctl.h> 21 #include <sys/sensors.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <signal.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <syslog.h> 30 #include <time.h> 31 #include <unistd.h> 32 33 #define RFBUFSIZ 28 /* buffer size for print_sensor */ 34 #define RFBUFCNT 4 /* ring buffers */ 35 #define REPORT_PERIOD 60 /* report every n seconds */ 36 #define CHECK_PERIOD 60 /* check every n seconds */ 37 38 int main(int, char *[]); 39 void check_sensors(void); 40 void report(time_t); 41 static char *print_sensor(enum sensor_type, u_int64_t); 42 int parse_config(char *); 43 int64_t get_val(char *, int, enum sensor_type); 44 void reparse_cfg(int); 45 46 enum sensor_status { 47 STATUS_OK, 48 STATUS_FAIL 49 }; 50 51 struct limits_t { 52 TAILQ_ENTRY(limits_t) entries; 53 u_int8_t watch; 54 int num; /* sensor number */ 55 enum sensor_type type; /* sensor type */ 56 int64_t lower; /* lower limit */ 57 int64_t upper; /* upper limit */ 58 enum sensor_status status; /* last status */ 59 time_t status_changed; 60 int64_t last_val; 61 }; 62 63 TAILQ_HEAD(limits, limits_t) limits = TAILQ_HEAD_INITIALIZER(limits); 64 65 char *configfile; 66 volatile sig_atomic_t reload = 0; 67 68 int 69 main(int argc, char *argv[]) 70 { 71 struct sensor sensor; 72 struct limits_t *limit; 73 size_t len; 74 time_t next_report, last_report = 0; 75 time_t next_check; 76 int mib[3]; 77 int i, sleeptime, watch_cnt; 78 79 mib[0] = CTL_HW; 80 mib[1] = HW_SENSORS; 81 len = sizeof(sensor); 82 83 for (i = 0; i < 256; i++) { 84 mib[2] = i; 85 if (sysctl(mib, 3, &sensor, &len, NULL, 0) == -1) { 86 if (errno == ENXIO) 87 break; 88 else 89 err(1, "sysctl"); 90 } 91 if ((limit = calloc(1, sizeof(struct limits_t))) == NULL) 92 err(1, "calloc"); 93 limit->num = i; 94 limit->type = sensor.type; 95 TAILQ_INSERT_TAIL(&limits, limit, entries); 96 } 97 98 if (i == 0) 99 errx(1, "no sensors found"); 100 101 openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 102 103 if (configfile == NULL) 104 if (asprintf(&configfile, "/etc/sensorsd.conf") == -1) 105 err(1, "out of memory"); 106 if ((watch_cnt = parse_config(configfile)) == -1) 107 errx(1, "error in config file"); 108 109 if (watch_cnt == 0) 110 errx(1, "no watches defined"); 111 112 if (daemon(0, 0) == -1) 113 err(1, "unable to fork"); 114 115 signal(SIGHUP, reparse_cfg); 116 117 syslog(LOG_INFO, "startup, %d watches for %d sensors", watch_cnt, i); 118 119 next_check = next_report = time(NULL); 120 121 for (;;) { 122 if (reload) { 123 if ((watch_cnt = parse_config(configfile)) == -1) 124 syslog(LOG_CRIT, "error in config file %s", 125 configfile); 126 else 127 syslog(LOG_INFO, 128 "configuration reloaded, %d watches", 129 watch_cnt); 130 reload = 0; 131 } 132 if (next_check <= time(NULL)) { 133 check_sensors(); 134 next_check = time(NULL) + CHECK_PERIOD; 135 } 136 if (next_report <= time(NULL)) { 137 report(last_report); 138 last_report = next_report; 139 next_report = time(NULL) + REPORT_PERIOD; 140 } 141 if (next_report < next_check) 142 sleeptime = next_report - time(NULL); 143 else 144 sleeptime = next_check - time(NULL); 145 if (sleeptime > 0) 146 sleep(sleeptime); 147 } 148 } 149 150 void 151 check_sensors(void) 152 { 153 struct sensor sensor; 154 struct limits_t *limit; 155 size_t len; 156 int mib[3]; 157 int newstatus; 158 159 mib[0] = CTL_HW; 160 mib[1] = HW_SENSORS; 161 len = sizeof(sensor); 162 163 TAILQ_FOREACH(limit, &limits, entries) 164 if (limit->watch) { 165 mib[2] = limit->num; 166 if (sysctl(mib, 3, &sensor, &len, NULL, 0) == -1) 167 err(1, "sysctl"); 168 169 limit->last_val = sensor.value; 170 if (sensor.value > limit->upper || 171 sensor.value < limit->lower) 172 newstatus = STATUS_FAIL; 173 else 174 newstatus = STATUS_OK; 175 176 if (limit->status != newstatus) { 177 limit->status = newstatus; 178 limit->status_changed = time(NULL); 179 } 180 } 181 } 182 183 void 184 report(time_t last_report) 185 { 186 struct limits_t *limit = NULL; 187 188 TAILQ_FOREACH(limit, &limits, entries) 189 if (limit->status_changed > last_report) { 190 if (limit->status == STATUS_FAIL) 191 syslog(LOG_ALERT, 192 "failure for hw.sensors.%d: " 193 "%s not within limits", 194 limit->num, 195 print_sensor(limit->type, limit->last_val)); 196 else 197 syslog(LOG_ALERT, 198 "hw.sensors.%d within limits again, " 199 "current value %s", 200 limit->num, 201 print_sensor(limit->type, limit->last_val)); 202 } 203 } 204 205 static char * 206 print_sensor(enum sensor_type type, u_int64_t value) 207 { 208 static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */ 209 static int idx; 210 char *fbuf; 211 212 fbuf = rfbuf[idx++]; 213 if (idx == RFBUFCNT) 214 idx = 0; 215 216 switch (type) { 217 case SENSOR_TEMP: 218 snprintf(fbuf, RFBUFSIZ, "%.2fC/%.2fF", 219 (value - 273150000) / 1000000.0, 220 (value - 273150000) / 1000000.0 * 9 / 5 + 32); 221 break; 222 case SENSOR_FANRPM: 223 snprintf(fbuf, RFBUFSIZ, "%lld RPM", value); 224 break; 225 case SENSOR_VOLTS_DC: 226 snprintf(fbuf, RFBUFSIZ, "%.2fV", value / 1000.0 / 1000.0); 227 break; 228 default: 229 snprintf(fbuf, RFBUFSIZ, "%lld ???", value); 230 } 231 232 return (fbuf); 233 } 234 235 int 236 parse_config(char *cf) 237 { 238 struct limits_t *p, *next; 239 char *buf = NULL, *ebuf = NULL; 240 char node[24]; 241 char **cfa; 242 int watch_cnt = 0; 243 244 if ((cfa = calloc(2, sizeof(char *))) == NULL) 245 err(1, "calloc"); 246 cfa[0] = cf; 247 cfa[1] = NULL; 248 249 for (p = TAILQ_FIRST(&limits); p != NULL; p = next) { 250 next = TAILQ_NEXT(p, entries); 251 snprintf(node, sizeof(node), "hw.sensors.%d", p->num); 252 if (cgetent(&buf, cfa, node) != 0) 253 p->watch = 0; 254 else { 255 p->watch = 1; 256 watch_cnt++; 257 if (cgetstr(buf, "low", &ebuf) < 0) 258 ebuf = NULL; 259 p->lower = get_val(ebuf, 0, p->type); 260 if (cgetstr(buf, "high", &ebuf) < 0) 261 ebuf = NULL; 262 p->upper = get_val(ebuf, 1, p->type); 263 free(buf); 264 buf = NULL; 265 } 266 } 267 free(cfa); 268 return (watch_cnt); 269 } 270 271 int64_t 272 get_val(char *buf, int upper, enum sensor_type type) 273 { 274 double val; 275 int64_t rval = 0; 276 char *p; 277 278 if (buf == NULL) { 279 if (upper) 280 return (LLONG_MAX); 281 else 282 return (LLONG_MIN); 283 } 284 285 val = strtod(buf, &p); 286 if (buf == p) 287 err(1, "incorrect value: %s", buf); 288 289 switch(type) { 290 case SENSOR_TEMP: 291 switch(*p) { 292 case 'C': 293 printf ("C"); 294 rval = (val + 273.16) * 1000 * 1000; 295 break; 296 case 'F': 297 printf("F"); 298 rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000; 299 break; 300 default: 301 errx(1, "unknown unit %s for temp sensor", p); 302 } 303 break; 304 case SENSOR_FANRPM: 305 rval = val; 306 break; 307 case SENSOR_VOLTS_DC: 308 if (*p != 'V') 309 errx(1, "unknown unit %s for voltage sensor", p); 310 rval = val * 1000 * 1000; 311 break; 312 default: 313 errx(1, "unsupported sensor type"); 314 /* not reached */ 315 } 316 free(buf); 317 return (rval); 318 } 319 320 void 321 reparse_cfg(int signum) 322 { 323 reload = 1; 324 } 325