1/**
2 * @file
3 * lwip Private MIB
4 *
5 * @todo create MIB file for this example
6 * @note the lwip enterprise tree root (26381) is owned by the lwIP project.
7 * It is NOT allowed to allocate new objects under this ID (26381) without our,
8 * the lwip developers, permission!
9 *
10 * Please apply for your own ID with IANA: http://www.iana.org/numbers.html
11 *
12 * lwip        OBJECT IDENTIFIER ::= { enterprises 26381 }
13 * example     OBJECT IDENTIFIER ::= { lwip 1 }
14 */
15
16/*
17 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without modification,
21 * are permitted provided that the following conditions are met:
22 *
23 * 1. Redistributions of source code must retain the above copyright notice,
24 *    this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright notice,
26 *    this list of conditions and the following disclaimer in the documentation
27 *    and/or other materials provided with the distribution.
28 * 3. The name of the author may not be used to endorse or promote products
29 *    derived from this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
32 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
34 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
39 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
40 * OF SUCH DAMAGE.
41 *
42 * Author: Christiaan Simons <christiaan.simons@axon.tv>
43 */
44
45#include "private_mib.h"
46
47#if LWIP_SNMP
48
49/** Directory where the sensor files are */
50#define SENSORS_DIR           "w:\\sensors"
51/** Set to 1 to read sensor values from files (in directory defined by SENSORS_DIR) */
52#define SENSORS_USE_FILES     0
53/** Set to 1 to search sensor files at startup (in directory defined by SENSORS_DIR) */
54#define SENSORS_SEARCH_FILES  0
55
56#if SENSORS_SEARCH_FILES
57#include <sys/stat.h>
58#include <sys/types.h>
59#include <ctype.h>
60#include <unistd.h>
61#include <fcntl.h>
62#include <dirent.h>
63#endif /* SENSORS_SEARCH_FILES */
64
65#include <string.h>
66#include <stdio.h>
67
68#include "lwip/apps/snmp_table.h"
69#include "lwip/apps/snmp_scalar.h"
70
71#if !SENSORS_USE_FILES || !SENSORS_SEARCH_FILES
72/** When not using & searching files, defines the number of sensors */
73#define SENSOR_COUNT 4
74#endif /* !SENSORS_USE_FILES || !SENSORS_SEARCH_FILES */
75
76/*
77  This example presents a table for a few (at most 10) sensors.
78  Sensor detection takes place at initialization (once only).
79  Sensors may and can not be added or removed after agent
80  has started. Note this is only a limitation of this crude example,
81  the agent does support dynamic object insertions and removals.
82
83  You'll need to manually create a directory called "sensors" and
84  a few single line text files with an integer temperature value.
85  The files must be called [0..9].txt.
86
87  ./sensors/0.txt [content: 20]
88  ./sensors/3.txt [content: 75]
89
90  The sensor values may be changed in runtime by editing the
91  text files in the "sensors" directory.
92*/
93
94#define SENSOR_MAX      10
95#define SENSOR_NAME_LEN 20
96
97struct sensor_inf
98{
99  u8_t num;
100
101  char file[SENSOR_NAME_LEN + 1];
102
103#if !SENSORS_USE_FILES
104  /** When not using files, contains the value of the sensor */
105  s32_t value;
106#endif /* !SENSORS_USE_FILES */
107};
108
109static struct sensor_inf sensors[SENSOR_MAX];
110
111static s16_t      sensor_count_get_value(struct snmp_node_instance* instance, void* value);
112static snmp_err_t sensor_table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance);
113static snmp_err_t sensor_table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance);
114static s16_t      sensor_table_get_value(struct snmp_node_instance* instance, void* value);
115static snmp_err_t sensor_table_set_value(struct snmp_node_instance* instance, u16_t len, void *value);
116
117/* sensorentry .1.3.6.1.4.1.26381.1.1.1 (.level0.level1)
118   where level 0 is the table column (temperature/file name)
119   and level 1 the table row (sensor index) */
120static const struct snmp_table_col_def sensor_table_columns[] = {
121  { 1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE },
122  { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY  }
123};
124
125/* sensortable .1.3.6.1.4.1.26381.1.1 */
126static const struct snmp_table_node sensor_table = SNMP_TABLE_CREATE(
127  1, sensor_table_columns,
128  sensor_table_get_cell_instance, sensor_table_get_next_cell_instance,
129  sensor_table_get_value, snmp_set_test_ok, sensor_table_set_value);
130
131/* sensorcount .1.3.6.1.4.1.26381.1.2 */
132static const struct snmp_scalar_node sensor_count = SNMP_SCALAR_CREATE_NODE_READONLY(
133  2, SNMP_ASN1_TYPE_INTEGER, sensor_count_get_value);
134
135/* example .1.3.6.1.4.1.26381.1 */
136static const struct snmp_node* const example_nodes[] = {
137  &sensor_table.node.node,
138  &sensor_count.node.node
139};
140static const struct snmp_tree_node example_node = SNMP_CREATE_TREE_NODE(1, example_nodes);
141
142static const u32_t prvmib_base_oid[] = { 1,3,6,1,4,1,26381,1 };
143const struct snmp_mib mib_private = SNMP_MIB_CREATE(prvmib_base_oid, &example_node.node);
144
145#if 0
146/* for reference: we could also have expressed it like this: */
147
148/* lwip .1.3.6.1.4.1.26381 */
149static const struct snmp_node* const lwip_nodes[] = {
150  &example_node.node
151};
152static const struct snmp_tree_node lwip_node = SNMP_CREATE_TREE_NODE(26381, lwip_nodes);
153
154/* enterprises .1.3.6.1.4.1 */
155static const struct snmp_node* const enterprises_nodes[] = {
156  &lwip_node.node
157};
158static const struct snmp_tree_node enterprises_node = SNMP_CREATE_TREE_NODE(1, enterprises_nodes);
159
160/* private .1.3.6.1.4 */
161static const struct snmp_node* const private_nodes[] = {
162  &enterprises_node.node
163};
164static const struct snmp_tree_node private_root = SNMP_CREATE_TREE_NODE(0, private_nodes);
165
166static const u32_t prvmib_base_oid[] = { 1,3,6,1,4 };
167const struct snmp_mib mib_private = SNMP_MIB_CREATE(prvmib_base_oid, &private_root.node);
168#endif
169
170/**
171 * Initialises this private MIB before use.
172 * @see main.c
173 */
174void
175lwip_privmib_init(void)
176{
177#if SENSORS_USE_FILES && SENSORS_SEARCH_FILES
178  char *buf, *ebuf, *cp;
179  size_t bufsize;
180  int nbytes;
181  struct stat sb;
182  struct dirent *dp;
183  int fd;
184#else /* SENSORS_USE_FILES && SENSORS_SEARCH_FILES */
185  u8_t i;
186#endif /* SENSORS_USE_FILES && SENSORS_SEARCH_FILES */
187
188  memset(sensors, 0, sizeof(sensors));
189
190  printf("SNMP private MIB start, detecting sensors.\n");
191
192#if SENSORS_USE_FILES && SENSORS_SEARCH_FILES
193  /* look for sensors in sensors directory */
194  fd = open(SENSORS_DIR, O_RDONLY);
195  if (fd > -1)
196  {
197    fstat(fd, &sb);
198    bufsize = sb.st_size;
199    if (bufsize < (size_t)sb.st_blksize)
200    {
201      bufsize = sb.st_blksize;
202    }
203    buf = (char*)malloc(bufsize);
204    if (buf != NULL)
205    {
206      do
207      {
208        long base;
209
210        nbytes = getdirentries(fd, buf, bufsize, &base);
211        if (nbytes > 0)
212        {
213          ebuf = buf + nbytes;
214          cp = buf;
215          while (cp < ebuf)
216          {
217            dp = (struct dirent *)cp;
218            if (isdigit(dp->d_name[0]))
219            {
220              unsigned char idx = dp->d_name[0] - '0';
221
222              sensors[idx].num = idx+1;
223              strncpy(&sensors[idx].file[0], dp->d_name, SENSOR_NAME_LEN);
224              printf("%s\n", sensors[idx].file);
225            }
226            cp += dp->d_reclen;
227          }
228        }
229      }
230      while (nbytes > 0);
231
232      free(buf);
233    }
234    close(fd);
235  }
236#else /* SENSORS_USE_FILES && SENSORS_SEARCH_FILES */
237  for (i = 0; i < SENSOR_COUNT; i++) {
238    sensors[i].num = i+1;
239    snprintf(sensors[i].file, sizeof(sensors[i].file), "%d.txt", i);
240
241#if !SENSORS_USE_FILES
242    /* initialize sensor value to != zero */
243    sensors[i].value = 11 * (i+1);
244#endif /* !SENSORS_USE_FILES */
245  }
246#endif /* SENSORS_USE_FILE && SENSORS_SEARCH_FILES */
247}
248
249/* sensorcount .1.3.6.1.4.1.26381.1.2 */
250static s16_t
251sensor_count_get_value(struct snmp_node_instance* instance, void* value)
252{
253  size_t count = 0;
254  u32_t *uint_ptr = (u32_t*)value;
255
256  LWIP_UNUSED_ARG(instance);
257
258  for(count=0; count<LWIP_ARRAYSIZE(sensors); count++) {
259    if(sensors[count].num == 0) {
260      *uint_ptr = (u32_t)count;
261      return sizeof(*uint_ptr);
262    }
263  }
264
265  return 0;
266}
267
268/* sensortable .1.3.6.1.4.1.26381.1.1 */
269/* list of allowed value ranges for incoming OID */
270static const struct snmp_oid_range sensor_table_oid_ranges[] = {
271  { 1, SENSOR_MAX+1 }
272};
273
274static snmp_err_t
275sensor_table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance)
276{
277  u32_t sensor_num;
278  size_t i;
279
280  LWIP_UNUSED_ARG(column);
281
282  /* check if incoming OID length and if values are in plausible range */
283  if(!snmp_oid_in_range(row_oid, row_oid_len, sensor_table_oid_ranges, LWIP_ARRAYSIZE(sensor_table_oid_ranges))) {
284    return SNMP_ERR_NOSUCHINSTANCE;
285  }
286
287  /* get sensor index from incoming OID */
288  sensor_num = row_oid[0];
289
290  /* find sensor with index */
291  for(i=0; i<LWIP_ARRAYSIZE(sensors); i++) {
292    if(sensors[i].num != 0) {
293      if(sensors[i].num == sensor_num) {
294        /* store sensor index for subsequent operations (get/test/set) */
295        cell_instance->reference.u32 = (u32_t)i;
296        return SNMP_ERR_NOERROR;
297      }
298    }
299  }
300
301  /* not found */
302  return SNMP_ERR_NOSUCHINSTANCE;
303}
304
305static snmp_err_t
306sensor_table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
307{
308  size_t i;
309  struct snmp_next_oid_state state;
310  u32_t result_temp[LWIP_ARRAYSIZE(sensor_table_oid_ranges)];
311
312  LWIP_UNUSED_ARG(column);
313
314  /* init struct to search next oid */
315  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(sensor_table_oid_ranges));
316
317  /* iterate over all possible OIDs to find the next one */
318  for(i=0; i<LWIP_ARRAYSIZE(sensors); i++) {
319    if(sensors[i].num != 0) {
320      u32_t test_oid[LWIP_ARRAYSIZE(sensor_table_oid_ranges)];
321
322      test_oid[0] = sensors[i].num;
323
324      /* check generated OID: is it a candidate for the next one? */
325      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(sensor_table_oid_ranges), (void*)i);
326    }
327  }
328
329  /* did we find a next one? */
330  if(state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
331    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
332    /* store sensor index for subsequent operations (get/test/set) */
333    cell_instance->reference.u32 = LWIP_CONST_CAST(u32_t, state.reference);
334    return SNMP_ERR_NOERROR;
335  }
336
337  /* not found */
338  return SNMP_ERR_NOSUCHINSTANCE;
339}
340
341static s16_t
342sensor_table_get_value(struct snmp_node_instance* instance, void* value)
343{
344  u32_t i = instance->reference.u32;
345  s32_t *temperature = (s32_t *)value;
346
347  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
348  {
349  case 1: /* sensor value */
350#if SENSORS_USE_FILES
351    FILE* sensf;
352    char senspath[sizeof(SENSORS_DIR)+1+SENSOR_NAME_LEN+1] = SENSORS_DIR"/";
353
354    strncpy(&senspath[sizeof(SENSORS_DIR)],
355            sensors[i].file,
356            SENSOR_NAME_LEN);
357    sensf = fopen(senspath,"r");
358    if (sensf != NULL)
359    {
360      fscanf(sensf,"%"S32_F,temperature);
361      fclose(sensf);
362    }
363#else /* SENSORS_USE_FILES */
364    *temperature = sensors[i].value;
365#endif /* SENSORS_USE_FILES */
366    return sizeof(s32_t);
367  case 2: /* file name */
368    MEMCPY(value, sensors[i].file, strlen(sensors[i].file));
369    return (u16_t)strlen(sensors[i].file);
370  default:
371    return 0;
372  }
373}
374
375static snmp_err_t
376sensor_table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
377{
378  u32_t i = instance->reference.u32;
379  s32_t *temperature = (s32_t *)value;
380#if SENSORS_USE_FILES
381  FILE* sensf;
382  char senspath[sizeof(SENSORS_DIR)+1+SENSOR_NAME_LEN+1] = SENSORS_DIR"/";
383
384  strncpy(&senspath[sizeof(SENSORS_DIR)],
385          sensors[i].file,
386          SENSOR_NAME_LEN);
387  sensf = fopen(senspath, "w");
388  if (sensf != NULL)
389  {
390    fprintf(sensf, "%"S32_F, *temperature);
391    fclose(sensf);
392  }
393#else /* SENSORS_USE_FILES */
394  sensors[i].value = *temperature;
395#endif /* SENSORS_USE_FILES */
396
397  LWIP_UNUSED_ARG(len);
398
399  return SNMP_ERR_NOERROR;
400}
401
402#endif /* LWIP_SNMP */
403