1 /*- 2 * Copyright (c) 2014 Luiz Otavio O Souza <loos@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/queue.h> 32 #include <sys/sysctl.h> 33 34 #include <bsnmp/snmpmod.h> 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <syslog.h> 40 41 #include "lm75_oid.h" 42 #include "lm75_tree.h" 43 44 #ifndef LM75BUF 45 #define LM75BUF 64 46 #endif 47 #define TZ_ZEROC 2732 48 #define UPDATE_INTERVAL 500 /* update interval in ticks */ 49 50 static struct lmodule *module; 51 52 static const struct asn_oid oid_lm75 = OIDX_begemotLm75; 53 54 /* the Object Resource registration index */ 55 static u_int lm75_index = 0; 56 57 /* Number of available sensors in the system. */ 58 static int lm75_sensors; 59 60 /* 61 * Structure that describes single sensor. 62 */ 63 struct lm75_snmp_sensor { 64 TAILQ_ENTRY(lm75_snmp_sensor) link; 65 int32_t index; 66 int32_t sysctlidx; 67 int32_t temp; 68 char desc[LM75BUF]; 69 char location[LM75BUF]; 70 char parent[LM75BUF]; 71 char pnpinfo[LM75BUF]; 72 }; 73 74 static TAILQ_HEAD(, lm75_snmp_sensor) sensors = 75 TAILQ_HEAD_INITIALIZER(sensors); 76 77 /* Ticks of the last sensors reading. */ 78 static uint64_t last_sensors_update; 79 80 static void free_sensors(void); 81 static int lm75_fini(void); 82 static int lm75_init(struct lmodule *mod, int argc, char *argv[]); 83 static void lm75_start(void); 84 static int update_sensors(void); 85 86 const struct snmp_module config = { 87 .comment = 88 "This module implements the BEGEMOT MIB for reading LM75 sensors data.", 89 .init = lm75_init, 90 .start = lm75_start, 91 .fini = lm75_fini, 92 .tree = lm75_ctree, 93 .tree_size = lm75_CTREE_SIZE, 94 }; 95 96 static int 97 lm75_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) 98 { 99 100 module = mod; 101 102 lm75_sensors = 0; 103 openlog("snmp_lm75", LOG_NDELAY | LOG_PID, LOG_DAEMON); 104 105 return(0); 106 } 107 108 static void 109 lm75_start(void) 110 { 111 112 lm75_index = or_register(&oid_lm75, 113 "The MIB module for reading lm75 sensors data.", module); 114 } 115 116 static int 117 lm75_fini(void) 118 { 119 120 or_unregister(lm75_index); 121 free_sensors(); 122 closelog(); 123 124 return (0); 125 } 126 127 static void 128 free_sensors(void) 129 { 130 struct lm75_snmp_sensor *sensor; 131 132 while ((sensor = TAILQ_FIRST(&sensors)) != NULL) { 133 TAILQ_REMOVE(&sensors, sensor, link); 134 free(sensor); 135 } 136 } 137 138 static int 139 sysctlname(int *oid, int nlen, char *name, size_t len) 140 { 141 int mib[12]; 142 143 if (nlen > (int)(sizeof(mib) / sizeof(int) - 2)) 144 return (-1); 145 146 mib[0] = 0; 147 mib[1] = 1; 148 memcpy(mib + 2, oid, nlen * sizeof(int)); 149 150 if (sysctl(mib, nlen + 2, name, &len, 0, 0) == -1) 151 return (-1); 152 153 return (0); 154 } 155 156 static int 157 sysctlgetnext(int *oid, int nlen, int *next, size_t *nextlen) 158 { 159 int mib[12]; 160 161 if (nlen > (int)(sizeof(mib) / sizeof(int) - 2)) 162 return (-1); 163 164 mib[0] = 0; 165 mib[1] = 2; 166 memcpy(mib + 2, oid, nlen * sizeof(int)); 167 168 if (sysctl(mib, nlen + 2, next, nextlen, 0, 0) == -1) 169 return (-1); 170 171 return (0); 172 } 173 174 static int 175 update_sensor_sysctl(void *obuf, size_t *obuflen, int idx, const char *name) 176 { 177 char buf[LM75BUF]; 178 int mib[5]; 179 size_t len; 180 181 /* Fill out the mib information. */ 182 snprintf(buf, sizeof(buf) - 1, "dev.lm75.%d.%s", idx, name); 183 len = sizeof(mib) / sizeof(int); 184 if (sysctlnametomib(buf, mib, &len) == -1) 185 return (-1); 186 187 if (len != 4) 188 return (-1); 189 190 /* Read the sysctl data. */ 191 if (sysctl(mib, len, obuf, obuflen, NULL, 0) == -1) 192 return (-1); 193 194 return (0); 195 } 196 197 static void 198 update_sensor(struct lm75_snmp_sensor *sensor, int idx) 199 { 200 size_t len; 201 202 len = sizeof(sensor->desc); 203 update_sensor_sysctl(sensor->desc, &len, idx, "%desc"); 204 205 len = sizeof(sensor->location); 206 update_sensor_sysctl(sensor->location, &len, idx, "%location"); 207 208 len = sizeof(sensor->pnpinfo); 209 update_sensor_sysctl(sensor->pnpinfo, &len, idx, "%pnpinfo"); 210 211 len = sizeof(sensor->parent); 212 update_sensor_sysctl(sensor->parent, &len, idx, "%parent"); 213 } 214 215 static int 216 add_sensor(char *buf) 217 { 218 int idx, temp; 219 size_t len; 220 struct lm75_snmp_sensor *sensor; 221 222 if (sscanf(buf, "dev.lm75.%d.temperature", &idx) != 1) 223 return (-1); 224 225 /* Read the sensor temperature. */ 226 len = sizeof(temp); 227 if (update_sensor_sysctl(&temp, &len, idx, "temperature") != 0) 228 return (-1); 229 230 /* Add the sensor data to the table. */ 231 sensor = calloc(1, sizeof(*sensor)); 232 if (sensor == NULL) { 233 syslog(LOG_ERR, "Unable to allocate %zu bytes for resource", 234 sizeof(*sensor)); 235 return (-1); 236 } 237 sensor->index = ++lm75_sensors; 238 sensor->sysctlidx = idx; 239 sensor->temp = (temp - TZ_ZEROC) / 10; 240 TAILQ_INSERT_TAIL(&sensors, sensor, link); 241 242 update_sensor(sensor, idx); 243 244 return (0); 245 } 246 247 static int 248 update_sensors(void) 249 { 250 char buf[LM75BUF]; 251 int i, root[5], *next, *oid; 252 size_t len, nextlen, rootlen; 253 static uint64_t now; 254 255 now = get_ticks(); 256 if (now - last_sensors_update < UPDATE_INTERVAL) 257 return (0); 258 259 last_sensors_update = now; 260 261 /* Reset the sensor data. */ 262 free_sensors(); 263 lm75_sensors = 0; 264 265 /* Start from the lm75 default root node. */ 266 rootlen = 2; 267 if (sysctlnametomib("dev.lm75", root, &rootlen) == -1) 268 return (0); 269 270 oid = (int *)malloc(sizeof(int) * rootlen); 271 if (oid == NULL) { 272 perror("malloc"); 273 return (-1); 274 } 275 memcpy(oid, root, rootlen * sizeof(int)); 276 len = rootlen; 277 278 /* Traverse the sysctl(3) interface and find the active sensors. */ 279 for (;;) { 280 281 /* Find the size of the next mib. */ 282 nextlen = 0; 283 if (sysctlgetnext(oid, len, NULL, &nextlen) == -1) { 284 free(oid); 285 return (0); 286 } 287 /* Alocate and read the next mib. */ 288 next = (int *)malloc(nextlen); 289 if (next == NULL) { 290 syslog(LOG_ERR, 291 "Unable to allocate %zu bytes for resource", 292 nextlen); 293 free(oid); 294 return (-1); 295 } 296 if (sysctlgetnext(oid, len, next, &nextlen) == -1) { 297 free(oid); 298 free(next); 299 return (0); 300 } 301 free(oid); 302 /* Check if we care about the next mib. */ 303 for (i = 0; i < (int)rootlen; i++) 304 if (next[i] != root[i]) { 305 free(next); 306 return (0); 307 } 308 oid = (int *)malloc(nextlen); 309 if (oid == NULL) { 310 syslog(LOG_ERR, 311 "Unable to allocate %zu bytes for resource", 312 nextlen); 313 free(next); 314 return (-1); 315 } 316 memcpy(oid, next, nextlen); 317 free(next); 318 len = nextlen / sizeof(int); 319 320 /* Find the mib name. */ 321 if (sysctlname(oid, len, buf, sizeof(buf)) != 0) 322 continue; 323 324 if (strstr(buf, "temperature")) 325 if (add_sensor(buf) != 0) { 326 free(oid); 327 return (-1); 328 } 329 } 330 331 return (0); 332 } 333 334 int 335 op_lm75Sensors(struct snmp_context *context __unused, struct snmp_value *value, 336 u_int sub, u_int iidx __unused, enum snmp_op op) 337 { 338 asn_subid_t which; 339 340 if (update_sensors() == -1) 341 return (SNMP_ERR_RES_UNAVAIL); 342 343 which = value->var.subs[sub - 1]; 344 345 switch (op) { 346 case SNMP_OP_GET: 347 switch (which) { 348 case LEAF_lm75Sensors: 349 value->v.integer = lm75_sensors; 350 break; 351 default: 352 return (SNMP_ERR_RES_UNAVAIL); 353 } 354 break; 355 case SNMP_OP_SET: 356 return (SNMP_ERR_NOT_WRITEABLE); 357 case SNMP_OP_GETNEXT: 358 case SNMP_OP_ROLLBACK: 359 case SNMP_OP_COMMIT: 360 return (SNMP_ERR_NOERROR); 361 default: 362 return (SNMP_ERR_RES_UNAVAIL); 363 } 364 365 return (SNMP_ERR_NOERROR); 366 } 367 368 int 369 op_lm75SensorTable(struct snmp_context *context __unused, 370 struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) 371 { 372 struct lm75_snmp_sensor *sensor; 373 asn_subid_t which; 374 int ret; 375 376 if (update_sensors() == -1) 377 return (SNMP_ERR_RES_UNAVAIL); 378 379 which = value->var.subs[sub - 1]; 380 381 switch (op) { 382 case SNMP_OP_GETNEXT: 383 sensor = NEXT_OBJECT_INT(&sensors, &value->var, sub); 384 if (sensor == NULL) 385 return (SNMP_ERR_NOSUCHNAME); 386 value->var.len = sub + 1; 387 value->var.subs[sub] = sensor->index; 388 break; 389 case SNMP_OP_GET: 390 if (value->var.len - sub != 1) 391 return (SNMP_ERR_NOSUCHNAME); 392 sensor = FIND_OBJECT_INT(&sensors, &value->var, sub); 393 if (sensor == NULL) 394 return (SNMP_ERR_NOSUCHNAME); 395 break; 396 case SNMP_OP_SET: 397 return (SNMP_ERR_NOT_WRITEABLE); 398 case SNMP_OP_ROLLBACK: 399 case SNMP_OP_COMMIT: 400 return (SNMP_ERR_NOERROR); 401 default: 402 return (SNMP_ERR_RES_UNAVAIL); 403 } 404 405 ret = SNMP_ERR_NOERROR; 406 407 switch (which) { 408 case LEAF_lm75SensorIndex: 409 value->v.integer = sensor->index; 410 break; 411 case LEAF_lm75SensorSysctlIndex: 412 value->v.integer = sensor->sysctlidx; 413 break; 414 case LEAF_lm75SensorDesc: 415 ret = string_get(value, sensor->desc, -1); 416 break; 417 case LEAF_lm75SensorLocation: 418 ret = string_get(value, sensor->location, -1); 419 break; 420 case LEAF_lm75SensorPnpInfo: 421 ret = string_get(value, sensor->pnpinfo, -1); 422 break; 423 case LEAF_lm75SensorParent: 424 ret = string_get(value, sensor->parent, -1); 425 break; 426 case LEAF_lm75SensorTemperature: 427 value->v.integer = sensor->temp; 428 break; 429 default: 430 ret = SNMP_ERR_RES_UNAVAIL; 431 break; 432 } 433 434 return (ret); 435 } 436