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) + 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) + 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(char *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 = 4; 184 if (sysctlnametomib(buf, mib, &len) == -1) 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, size_t nlen) 214 { 215 int idx, mib[5], 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 /* Fill out the mib information. */ 223 if (sysctlnametomib(buf, mib, &nlen) == -1) 224 return (-1); 225 226 /* Read the sensor temperature. */ 227 len = sizeof(temp); 228 if (sysctl(mib, nlen, &temp, &len, NULL, 0) == -1) 229 return (-1); 230 231 /* Add the sensor data to the table. */ 232 sensor = calloc(1, sizeof(*sensor)); 233 if (sensor == NULL) { 234 syslog(LOG_ERR, "Unable to allocate %zu bytes for resource", 235 sizeof(*sensor)); 236 return (-1); 237 } 238 sensor->index = ++lm75_sensors; 239 sensor->sysctlidx = idx; 240 sensor->temp = (temp - TZ_ZEROC) / 10; 241 TAILQ_INSERT_TAIL(&sensors, sensor, link); 242 243 update_sensor(sensor, idx); 244 245 return (0); 246 } 247 248 static int 249 update_sensors(void) 250 { 251 char buf[LM75BUF]; 252 int i, root[5], *next, *oid; 253 size_t len, nextlen, rootlen; 254 static uint64_t now; 255 256 now = get_ticks(); 257 if (now - last_sensors_update < UPDATE_INTERVAL) 258 return (0); 259 260 last_sensors_update = now; 261 262 /* Reset the sensor data. */ 263 free_sensors(); 264 lm75_sensors = 0; 265 266 /* Start from the lm75 default root node. */ 267 rootlen = 2; 268 if (sysctlnametomib("dev.lm75", root, &rootlen) == -1) 269 return (0); 270 271 oid = (int *)malloc(sizeof(int) * rootlen); 272 if (oid == NULL) { 273 perror("malloc"); 274 return (-1); 275 } 276 memcpy(oid, root, rootlen * sizeof(int)); 277 len = rootlen; 278 279 /* Traverse the sysctl(3) interface and find the active sensors. */ 280 for (;;) { 281 282 /* Find the size of the next mib. */ 283 nextlen = 0; 284 if (sysctlgetnext(oid, len, NULL, &nextlen) == -1) { 285 free(oid); 286 return (0); 287 } 288 /* Alocate and read the next mib. */ 289 next = (int *)malloc(nextlen); 290 if (next == NULL) { 291 syslog(LOG_ERR, 292 "Unable to allocate %zu bytes for resource", 293 nextlen); 294 free(oid); 295 return (-1); 296 } 297 if (sysctlgetnext(oid, len, next, &nextlen) == -1) { 298 free(oid); 299 free(next); 300 return (0); 301 } 302 free(oid); 303 /* Check if we care about the next mib. */ 304 for (i = 0; i < (int)rootlen; i++) 305 if (next[i] != root[i]) { 306 free(next); 307 return (0); 308 } 309 oid = (int *)malloc(nextlen); 310 if (oid == NULL) { 311 syslog(LOG_ERR, 312 "Unable to allocate %zu bytes for resource", 313 nextlen); 314 free(next); 315 return (-1); 316 } 317 memcpy(oid, next, nextlen); 318 free(next); 319 len = nextlen / sizeof(int); 320 321 /* Find the mib name. */ 322 if (sysctlname(oid, len, buf, sizeof(buf)) != 0) 323 continue; 324 325 if (strstr(buf, "temperature")) 326 if (add_sensor(buf, len) != 0) { 327 free(oid); 328 return (-1); 329 } 330 } 331 332 return (0); 333 } 334 335 int 336 op_lm75Sensors(struct snmp_context *context __unused, struct snmp_value *value, 337 u_int sub, u_int iidx __unused, enum snmp_op op) 338 { 339 asn_subid_t which; 340 341 if (update_sensors() == -1) 342 return (SNMP_ERR_RES_UNAVAIL); 343 344 which = value->var.subs[sub - 1]; 345 346 switch (op) { 347 case SNMP_OP_GET: 348 switch (which) { 349 case LEAF_lm75Sensors: 350 value->v.integer = lm75_sensors; 351 break; 352 default: 353 return (SNMP_ERR_RES_UNAVAIL); 354 } 355 break; 356 case SNMP_OP_SET: 357 return (SNMP_ERR_NOT_WRITEABLE); 358 case SNMP_OP_GETNEXT: 359 case SNMP_OP_ROLLBACK: 360 case SNMP_OP_COMMIT: 361 return (SNMP_ERR_NOERROR); 362 default: 363 return (SNMP_ERR_RES_UNAVAIL); 364 } 365 366 return (SNMP_ERR_NOERROR); 367 } 368 369 int 370 op_lm75SensorTable(struct snmp_context *context __unused, 371 struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) 372 { 373 struct lm75_snmp_sensor *sensor; 374 asn_subid_t which; 375 int ret; 376 377 if (update_sensors() == -1) 378 return (SNMP_ERR_RES_UNAVAIL); 379 380 which = value->var.subs[sub - 1]; 381 382 switch (op) { 383 case SNMP_OP_GETNEXT: 384 sensor = NEXT_OBJECT_INT(&sensors, &value->var, sub); 385 if (sensor == NULL) 386 return (SNMP_ERR_NOSUCHNAME); 387 value->var.len = sub + 1; 388 value->var.subs[sub] = sensor->index; 389 break; 390 case SNMP_OP_GET: 391 if (value->var.len - sub != 1) 392 return (SNMP_ERR_NOSUCHNAME); 393 sensor = FIND_OBJECT_INT(&sensors, &value->var, sub); 394 if (sensor == NULL) 395 return (SNMP_ERR_NOSUCHNAME); 396 break; 397 case SNMP_OP_SET: 398 return (SNMP_ERR_NOT_WRITEABLE); 399 case SNMP_OP_ROLLBACK: 400 case SNMP_OP_COMMIT: 401 return (SNMP_ERR_NOERROR); 402 default: 403 return (SNMP_ERR_RES_UNAVAIL); 404 } 405 406 ret = SNMP_ERR_NOERROR; 407 408 switch (which) { 409 case LEAF_lm75SensorIndex: 410 value->v.integer = sensor->index; 411 break; 412 case LEAF_lm75SensorSysctlIndex: 413 value->v.integer = sensor->sysctlidx; 414 break; 415 case LEAF_lm75SensorDesc: 416 ret = string_get(value, sensor->desc, -1); 417 break; 418 case LEAF_lm75SensorLocation: 419 ret = string_get(value, sensor->location, -1); 420 break; 421 case LEAF_lm75SensorPnpInfo: 422 ret = string_get(value, sensor->pnpinfo, -1); 423 break; 424 case LEAF_lm75SensorParent: 425 ret = string_get(value, sensor->parent, -1); 426 break; 427 case LEAF_lm75SensorTemperature: 428 value->v.integer = sensor->temp; 429 break; 430 default: 431 ret = SNMP_ERR_RES_UNAVAIL; 432 break; 433 } 434 435 return (ret); 436 } 437