net-snmp 5.7
container_iterator.c
00001 /*
00002  * $Id$
00003  *
00004  */
00005 
00006 #include <net-snmp/net-snmp-config.h>
00007 #include <net-snmp/net-snmp-features.h>
00008 
00009 #include <stdio.h>
00010 #if HAVE_STDLIB_H
00011 #include <stdlib.h>
00012 #endif
00013 #if HAVE_MALLOC_H
00014 #include <malloc.h>
00015 #endif
00016 #include <sys/types.h>
00017 #if HAVE_STRING_H
00018 #include <string.h>
00019 #else
00020 #include <strings.h>
00021 #endif
00022 
00023 #include <net-snmp/net-snmp-includes.h>
00024 #include <net-snmp/types.h>
00025 #include <net-snmp/library/snmp_api.h>
00026 #include <net-snmp/library/container.h>
00027 #include <net-snmp/library/tools.h>
00028 #include <net-snmp/library/snmp_assert.h>
00029 
00030 #include <net-snmp/library/container_iterator.h>
00031 
00032 netsnmp_feature_child_of(container_iterator, container_types)
00033 
00034 #ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR
00035 
00043 typedef struct iterator_info_s {
00044    /*
00045     * netsnmp_conatiner  must be first
00046     */
00047    netsnmp_container c;
00048 
00049    /*
00050     * iterator data
00051     */
00052    Netsnmp_Iterator_Loop_Key *get_first;
00053    Netsnmp_Iterator_Loop_Key *get_next;
00054    
00055    Netsnmp_Iterator_Loop_Data *get_data;
00056 
00057    Netsnmp_Iterator_Data *free_user_ctx;
00058    
00059    Netsnmp_Iterator_Ctx *init_loop_ctx;
00060    Netsnmp_Iterator_Ctx *cleanup_loop_ctx;
00061    Netsnmp_Iterator_Ctx_Dup *save_pos;
00062    
00063    Netsnmp_Iterator_Data * release_data;
00064    Netsnmp_Iterator_Data * insert_data;
00065    Netsnmp_Iterator_Data * remove_data;
00066 
00067    Netsnmp_Iterator_Op * get_size;
00068    
00069    int             sorted;
00070    
00073    void           *user_ctx;
00074 } iterator_info;
00075 
00076 /**********************************************************************
00077  *
00078  * iterator
00079  *
00080  **********************************************************************/
00081 static void *
00082 _iterator_get(iterator_info *ii, const void *key)
00083 {
00084     int cmp, rc = SNMP_ERR_NOERROR;
00085     netsnmp_ref_void best = { NULL };
00086     netsnmp_ref_void tmp = { NULL };
00087     netsnmp_ref_void loop_ctx = { NULL };
00088 
00089     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_get"));
00090     
00091     if(ii->init_loop_ctx)
00092         ii->init_loop_ctx(ii->user_ctx, &loop_ctx);
00093     
00094     rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp);
00095     if(SNMP_ERR_NOERROR != rc) {
00096         if(SNMP_ENDOFMIBVIEW != rc)
00097             snmp_log(LOG_ERR, "bad rc %d from get_next\n", rc);
00098     }
00099     else {
00100         for( ;
00101              (NULL != tmp.val) && (SNMP_ERR_NOERROR == rc);
00102              rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) {
00103             
00104             /*
00105              * if keys are equal, we are done.
00106              */
00107             cmp = ii->c.compare(tmp.val, key);
00108             if(0 == cmp) {
00109                 best.val = tmp.val;
00110                 if(ii->get_data)
00111                     ii->get_data(ii->user_ctx, &loop_ctx, &best);
00112             }
00113             
00114             /*
00115              * if data is sorted and if key is greater,
00116              * we are done (not found)
00117              */
00118             if((cmp > 0) && ii->sorted)
00119                 break;
00120         } /* end for */
00121     }
00122     
00123     if(ii->cleanup_loop_ctx)
00124         ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx);
00125 
00126     return best.val;
00127 }
00128 
00136 static void *
00137 _iterator_get_next(iterator_info *ii, const void *key)
00138 {
00139     int cmp, rc = SNMP_ERR_NOERROR;
00140     netsnmp_ref_void best_val = { NULL };
00141     netsnmp_ref_void best_ctx = { NULL };
00142     netsnmp_ref_void tmp = { NULL };
00143     netsnmp_ref_void loop_ctx = { NULL };
00144 
00145     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_get_next"));
00146     
00147     /*
00148      * initialize loop context
00149      */
00150     if(ii->init_loop_ctx)
00151         ii->init_loop_ctx(ii->user_ctx, &loop_ctx);
00152     
00153     /*
00154      * get first item
00155      */
00156     rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp);
00157     if(SNMP_ERR_NOERROR == rc) {
00158         /*
00159          * special case: if key is null, find the first item.
00160          * this is each if the container is sorted, since we're
00161          * already done!  Otherwise, get the next item for the
00162          * first comparison in the loop below.
00163          */
00164         if (NULL == key) {
00165             if(ii->get_data)
00166                 ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1);
00167             best_val.val = tmp.val;
00168             if(ii->sorted)
00169                 tmp.val = NULL; /* so we skip for loop */
00170             else
00171                 rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp);
00172         }
00173         /*
00174          * loop over remaining items
00175          */
00176         for( ;
00177              (NULL != tmp.val) && (rc == SNMP_ERR_NOERROR);
00178              rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) ) {
00179             
00180             /*
00181              * if we have a key, this is a get-next, and we need to compare
00182              * the key to the tmp value to see if the tmp value is greater
00183              * than the key, but less than any previous match.
00184              *
00185              * if there is no key, this is a get-first, and we need to
00186              * compare the best value agains the tmp value to see if the
00187              * tmp value is lesser than the best match.
00188              */
00189             if(key) /* get next */
00190                 cmp = ii->c.compare(tmp.val, key);
00191             else { /* get first */
00192                 /*
00193                  * best value and tmp value should never be equal,
00194                  * otherwise we'd be comparing a pointer to itself.
00195                  * (see note on context reuse in comments above function.
00196                  */
00197                 if(best_val.val == tmp.val) {
00198                     snmp_log(LOG_ERR,"illegal reuse of data context in "
00199                              "container_iterator\n");
00200                     rc = SNMP_ERR_GENERR;
00201                     break;
00202                 }
00203                 cmp = ii->c.compare(best_val.val, tmp.val);
00204             }
00205             if(cmp > 0) {
00206                 /*
00207                  * if we don't have a key (get-first) or a current best match,
00208                  * then the comparison above is all we need to know that
00209                  * tmp is the best match. otherwise, compare against the
00210                  * current best match.
00211                  */
00212                 if((NULL == key) || (NULL == best_val.val) ||
00213                    ((cmp=ii->c.compare(tmp.val, best_val.val)) < 0) ) {
00214                     DEBUGMSGT(("container_iterator:results"," best match\n"));
00215                     best_val.val = tmp.val;
00216                     if(ii->get_data)
00217                         ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1);
00218                 }
00219             }
00220             else if((cmp == 0) && ii->sorted && key) {
00221                 /*
00222                  * if keys are equal and container is sorted, then we know
00223                  * the next key will be the one we want.
00224                  * NOTE: if no vars, treat as generr, since we
00225                  *    went past the end of the container when we know
00226                  *    the next item is the one we want. (IGN-A)
00227                  */
00228                 rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp);
00229                 if(SNMP_ERR_NOERROR == rc) {
00230                     best_val.val = tmp.val;
00231                     if(ii->get_data)
00232                         ii->save_pos(ii->user_ctx, &loop_ctx, &best_ctx, 1);
00233                 }
00234                 else if(SNMP_ENDOFMIBVIEW == rc)
00235                     rc = SNMPERR_GENERR; /* not found */
00236                 break;
00237             }
00238             
00239         } /* end for */
00240     }
00241 
00242     /*
00243      * no vars is ok, except as noted above (IGN-A)
00244      */
00245     if(SNMP_ENDOFMIBVIEW == rc)
00246         rc = SNMP_ERR_NOERROR;
00247             
00248     /*
00249      * get data, iff necessary
00250      * clear return value iff errors
00251      */
00252     if(SNMP_ERR_NOERROR == rc) {
00253         if(ii->get_data && best_val.val) {
00254             rc = ii->get_data(ii->user_ctx, &best_ctx, &best_val);
00255             if(SNMP_ERR_NOERROR != rc) {
00256                 snmp_log(LOG_ERR, "bad rc %d from get_data\n", rc);
00257                 best_val.val = NULL;
00258             }
00259         }
00260     }
00261     else if(SNMP_ENDOFMIBVIEW != rc) {
00262         snmp_log(LOG_ERR, "bad rc %d from get_next\n", rc);
00263         best_val.val = NULL;
00264     }
00265 
00266     /*
00267      * if we have a saved loop ctx, clean it up
00268      */
00269     if((best_ctx.val != NULL) && (best_ctx.val != loop_ctx.val) &&
00270        (ii->cleanup_loop_ctx))
00271         ii->cleanup_loop_ctx(ii->user_ctx,&best_ctx);
00272 
00273     /*
00274      * clean up loop ctx
00275      */
00276     if(ii->cleanup_loop_ctx)
00277         ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx);
00278 
00279     DEBUGMSGT(("container_iterator:results"," returning %p\n", best_val.val));
00280     return best_val.val;
00281 }
00282 
00283 /**********************************************************************
00284  *
00285  * container
00286  *
00287  **********************************************************************/
00288 static void
00289 _iterator_free(iterator_info *ii)
00290 {
00291     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_free"));
00292     
00293     if(NULL == ii)
00294         return;
00295     
00296     if(ii->user_ctx)
00297         ii->free_user_ctx(ii->user_ctx,ii->user_ctx);
00298     
00299     free(ii);
00300 }
00301 
00302 static void *
00303 _iterator_find(iterator_info *ii, const void *data)
00304 {
00305     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_find"));
00306     
00307     if((NULL == ii) || (NULL == data))
00308         return NULL;
00309 
00310     return _iterator_get(ii, data);
00311 }
00312 
00313 static void *
00314 _iterator_find_next(iterator_info *ii, const void *data)
00315 {
00316     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_find_next"));
00317     
00318     if(NULL == ii)
00319         return NULL;
00320 
00321     return _iterator_get_next(ii, data);
00322 }
00323 
00324 static int
00325 _iterator_insert(iterator_info *ii, const void *data)
00326 {
00327     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_insert"));
00328     
00329     if(NULL == ii)
00330         return -1;
00331 
00332     if(NULL == ii->insert_data)
00333         return -1;
00334 
00335     return ii->insert_data(ii->user_ctx, data);
00336 }
00337 
00338 static int
00339 _iterator_remove(iterator_info *ii, const void *data)
00340 {
00341     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_remove"));
00342     
00343     if(NULL == ii)
00344         return -1;
00345 
00346     if(NULL == ii->remove_data)
00347         return -1;
00348 
00349     return ii->remove_data(ii->user_ctx, data);
00350 }
00351 
00352 static int
00353 _iterator_release(iterator_info *ii, const void *data)
00354 {
00355     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_release"));
00356     
00357     if(NULL == ii)
00358         return -1;
00359 
00360     if(NULL == ii->release_data)
00361         return -1;
00362 
00363     return ii->release_data(ii->user_ctx, data);
00364 }
00365 
00366 static size_t
00367 _iterator_size(iterator_info *ii)
00368 {
00369     size_t count = 0;
00370     int rc = SNMP_ERR_NOERROR;
00371     netsnmp_ref_void loop_ctx = { NULL };
00372     netsnmp_ref_void tmp = { NULL };
00373 
00374     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_size"));
00375     
00376     if(NULL == ii)
00377         return -1;
00378 
00379     if(NULL != ii->get_size)
00380         return ii->get_size(ii->user_ctx);
00381 
00382     /*
00383      * no get_size. loop and count ourselves
00384      */
00385     if(ii->init_loop_ctx)
00386         ii->init_loop_ctx(ii->user_ctx, &loop_ctx);
00387     
00388     for( rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp);
00389          NULL != tmp.val;
00390          rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) )
00391         ++count;
00392 
00393     if(ii->cleanup_loop_ctx)
00394         ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx);
00395 
00396     return count;
00397 }
00398 
00399 static void
00400 _iterator_for_each(iterator_info *ii, netsnmp_container_obj_func *f,
00401                    void *ctx)
00402 {
00403     int rc = SNMP_ERR_NOERROR;
00404     netsnmp_ref_void loop_ctx = { NULL };
00405     netsnmp_ref_void tmp = { NULL };
00406 
00407     DEBUGMSGT(("container_iterator",">%s\n", "_iterator_foreach"));
00408     
00409     if(NULL == ii)
00410         return;
00411 
00412     if(ii->init_loop_ctx)
00413         ii->init_loop_ctx(ii->user_ctx, &loop_ctx);
00414     
00415     for( rc = ii->get_first(ii->user_ctx, &loop_ctx, &tmp);
00416          NULL != tmp.val;
00417          rc = ii->get_next(ii->user_ctx, &loop_ctx, &tmp) )
00418         (*f) (tmp.val, ctx);
00419 
00420     if(ii->cleanup_loop_ctx)
00421         ii->cleanup_loop_ctx(ii->user_ctx,&loop_ctx);
00422 }
00423 
00424 static void
00425 _iterator_clear(netsnmp_container *container, netsnmp_container_obj_func *f,
00426                  void *context)
00427 {
00428     snmp_log(LOG_WARNING,"clear is meaningless for iterator container.\n");
00429 }
00430 
00431 /**********************************************************************
00432  *
00433  */
00434 netsnmp_container*
00435 netsnmp_container_iterator_get(void *iterator_user_ctx,
00436                                netsnmp_container_compare * compare,
00437                                Netsnmp_Iterator_Loop_Key * get_first,
00438                                Netsnmp_Iterator_Loop_Key * get_next,
00439                                Netsnmp_Iterator_Loop_Data * get_data,
00440                                Netsnmp_Iterator_Ctx_Dup * save_pos,
00441                                Netsnmp_Iterator_Ctx * init_loop_ctx,
00442                                Netsnmp_Iterator_Ctx * cleanup_loop_ctx,
00443                                Netsnmp_Iterator_Data * free_user_ctx,
00444                                int sorted)
00445 {
00446     iterator_info *ii;
00447 
00448     /*
00449      * sanity checks
00450      */
00451     if(get_data && ! save_pos) {
00452         snmp_log(LOG_ERR, "save_pos required with get_data\n");
00453         return NULL;
00454     }
00455 
00456     /*
00457      * allocate memory
00458      */
00459     ii = SNMP_MALLOC_TYPEDEF(iterator_info);
00460     if (NULL==ii) {
00461         snmp_log(LOG_ERR, "couldn't allocate memory\n");
00462         return NULL;
00463     }
00464 
00465     /*
00466      * init container structure with iterator functions
00467      */
00468     ii->c.cfree = (netsnmp_container_rc*)_iterator_free;
00469     ii->c.compare = compare;
00470     ii->c.get_size = (netsnmp_container_size*)_iterator_size;
00471     ii->c.init = NULL;
00472     ii->c.insert = (netsnmp_container_op*)_iterator_insert;
00473     ii->c.remove = (netsnmp_container_op*)_iterator_remove;
00474     ii->c.release = (netsnmp_container_op*)_iterator_release;
00475     ii->c.find = (netsnmp_container_rtn*)_iterator_find;
00476     ii->c.find_next = (netsnmp_container_rtn*)_iterator_find_next;
00477     ii->c.get_subset = NULL;
00478     ii->c.get_iterator = NULL;
00479     ii->c.for_each = (netsnmp_container_func*)_iterator_for_each;
00480     ii->c.clear = _iterator_clear;
00481 
00482     /*
00483      * init iterator structure with user functions
00484      */
00485     ii->get_first = get_first;
00486     ii->get_next = get_next;
00487     ii->get_data = get_data;
00488     ii->save_pos = save_pos;
00489     ii->init_loop_ctx = init_loop_ctx;
00490     ii->cleanup_loop_ctx = cleanup_loop_ctx;
00491     ii->free_user_ctx = free_user_ctx;
00492     ii->sorted = sorted;
00493 
00494     ii->user_ctx = iterator_user_ctx;
00495 
00496     return (netsnmp_container*)ii;
00497 }
00498 
00499 void
00500 netsnmp_container_iterator_set_data_cb(netsnmp_container *c,
00501                                        Netsnmp_Iterator_Data * insert_data,
00502                                        Netsnmp_Iterator_Data * remove_data,
00503                                        Netsnmp_Iterator_Op * get_size)
00504 {
00505     iterator_info *ii = (iterator_info *)c;
00506     if(NULL == ii)
00507         return;
00508     
00509     ii->insert_data = insert_data;
00510     ii->remove_data = remove_data;
00511     ii->get_size = get_size;
00512 }
00513 #else  /* NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR */
00514 netsnmp_feature_unused(container_iterator);
00515 #endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_ITERATOR */