net-snmp  5.4.1
row_merge.c
00001 #include <net-snmp/net-snmp-config.h>
00002 
00003 #if HAVE_STRING_H
00004 #include <string.h>
00005 #else
00006 #include <strings.h>
00007 #endif
00008 
00009 #include <net-snmp/net-snmp-includes.h>
00010 #include <net-snmp/agent/net-snmp-agent-includes.h>
00011 
00012 #include <net-snmp/agent/row_merge.h>
00013 
00027 netsnmp_mib_handler *
00028 netsnmp_get_row_merge_handler(int prefix_len)
00029 {
00030     netsnmp_mib_handler *ret = NULL;
00031     ret = netsnmp_create_handler("row_merge",
00032                                   netsnmp_row_merge_helper_handler);
00033     if (ret) {
00034         ret->myvoid = (void *)(intptr_t)prefix_len;
00035     }
00036     return ret;
00037 }
00038 
00041 int
00042 netsnmp_register_row_merge(netsnmp_handler_registration *reginfo)
00043 {
00044     netsnmp_inject_handler(reginfo,
00045                     netsnmp_get_row_merge_handler(reginfo->rootoid_len+1));
00046     return netsnmp_register_handler(reginfo);
00047 }
00048 
00049 static void
00050 _rm_status_free(void *mem)
00051 {
00052     netsnmp_row_merge_status *rm_status = (netsnmp_row_merge_status*)mem;
00053 
00054     if (NULL != rm_status->saved_requests)
00055         free(rm_status->saved_requests);
00056 
00057     if (NULL != rm_status->saved_status)
00058         free(rm_status->saved_status);
00059 
00060     free(mem);
00061 }
00062 
00063 
00066 netsnmp_row_merge_status *
00067 netsnmp_row_merge_status_get(netsnmp_handler_registration *reginfo,
00068                              netsnmp_agent_request_info *reqinfo,
00069                              int create_missing)
00070 {
00071     netsnmp_row_merge_status *rm_status;
00072     char buf[64];
00073     int rc;
00074 
00075     /*
00076      * see if we've already been here
00077      */
00078     rc = snprintf(buf, sizeof(buf), "row_merge:%p", reginfo);
00079     if ((-1 == rc) || (rc >= sizeof(buf))) {
00080         snmp_log(LOG_ERR,"error creating key\n");
00081         return NULL;
00082     }
00083     
00084     rm_status = netsnmp_agent_get_list_data(reqinfo, buf);
00085     if ((NULL == rm_status) && create_missing) {
00086         void *data_list;
00087         
00088         rm_status = SNMP_MALLOC_TYPEDEF(netsnmp_row_merge_status);
00089         if (NULL == rm_status) {
00090             snmp_log(LOG_ERR,"error allocating memory\n");
00091             return NULL;
00092         }
00093         data_list = netsnmp_create_data_list(buf, rm_status,
00094                                              _rm_status_free);
00095         if (NULL == data_list) {
00096             free(rm_status);
00097             return NULL;
00098         }
00099         netsnmp_agent_add_list_data(reqinfo, data_list);
00100     }
00101     
00102     return rm_status;
00103 }
00104 
00109 int
00110 netsnmp_row_merge_status_first(netsnmp_handler_registration *reginfo,
00111                                netsnmp_agent_request_info *reqinfo)
00112 {
00113     netsnmp_row_merge_status *rm_status;
00114 
00115     /*
00116      * find status
00117      */
00118     rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0);
00119     if (NULL == rm_status)
00120         return 0;
00121 
00122     return (rm_status->count == 1) ? 1 : (rm_status->current == 1);
00123 }
00124 
00129 int
00130 netsnmp_row_merge_status_last(netsnmp_handler_registration *reginfo,
00131                               netsnmp_agent_request_info *reqinfo)
00132 {
00133     netsnmp_row_merge_status *rm_status;
00134 
00135     /*
00136      * find status
00137      */
00138     rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 0);
00139     if (NULL == rm_status)
00140         return 0;
00141 
00142     return (rm_status->count == 1) ? 1 :
00143         (rm_status->current == rm_status->rows);
00144 }
00145 
00146 
00147 #define ROW_MERGE_WAITING 0
00148 #define ROW_MERGE_ACTIVE  1
00149 #define ROW_MERGE_DONE    2
00150 #define ROW_MERGE_HEAD    3
00151 
00153 int
00154 netsnmp_row_merge_helper_handler(netsnmp_mib_handler *handler,
00155                                  netsnmp_handler_registration *reginfo,
00156                                  netsnmp_agent_request_info *reqinfo,
00157                                  netsnmp_request_info *requests)
00158 {
00159     netsnmp_request_info *request, **saved_requests;
00160     char *saved_status;
00161     netsnmp_row_merge_status *rm_status;
00162     int i, j, ret, tail, count, final_rc = SNMP_ERR_NOERROR;
00163 
00164     /*
00165      * Use the prefix length as supplied during registration, rather
00166      *  than trying to second-guess what the MIB implementer wanted.
00167      */
00168     int SKIP_OID = (int)(intptr_t)handler->myvoid;
00169 
00170     DEBUGMSGTL(("helper:row_merge", "Got request (%d): ", SKIP_OID));
00171     DEBUGMSGOID(("helper:row_merge", reginfo->rootoid, reginfo->rootoid_len));
00172     DEBUGMSG(("helper:row_merge", "\n"));
00173 
00174     /*
00175      * find or create status
00176      */
00177     rm_status = netsnmp_row_merge_status_get(reginfo, reqinfo, 1);
00178 
00179     /*
00180      * Count the requests, and set up an array to keep
00181      *  track of the original order.
00182      */
00183     for (count = 0, request = requests; request; request = request->next) {
00184         DEBUGIF("helper:row_merge") {
00185             DEBUGMSGTL(("helper:row_merge", "  got varbind: "));
00186             DEBUGMSGOID(("helper:row_merge", request->requestvb->name,
00187                          request->requestvb->name_length));
00188             DEBUGMSG(("helper:row_merge", "\n"));
00189         }
00190         count++;
00191     }
00192 
00193     /*
00194      * Optimization: skip all this if there is just one request
00195      */
00196     if(count == 1) {
00197         rm_status->count = count;
00198         if (requests->processed)
00199             return SNMP_ERR_NOERROR;
00200         return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
00201     }
00202 
00203     /*
00204      * we really should only have to do this once, instead of every pass.
00205      * as a precaution, we'll do it every time, but put in some asserts
00206      * to see if we have to.
00207      */
00208     /*
00209      * if the count changed, re-do everything
00210      */
00211     if ((0 != rm_status->count) && (rm_status->count != count)) {
00212         /*
00213          * ok, i know next/bulk can cause this condition. Probably
00214          * GET, too. need to rethink this mode counting. maybe
00215          * add the mode to the rm_status structure? xxx-rks
00216          */
00217         if ((reqinfo->mode != MODE_GET) &&
00218             (reqinfo->mode != MODE_GETNEXT) &&
00219             (reqinfo->mode != MODE_GETBULK)) {
00220             netsnmp_assert((NULL != rm_status->saved_requests) &&
00221                            (NULL != rm_status->saved_status));
00222         }
00223         DEBUGMSGTL(("helper:row_merge", "count changed! do over...\n"));
00224 
00225         SNMP_FREE(rm_status->saved_requests);
00226         SNMP_FREE(rm_status->saved_status);
00227         
00228         rm_status->count = 0;
00229         rm_status->rows = 0;
00230     }
00231 
00232     if (0 == rm_status->count) {
00233         /*
00234          * allocate memory for saved structure
00235          */
00236         rm_status->saved_requests =
00237             (netsnmp_request_info**)calloc(count+1,
00238                                            sizeof(netsnmp_request_info*));
00239         rm_status->saved_status = (char*)calloc(count,sizeof(char));
00240     }
00241 
00242     saved_status = rm_status->saved_status;
00243     saved_requests = rm_status->saved_requests;
00244 
00245     /*
00246      * set up saved requests, and set any processed requests to done
00247      */
00248     i = 0;
00249     for (request = requests; request; request = request->next, i++) {
00250         if (request->processed) {
00251             saved_status[i] = ROW_MERGE_DONE;
00252             DEBUGMSGTL(("helper:row_merge", "  skipping processed oid: "));
00253             DEBUGMSGOID(("helper:row_merge", request->requestvb->name,
00254                          request->requestvb->name_length));
00255             DEBUGMSG(("helper:row_merge", "\n"));
00256         }
00257         else
00258             saved_status[i] = ROW_MERGE_WAITING;
00259         if (0 != rm_status->count)
00260             netsnmp_assert(saved_requests[i] == request);
00261         saved_requests[i] = request;
00262         saved_requests[i]->prev = NULL;
00263     }
00264     saved_requests[i] = NULL;
00265 
00266     /*
00267      * Note that saved_requests[count] is valid
00268      *    (because of the 'count+1' in the calloc above),
00269      * but NULL (since it's past the end of the list).
00270      * This simplifies the re-linking later.
00271      */
00272 
00273     /*
00274      * Work through the (unprocessed) requests in order.
00275      * For each of these, search the rest of the list for any
00276      *   matching indexes, and link them into a new list.
00277      */
00278     for (i=0; i<count; i++) {
00279         if (saved_status[i] != ROW_MERGE_WAITING)
00280             continue;
00281 
00282         if (0 == rm_status->count)
00283             rm_status->rows++;
00284         DEBUGMSGTL(("helper:row_merge", " row %d oid[%d]: ", rm_status->rows, i));
00285         DEBUGMSGOID(("helper:row_merge", saved_requests[i]->requestvb->name,
00286                      saved_requests[i]->requestvb->name_length));
00287         DEBUGMSG(("helper:row_merge", "\n"));
00288 
00289         saved_requests[i]->next = NULL;
00290         saved_status[i] = ROW_MERGE_HEAD;
00291         tail = i;
00292         for (j=i+1; j<count; j++) {
00293             if (saved_status[j] != ROW_MERGE_WAITING)
00294                 continue;
00295 
00296             DEBUGMSGTL(("helper:row_merge", "? oid[%d]: ", j));
00297             DEBUGMSGOID(("helper:row_merge",
00298                          saved_requests[j]->requestvb->name,
00299                          saved_requests[j]->requestvb->name_length));
00300             if (!snmp_oid_compare(
00301                     saved_requests[i]->requestvb->name+SKIP_OID,
00302                     saved_requests[i]->requestvb->name_length-SKIP_OID,
00303                     saved_requests[j]->requestvb->name+SKIP_OID,
00304                     saved_requests[j]->requestvb->name_length-SKIP_OID)) {
00305                 DEBUGMSG(("helper:row_merge", " match\n"));
00306                 saved_requests[tail]->next = saved_requests[j];
00307                 saved_requests[j]->next    = NULL;
00308                 saved_requests[j]->prev = saved_requests[tail];
00309                 saved_status[j] = ROW_MERGE_ACTIVE;
00310                 tail = j;
00311             }
00312             else
00313                 DEBUGMSG(("helper:row_merge", " no match\n"));
00314         }
00315     }
00316 
00317     /*
00318      * not that we have a list for each row, call next handler...
00319      */
00320     if (0 == rm_status->count)
00321         rm_status->count = count;
00322     rm_status->current = 0;
00323     for (i=0; i<count; i++) {
00324         if (saved_status[i] != ROW_MERGE_HEAD)
00325             continue;
00326 
00327         /*
00328          * found the head of a new row,
00329          * call the next handler with this list
00330          */
00331         rm_status->current++;
00332         ret = netsnmp_call_next_handler(handler, reginfo, reqinfo,
00333                                         saved_requests[i]);
00334         if (ret != SNMP_ERR_NOERROR) {
00335             snmp_log(LOG_WARNING,
00336                      "bad rc (%d) from next handler in row_merge\n", ret);
00337             if (SNMP_ERR_NOERROR == final_rc)
00338                 final_rc = ret;
00339         }
00340     }
00341 
00342     /*
00343      * restore original linked list
00344      */
00345     for (i=0; i<count; i++) {
00346         saved_requests[i]->next = saved_requests[i+1];
00347         if (i>0)
00348             saved_requests[i]->prev = saved_requests[i-1];
00349     }
00350 
00351     return final_rc;
00352 }
00353 
00359 void
00360 netsnmp_init_row_merge(void)
00361 {
00362     netsnmp_register_handler_by_name("row_merge",
00363                                      netsnmp_get_row_merge_handler(-1));
00364 }