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
lm75_init(struct lmodule * mod,int argc __unused,char * argv[]__unused)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
lm75_start(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
lm75_fini(void)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
free_sensors(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
sysctlname(int * oid,int nlen,char * name,size_t len)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
sysctlgetnext(int * oid,int nlen,int * next,size_t * nextlen)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
update_sensor_sysctl(void * obuf,size_t * obuflen,int idx,const char * name)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
update_sensor(struct lm75_snmp_sensor * sensor,int idx)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
add_sensor(char * buf)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
update_sensors(void)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
op_lm75Sensors(struct snmp_context * context __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)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
op_lm75SensorTable(struct snmp_context * context __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)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