Revert "Revert "and added files""
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table_data.c
1 #ifdef BRCM_SNMP_SUPPORT
2 #include <net-snmp/net-snmp-config.h>
3
4 #if HAVE_STRING_H
5 #include <string.h>
6 #else
7 #include <strings.h>
8 #endif
9
10 #include <net-snmp/net-snmp-includes.h>
11 #include <net-snmp/agent/net-snmp-agent-includes.h>
12
13 #include <net-snmp/agent/table.h>
14 #include <net-snmp/agent/table_data.h>
15 #include <net-snmp/agent/read_only.h>
16
17 #if HAVE_DMALLOC_H
18 #include <dmalloc.h>
19 #endif
20
21 /** @defgroup table_data table_data: Helps you implement a table with datamatted storage.
22  *  @ingroup table
23  *
24  *  This helper helps you implement a table where all the indexes are
25  *  expected to be stored within the agent itself and not in some
26  *  external storage location.  It can be used to store a list of
27  *  rows, where a row consists of the indexes to the table and a
28  *  generic data pointer.  You can then implement a subhandler which
29  *  is passed the exact row definition and data it must return data
30  *  for or accept data for.  Complex GETNEXT handling is greatly
31  *  simplified in this case.
32  *
33  *  @{
34  */
35
36 /**
37  * generates the index portion of an table oid from a varlist.
38  */
39 void
40 netsnmp_table_data_generate_index_oid(netsnmp_table_row *row)
41 {
42     build_oid(&row->index_oid, &row->index_oid_len, NULL, 0, row->indexes);
43 }
44
45 /**
46  * Adds a row of data to a given table (stored in proper lexographical order).
47  *
48  * returns SNMPERR_SUCCESS on successful addition.
49  *      or SNMPERR_GENERR  on failure (E.G., indexes already existed)
50  */
51 int
52 netsnmp_table_data_add_row(netsnmp_table_data *table,
53                            netsnmp_table_row *row)
54 {
55     netsnmp_table_row *nextrow, *prevrow;
56
57     if (!row || !table)
58         return SNMPERR_GENERR;
59
60     if (row->indexes)
61         netsnmp_table_data_generate_index_oid(row);
62
63     /*
64      * we don't store the index info as it
65      * takes up memory. 
66      */
67     if (!table->store_indexes) {
68         snmp_free_varbind(row->indexes);
69         row->indexes = NULL;
70     }
71
72     if (!row->index_oid) {
73         snmp_log(LOG_ERR,
74                  "illegal data attempted to be added to table %s\n",
75                  table->name);
76         return SNMPERR_GENERR;
77     }
78
79     /*
80      * insert it into the table in the proper oid-lexographical order 
81      */
82     for (nextrow = table->first_row, prevrow = NULL;
83          nextrow != NULL; prevrow = nextrow, nextrow = nextrow->next) {
84         if (nextrow->index_oid &&
85             snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
86                              row->index_oid, row->index_oid_len) > 0)
87             break;
88         if (nextrow->index_oid &&
89             snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
90                              row->index_oid, row->index_oid_len) == 0) {
91             /*
92              * exact match.  Duplicate entries illegal 
93              */
94             snmp_log(LOG_WARNING,
95                      "duplicate table data attempted to be entered\n");
96             return SNMPERR_GENERR;
97         }
98     }
99
100
101     /*
102      * ok, we have the location of where it should go 
103      */
104     /*
105      * (after prevrow, and before nextrow) 
106      */
107     row->next = nextrow;
108     row->prev = prevrow;
109
110     if (row->next)
111         row->next->prev = row;
112
113     if (row->prev)
114         row->prev->next = row;
115
116     if (NULL == row->prev)      /* it's the (new) first row */
117         table->first_row = row;
118
119     DEBUGMSGTL(("table_data_add_data", "added something...\n"));
120
121     return SNMPERR_SUCCESS;
122 }
123
124 /**
125  * removes a row of data to a given table and returns it (no free's called)
126  *
127  * returns the row pointer itself on successful removing.
128  *      or NULL on failure (bad arguments)
129  */
130 netsnmp_table_row *
131 netsnmp_table_data_remove_row(netsnmp_table_data *table,
132                               netsnmp_table_row *row)
133 {
134     if (!row || !table)
135         return NULL;
136
137     if (row->prev)
138         row->prev->next = row->next;
139     else
140         table->first_row = row->next;
141
142     if (row->next)
143         row->next->prev = row->prev;
144
145     return row;
146 }
147
148 /** deletes a row's memory.
149  *  returns the void data that it doesn't know how to delete. */
150 void           *
151 netsnmp_table_data_delete_row(netsnmp_table_row *row)
152 {
153     void           *data;
154
155     if (!row)
156         return NULL;
157
158     /*
159      * free the memory we can 
160      */
161     if (row->indexes)
162         snmp_free_varbind(row->indexes);
163     SNMP_FREE(row->index_oid);
164     data = row->data;
165     free(row);
166
167     /*
168      * return the void * pointer 
169      */
170     return data;
171 }
172
173 /**
174  * removes and frees a row of data to a given table and returns the void *
175  *
176  * returns the void * data on successful deletion.
177  *      or NULL on failure (bad arguments)
178  */
179 void           *
180 netsnmp_table_data_remove_and_delete_row(netsnmp_table_data *table,
181                                          netsnmp_table_row *row)
182 {
183     if (!row || !table)
184         return NULL;
185
186     /*
187      * remove it from the list 
188      */
189     netsnmp_table_data_remove_row(table, row);
190     return netsnmp_table_data_delete_row(row);
191 }
192
193 /** swaps out origrow with newrow.  This does *not* delete/free anything! */
194 NETSNMP_INLINE void
195 netsnmp_table_data_replace_row(netsnmp_table_data *table,
196                                netsnmp_table_row *origrow,
197                                netsnmp_table_row *newrow)
198 {
199     netsnmp_table_data_remove_row(table, origrow);
200     netsnmp_table_data_add_row(table, newrow);
201 }
202
203 /** finds the data in "datalist" stored at "indexes" */
204 netsnmp_table_row *
205 netsnmp_table_data_get(netsnmp_table_data *table,
206                        netsnmp_variable_list * indexes)
207 {
208     oid             searchfor[MAX_OID_LEN];
209     size_t          searchfor_len = MAX_OID_LEN;
210
211     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
212                       indexes);
213     return netsnmp_table_data_get_from_oid(table, searchfor,
214                                            searchfor_len);
215 }
216
217 /** finds the data in "datalist" stored at the searchfor oid */
218 netsnmp_table_row *
219 netsnmp_table_data_get_from_oid(netsnmp_table_data *table,
220                                 oid * searchfor, size_t searchfor_len)
221 {
222     netsnmp_table_row *row;
223     if (!table)
224         return NULL;
225
226     for (row = table->first_row; row != NULL; row = row->next) {
227         if (row->index_oid &&
228             snmp_oid_compare(searchfor, searchfor_len,
229                              row->index_oid, row->index_oid_len) == 0)
230             return row;
231     }
232     return NULL;
233 }
234
235 /** Creates a table_data handler and returns it */
236 netsnmp_mib_handler *
237 netsnmp_get_table_data_handler(netsnmp_table_data *table)
238 {
239     netsnmp_mib_handler *ret = NULL;
240
241     if (!table) {
242         snmp_log(LOG_INFO,
243                  "netsnmp_get_table_data_handler(NULL) called\n");
244         return NULL;
245     }
246
247     ret =
248         netsnmp_create_handler(TABLE_DATA_NAME,
249                                netsnmp_table_data_helper_handler);
250     if (ret) {
251         ret->myvoid = (void *) table;
252     }
253     return ret;
254 }
255
256 /** registers a handler as a data table.
257  *  If table_info != NULL, it registers it as a normal table too. */
258 int
259 netsnmp_register_table_data(netsnmp_handler_registration *reginfo,
260                             netsnmp_table_data *table,
261                             netsnmp_table_registration_info *table_info)
262 {
263     netsnmp_inject_handler(reginfo, netsnmp_get_table_data_handler(table));
264     return netsnmp_register_table(reginfo, table_info);
265 }
266
267 /** registers a handler as a read-only data table
268  *  If table_info != NULL, it registers it as a normal table too. */
269 int
270 netsnmp_register_read_only_table_data(netsnmp_handler_registration
271                                       *reginfo, netsnmp_table_data *table,
272                                       netsnmp_table_registration_info
273                                       *table_info)
274 {
275     netsnmp_inject_handler(reginfo, netsnmp_get_read_only_handler());
276     return netsnmp_register_table_data(reginfo, table, table_info);
277 }
278
279
280 /**
281  * The helper handler that takes care of passing a specific row of
282  * data down to the lower handler(s).  It sets request->processed if
283  * the request should not be handled.
284  */
285 int
286 netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler,
287                                   netsnmp_handler_registration *reginfo,
288                                   netsnmp_agent_request_info *reqinfo,
289                                   netsnmp_request_info *requests)
290 {
291
292     netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid;
293     netsnmp_request_info *request;
294     int             valid_request = 0;
295     netsnmp_table_row *row;
296     netsnmp_table_request_info *table_info;
297     netsnmp_table_registration_info *table_reg_info =
298         netsnmp_find_table_registration_info(reginfo);
299     int             result, regresult;
300     int             oldmode;
301
302     for (request = requests; request; request = request->next) {
303         if (request->processed)
304             continue;
305
306         table_info = netsnmp_extract_table_info(request);
307         if (!table_info)
308             continue;           /* ack */
309
310         /*
311          * find the row in question 
312          */
313         switch (reqinfo->mode) {
314         case MODE_GETNEXT:
315         case MODE_GETBULK:     /* XXXWWW */
316             if (request->requestvb->type != ASN_NULL)
317                 continue;
318             /*
319              * loop through data till we find the next row 
320              */
321             result = snmp_oid_compare(request->requestvb->name,
322                                       request->requestvb->name_length,
323                                       reginfo->rootoid,
324                                       reginfo->rootoid_len);
325             regresult = snmp_oid_compare(request->requestvb->name,
326                                          SNMP_MIN(request->requestvb->
327                                                   name_length,
328                                                   reginfo->rootoid_len),
329                                          reginfo->rootoid,
330                                          reginfo->rootoid_len);
331             if (regresult == 0
332                 && request->requestvb->name_length < reginfo->rootoid_len)
333                 regresult = -1;
334
335             if (result < 0 || 0 == result) {
336                 /*
337                  * before us entirely, return the first 
338                  */
339                 row = table->first_row;
340                 table_info->colnum = table_reg_info->min_column;
341             } else if (regresult == 0 && request->requestvb->name_length ==
342                        reginfo->rootoid_len + 1 &&
343                        /* entry node must be 1, but any column is ok */
344                        request->requestvb->name[reginfo->rootoid_len] == 1) {
345                 /*
346                  * exactly to the entry 
347                  */
348                 row = table->first_row;
349                 table_info->colnum = table_reg_info->min_column;
350             } else if (regresult == 0 && request->requestvb->name_length ==
351                        reginfo->rootoid_len + 2 &&
352                        /* entry node must be 1, but any column is ok */
353                        request->requestvb->name[reginfo->rootoid_len] == 1) {
354                 /*
355                  * exactly to the column 
356                  */
357                 row = table->first_row;
358             } else {
359                 /*
360                  * loop through all rows looking for the first one
361                  * that is equal to the request or greater than it 
362                  */
363                 for (row = table->first_row; row; row = row->next) {
364                     /*
365                      * compare the index of the request to the row 
366                      */
367                     result =
368                         snmp_oid_compare(row->index_oid,
369                                          row->index_oid_len,
370                                          request->requestvb->name + 2 +
371                                          reginfo->rootoid_len,
372                                          request->requestvb->name_length -
373                                          2 - reginfo->rootoid_len);
374                     if (result == 0) {
375                         /*
376                          * equal match, return the next row 
377                          */
378                         if (row) {
379                             row = row->next;
380                         }
381                         break;
382                     } else if (result > 0) {
383                         /*
384                          * the current row is greater than the
385                          * request, use it 
386                          */
387                         break;
388                     }
389                 }
390             }
391             if (!row) {
392                 table_info->colnum++;
393                 if (table_info->colnum <= table_reg_info->max_column) {
394                     row = table->first_row;
395                 }
396             }
397             if (row) {
398                 valid_request = 1;
399                 netsnmp_request_add_list_data(request,
400                                               netsnmp_create_data_list
401                                               (TABLE_DATA_NAME, row,
402                                                NULL));
403                 /*
404                  * Set the name appropriately, so we can pass this
405                  *  request on as a simple GET request
406                  */
407                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
408                                                 row,
409                                                 table_info->colnum,
410                                                 ASN_NULL, NULL, 0);
411             } else {            /* no decent result found.  Give up. It's beyond us. */
412                 request->processed = 1;
413             }
414             break;
415
416         case MODE_GET:
417             if (request->requestvb->type != ASN_NULL)
418                 continue;
419             /*
420              * find the row in question 
421              */
422             if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */
423                 /*
424                  * request too short 
425                  */
426                 netsnmp_set_request_error(reqinfo, request,
427                                           SNMP_NOSUCHINSTANCE);
428                 break;
429             } else if (NULL ==
430                        (row =
431                         netsnmp_table_data_get_from_oid(table,
432                                                         request->
433                                                         requestvb->name +
434                                                         reginfo->
435                                                         rootoid_len + 2,
436                                                         request->
437                                                         requestvb->
438                                                         name_length -
439                                                         reginfo->
440                                                         rootoid_len -
441                                                         2))) {
442                 /*
443                  * no such row 
444                  */
445                 netsnmp_set_request_error(reqinfo, request,
446                                           SNMP_NOSUCHINSTANCE);
447                 break;
448             } else {
449                 valid_request = 1;
450                 netsnmp_request_add_list_data(request,
451                                               netsnmp_create_data_list
452                                               (TABLE_DATA_NAME, row,
453                                                NULL));
454             }
455             break;
456
457         case MODE_SET_RESERVE1:
458             valid_request = 1;
459             if (NULL !=
460                 (row =
461                  netsnmp_table_data_get_from_oid(table,
462                                                  request->requestvb->name +
463                                                  reginfo->rootoid_len + 2,
464                                                  request->requestvb->
465                                                  name_length -
466                                                  reginfo->rootoid_len -
467                                                  2))) {
468                 netsnmp_request_add_list_data(request,
469                                               netsnmp_create_data_list
470                                               (TABLE_DATA_NAME, row,
471                                                NULL));
472             }
473             break;
474
475         case MODE_SET_RESERVE2:
476         case MODE_SET_ACTION:
477         case MODE_SET_COMMIT:
478         case MODE_SET_FREE:
479         case MODE_SET_UNDO:
480             valid_request = 1;
481
482         }
483     }
484
485     if (valid_request) {
486         /*
487          * If this is a GetNext or GetBulk request, then we've identified
488          *  the row that ought to include the appropriate next instance.
489          *  Convert the request into a Get request, so that the lower-level
490          *  handlers don't need to worry about skipping on....
491          */
492         oldmode = reqinfo->mode;
493         if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
494             reqinfo->mode = MODE_GET;
495         }
496         result = netsnmp_call_next_handler(handler, reginfo, reqinfo,
497                                          requests);
498         if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) {       /* XXX */
499             for (request = requests; request; request = request->next) {
500                 /*
501                  *  ... but if the lower-level handlers aren't dealing with
502                  *  skipping on to the next instance, then we must handle
503                  *  this situation here.
504                  *    Mark 'holes' in the table as needing to be retried.
505                  *
506                  *    This approach is less efficient than handling such
507                  *  holes directly in the table_dataset handler, but allows
508                  *  user-provided handlers to override the dataset handler
509                  *  if this proves necessary.
510                  */
511                 if (requests->requestvb->type == ASN_NULL ||
512                     requests->requestvb->type == SNMP_NOSUCHINSTANCE) {
513                     requests->requestvb->type = ASN_PRIV_RETRY;
514                 }
515             }
516             reqinfo->mode = oldmode;
517         }
518         return result;
519     }
520     else
521         return SNMP_ERR_NOERROR;
522 }
523
524 /** creates and returns a pointer to table data set */
525 netsnmp_table_data *
526 netsnmp_create_table_data(const char *name)
527 {
528     netsnmp_table_data *table = SNMP_MALLOC_TYPEDEF(netsnmp_table_data);
529     if (name && table)
530         table->name = strdup(name);
531     return table;
532 }
533
534 /** creates and returns a pointer to table data set */
535 netsnmp_table_row *
536 netsnmp_create_table_data_row(void)
537 {
538     netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
539     return row;
540 }
541
542 /** extracts the row being accessed passed from the table_data helper */
543 netsnmp_table_row *
544 netsnmp_extract_table_row(netsnmp_request_info *request)
545 {
546     return (netsnmp_table_row *) netsnmp_request_get_list_data(request,
547                                                                TABLE_DATA_NAME);
548 }
549
550 /** extracts the data from the row being accessed passed from the
551  * table_data helper */
552 void           *
553 netsnmp_extract_table_row_data(netsnmp_request_info *request)
554 {
555     netsnmp_table_row *row;
556     row = (netsnmp_table_row *) netsnmp_extract_table_row(request);
557     if (row)
558         return row->data;
559     else
560         return NULL;
561 }
562
563 /** builds a result given a row, a varbind to set and the data */
564 int
565 netsnmp_table_data_build_result(netsnmp_handler_registration *reginfo,
566                                 netsnmp_agent_request_info *reqinfo,
567                                 netsnmp_request_info *request,
568                                 netsnmp_table_row *row,
569                                 int column,
570                                 u_char type,
571                                 u_char * result_data,
572                                 size_t result_data_len)
573 {
574     oid             build_space[MAX_OID_LEN];
575
576     if (!reginfo || !reqinfo || !request)
577         return SNMPERR_GENERR;
578
579     if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
580         /*
581          * only need to do this for getnext type cases where oid is changing 
582          */
583         memcpy(build_space, reginfo->rootoid,   /* registered oid */
584                reginfo->rootoid_len * sizeof(oid));
585         build_space[reginfo->rootoid_len] = 1;  /* entry */
586         build_space[reginfo->rootoid_len + 1] = column; /* column */
587         memcpy(build_space + reginfo->rootoid_len + 2,  /* index data */
588                row->index_oid, row->index_oid_len * sizeof(oid));
589         snmp_set_var_objid(request->requestvb, build_space,
590                            reginfo->rootoid_len + 2 + row->index_oid_len);
591     }
592     snmp_set_var_typed_value(request->requestvb, type,
593                              result_data, result_data_len);
594     return SNMPERR_SUCCESS;     /* WWWXXX: check for bounds */
595 }
596
597 /** clones a data row. DOES NOT CLONE THE CONTAINED DATA. */
598 netsnmp_table_row *
599 netsnmp_table_data_clone_row(netsnmp_table_row *row)
600 {
601     netsnmp_table_row *newrow = NULL;
602     if (!row)
603         return NULL;
604
605     memdup((u_char **) & newrow, (u_char *) row,
606            sizeof(netsnmp_table_row));
607     if (!newrow)
608         return NULL;
609
610     if (row->indexes) {
611         newrow->indexes = snmp_clone_varbind(newrow->indexes);
612         if (!newrow->indexes)
613             return NULL;
614     }
615
616     if (row->index_oid) {
617         memdup((u_char **) & newrow->index_oid,
618                (u_char *) row->index_oid,
619                row->index_oid_len * sizeof(oid));
620         if (!newrow->index_oid)
621             return NULL;
622     }
623
624     return newrow;
625 }
626
627 /*
628  * @} 
629  */
630
631 #endif /* BRCM_SNMP_SUPPORT */