1 /* $OpenBSD: sensorsd.c,v 1.5 2003/10/01 02:43:17 kevlo 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_FREQ 60 /* report every n seconds */ 36 #define CHECK_FREQ 60 /* check every n seconds */ 37 38 int main(int, char *[]); 39 void check_sensors(); 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 int 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, "out of memory"); 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_FREQ; 135 } 136 if (next_report < time(NULL)) { 137 report(last_report); 138 last_report = next_report; 139 next_report = time(NULL) + REPORT_FREQ; 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() 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 / 1000 / 1000) - 273.16, 220 ((value / 1000 / 1000) - 273.16) * 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 cfa = calloc(2, sizeof(char *)); 245 cfa[0] = cf; 246 cfa[1] = NULL; 247 248 for (p = TAILQ_FIRST(&limits); p != NULL; p = next) { 249 next = TAILQ_NEXT(p, entries); 250 snprintf(node, sizeof(node), "hw.sensors.%d", p->num); 251 if (cgetent(&buf, cfa, node) != 0) 252 p->watch = 0; 253 else { 254 p->watch = 1; 255 watch_cnt++; 256 if (cgetstr(buf, "low", &ebuf) < 0) 257 ebuf = NULL; 258 p->lower = get_val(ebuf, 0, p->type); 259 if (cgetstr(buf, "high", &ebuf) < 0) 260 ebuf = NULL; 261 p->upper = get_val(ebuf, 1, p->type); 262 free(buf); 263 buf = NULL; 264 } 265 } 266 free(cfa); 267 return (watch_cnt); 268 } 269 270 int64_t 271 get_val(char *buf, int upper, enum sensor_type type) 272 { 273 double val; 274 int64_t rval = 0; 275 char *p; 276 277 if (buf == NULL) { 278 if (upper) 279 return (LLONG_MAX); 280 else 281 return (LLONG_MIN); 282 } 283 284 val = strtod(buf, &p); 285 if (buf == p) 286 err(1, "incorrect value: %s", buf); 287 288 switch(type) { 289 case SENSOR_TEMP: 290 switch(*p) { 291 case 'C': 292 printf ("C"); 293 rval = (val + 273.16) * 1000 * 1000; 294 break; 295 case 'F': 296 printf("F"); 297 rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000; 298 break; 299 default: 300 errx(1, "unknown unit %s for temp sensor", p); 301 } 302 break; 303 case SENSOR_FANRPM: 304 rval = val; 305 break; 306 case SENSOR_VOLTS_DC: 307 if (*p != 'V') 308 errx(1, "unknown unit %s for voltage sensor", p); 309 rval = val * 1000 * 1000; 310 break; 311 } 312 free(buf); 313 return (rval); 314 } 315 316 void 317 reparse_cfg(int signum) 318 { 319 reload = 1; 320 } 321