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