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 104 return(0); 105 } 106 107 static void 108 lm75_start(void) 109 { 110 111 lm75_index = or_register(&oid_lm75, 112 "The MIB module for reading lm75 sensors data.", module); 113 } 114 115 static int 116 lm75_fini(void) 117 { 118 119 or_unregister(lm75_index); 120 free_sensors(); 121 closelog(); 122 123 return (0); 124 } 125 126 static void 127 free_sensors(void) 128 { 129 struct lm75_snmp_sensor *sensor; 130 131 while ((sensor = TAILQ_FIRST(&sensors)) != NULL) { 132 TAILQ_REMOVE(&sensors, sensor, link); 133 free(sensor); 134 } 135 } 136 137 static int 138 sysctlname(int *oid, int nlen, char *name, size_t len) 139 { 140 int mib[12]; 141 142 if (nlen > (int)(sizeof(mib) / sizeof(int) - 2)) 143 return (-1); 144 145 mib[0] = 0; 146 mib[1] = 1; 147 memcpy(mib + 2, oid, nlen * sizeof(int)); 148 149 if (sysctl(mib, nlen + 2, name, &len, 0, 0) == -1) 150 return (-1); 151 152 return (0); 153 } 154 155 static int 156 sysctlgetnext(int *oid, int nlen, int *next, size_t *nextlen) 157 { 158 int mib[12]; 159 160 if (nlen > (int)(sizeof(mib) / sizeof(int) - 2)) 161 return (-1); 162 163 mib[0] = 0; 164 mib[1] = 2; 165 memcpy(mib + 2, oid, nlen * sizeof(int)); 166 167 if (sysctl(mib, nlen + 2, next, nextlen, 0, 0) == -1) 168 return (-1); 169 170 return (0); 171 } 172 173 static int 174 update_sensor_sysctl(void *obuf, size_t *obuflen, int idx, const char *name) 175 { 176 char buf[LM75BUF]; 177 int mib[5]; 178 size_t len; 179 180 /* Fill out the mib information. */ 181 snprintf(buf, sizeof(buf) - 1, "dev.lm75.%d.%s", idx, name); 182 len = sizeof(mib) / sizeof(int); 183 if (sysctlnametomib(buf, mib, &len) == -1) 184 return (-1); 185 186 if (len != 4) 187 return (-1); 188 189 /* Read the sysctl data. */ 190 if (sysctl(mib, len, obuf, obuflen, NULL, 0) == -1) 191 return (-1); 192 193 return (0); 194 } 195 196 static void 197 update_sensor(struct lm75_snmp_sensor *sensor, int idx) 198 { 199 size_t len; 200 201 len = sizeof(sensor->desc); 202 update_sensor_sysctl(sensor->desc, &len, idx, "%desc"); 203 204 len = sizeof(sensor->location); 205 update_sensor_sysctl(sensor->location, &len, idx, "%location"); 206 207 len = sizeof(sensor->pnpinfo); 208 update_sensor_sysctl(sensor->pnpinfo, &len, idx, "%pnpinfo"); 209 210 len = sizeof(sensor->parent); 211 update_sensor_sysctl(sensor->parent, &len, idx, "%parent"); 212 } 213 214 static int 215 add_sensor(char *buf) 216 { 217 int idx, temp; 218 size_t len; 219 struct lm75_snmp_sensor *sensor; 220 221 if (sscanf(buf, "dev.lm75.%d.temperature", &idx) != 1) 222 return (-1); 223 224 /* Read the sensor temperature. */ 225 len = sizeof(temp); 226 if (update_sensor_sysctl(&temp, &len, idx, "temperature") != 0) 227 return (-1); 228 229 /* Add the sensor data to the table. */ 230 sensor = calloc(1, sizeof(*sensor)); 231 if (sensor == NULL) { 232 syslog(LOG_ERR, "Unable to allocate %zu bytes for resource", 233 sizeof(*sensor)); 234 return (-1); 235 } 236 sensor->index = ++lm75_sensors; 237 sensor->sysctlidx = idx; 238 sensor->temp = (temp - TZ_ZEROC) / 10; 239 TAILQ_INSERT_TAIL(&sensors, sensor, link); 240 241 update_sensor(sensor, idx); 242 243 return (0); 244 } 245 246 static int 247 update_sensors(void) 248 { 249 char buf[LM75BUF]; 250 int i, root[5], *next, *oid; 251 size_t len, nextlen, rootlen; 252 static uint64_t now; 253 254 now = get_ticks(); 255 if (now - last_sensors_update < UPDATE_INTERVAL) 256 return (0); 257 258 last_sensors_update = now; 259 260 /* Reset the sensor data. */ 261 free_sensors(); 262 lm75_sensors = 0; 263 264 /* Start from the lm75 default root node. */ 265 rootlen = 2; 266 if (sysctlnametomib("dev.lm75", root, &rootlen) == -1) 267 return (0); 268 269 oid = (int *)malloc(sizeof(int) * rootlen); 270 if (oid == NULL) { 271 perror("malloc"); 272 return (-1); 273 } 274 memcpy(oid, root, rootlen * sizeof(int)); 275 len = rootlen; 276 277 /* Traverse the sysctl(3) interface and find the active sensors. */ 278 for (;;) { 279 280 /* Find the size of the next mib. */ 281 nextlen = 0; 282 if (sysctlgetnext(oid, len, NULL, &nextlen) == -1) { 283 free(oid); 284 return (0); 285 } 286 /* Allocate and read the next mib. */ 287 next = (int *)malloc(nextlen); 288 if (next == NULL) { 289 syslog(LOG_ERR, 290 "Unable to allocate %zu bytes for resource", 291 nextlen); 292 free(oid); 293 return (-1); 294 } 295 if (sysctlgetnext(oid, len, next, &nextlen) == -1) { 296 free(oid); 297 free(next); 298 return (0); 299 } 300 free(oid); 301 /* Check if we care about the next mib. */ 302 for (i = 0; i < (int)rootlen; i++) 303 if (next[i] != root[i]) { 304 free(next); 305 return (0); 306 } 307 oid = (int *)malloc(nextlen); 308 if (oid == NULL) { 309 syslog(LOG_ERR, 310 "Unable to allocate %zu bytes for resource", 311 nextlen); 312 free(next); 313 return (-1); 314 } 315 memcpy(oid, next, nextlen); 316 free(next); 317 len = nextlen / sizeof(int); 318 319 /* Find the mib name. */ 320 if (sysctlname(oid, len, buf, sizeof(buf)) != 0) 321 continue; 322 323 if (strstr(buf, "temperature")) 324 if (add_sensor(buf) != 0) { 325 free(oid); 326 return (-1); 327 } 328 } 329 330 return (0); 331 } 332 333 int 334 op_lm75Sensors(struct snmp_context *context __unused, struct snmp_value *value, 335 u_int sub, u_int iidx __unused, enum snmp_op op) 336 { 337 asn_subid_t which; 338 339 if (update_sensors() == -1) 340 return (SNMP_ERR_RES_UNAVAIL); 341 342 which = value->var.subs[sub - 1]; 343 344 switch (op) { 345 case SNMP_OP_GET: 346 switch (which) { 347 case LEAF_lm75Sensors: 348 value->v.integer = lm75_sensors; 349 break; 350 default: 351 return (SNMP_ERR_RES_UNAVAIL); 352 } 353 break; 354 case SNMP_OP_SET: 355 return (SNMP_ERR_NOT_WRITEABLE); 356 case SNMP_OP_GETNEXT: 357 case SNMP_OP_ROLLBACK: 358 case SNMP_OP_COMMIT: 359 return (SNMP_ERR_NOERROR); 360 default: 361 return (SNMP_ERR_RES_UNAVAIL); 362 } 363 364 return (SNMP_ERR_NOERROR); 365 } 366 367 int 368 op_lm75SensorTable(struct snmp_context *context __unused, 369 struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) 370 { 371 struct lm75_snmp_sensor *sensor; 372 asn_subid_t which; 373 int ret; 374 375 if (update_sensors() == -1) 376 return (SNMP_ERR_RES_UNAVAIL); 377 378 which = value->var.subs[sub - 1]; 379 380 switch (op) { 381 case SNMP_OP_GETNEXT: 382 sensor = NEXT_OBJECT_INT(&sensors, &value->var, sub); 383 if (sensor == NULL) 384 return (SNMP_ERR_NOSUCHNAME); 385 value->var.len = sub + 1; 386 value->var.subs[sub] = sensor->index; 387 break; 388 case SNMP_OP_GET: 389 if (value->var.len - sub != 1) 390 return (SNMP_ERR_NOSUCHNAME); 391 sensor = FIND_OBJECT_INT(&sensors, &value->var, sub); 392 if (sensor == NULL) 393 return (SNMP_ERR_NOSUCHNAME); 394 break; 395 case SNMP_OP_SET: 396 return (SNMP_ERR_NOT_WRITEABLE); 397 case SNMP_OP_ROLLBACK: 398 case SNMP_OP_COMMIT: 399 return (SNMP_ERR_NOERROR); 400 default: 401 return (SNMP_ERR_RES_UNAVAIL); 402 } 403 404 ret = SNMP_ERR_NOERROR; 405 406 switch (which) { 407 case LEAF_lm75SensorIndex: 408 value->v.integer = sensor->index; 409 break; 410 case LEAF_lm75SensorSysctlIndex: 411 value->v.integer = sensor->sysctlidx; 412 break; 413 case LEAF_lm75SensorDesc: 414 ret = string_get(value, sensor->desc, -1); 415 break; 416 case LEAF_lm75SensorLocation: 417 ret = string_get(value, sensor->location, -1); 418 break; 419 case LEAF_lm75SensorPnpInfo: 420 ret = string_get(value, sensor->pnpinfo, -1); 421 break; 422 case LEAF_lm75SensorParent: 423 ret = string_get(value, sensor->parent, -1); 424 break; 425 case LEAF_lm75SensorTemperature: 426 value->v.integer = sensor->temp; 427 break; 428 default: 429 ret = SNMP_ERR_RES_UNAVAIL; 430 break; 431 } 432 433 return (ret); 434 } 435