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