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