net-snmp 5.7
baby_steps.c
00001 /*
00002  * baby_steps.c
00003  * $Id$
00004  */
00005 #include <net-snmp/net-snmp-config.h>
00006 #include <net-snmp/net-snmp-features.h>
00007 #include <net-snmp/net-snmp-includes.h>
00008 #include <net-snmp/agent/net-snmp-agent-includes.h>
00009 
00010 netsnmp_feature_provide(baby_steps)
00011 netsnmp_feature_child_of(baby_steps, mib_helpers)
00012 
00013 #ifdef NETSNMP_FEATURE_REQUIRE_BABY_STEPS
00014 netsnmp_feature_require(check_requests_error)
00015 #endif
00016 
00017 #ifndef NETSNMP_FEATURE_REMOVE_BABY_STEPS
00018 
00019 #include <net-snmp/agent/baby_steps.h>
00020 
00021 #define BABY_STEPS_PER_MODE_MAX     4
00022 #define BSTEP_USE_ORIGINAL          0xffff
00023 
00024 static u_short get_mode_map[BABY_STEPS_PER_MODE_MAX] = {
00025     MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, BSTEP_USE_ORIGINAL, MODE_BSTEP_POST_REQUEST };
00026 
00027 #ifndef NETSNMP_NO_WRITE_SUPPORT
00028 static u_short set_mode_map[SNMP_MSG_INTERNAL_SET_MAX][BABY_STEPS_PER_MODE_MAX] = {
00029     /*R1*/
00030     { MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, MODE_BSTEP_ROW_CREATE,
00031       MODE_BSTEP_CHECK_VALUE },
00032     /*R2*/
00033     { MODE_BSTEP_UNDO_SETUP, BABY_STEP_NONE, BABY_STEP_NONE, BABY_STEP_NONE },
00034     /*A */
00035     { MODE_BSTEP_SET_VALUE,MODE_BSTEP_CHECK_CONSISTENCY,
00036       MODE_BSTEP_COMMIT, BABY_STEP_NONE },
00037     /*C */
00038     { MODE_BSTEP_IRREVERSIBLE_COMMIT, MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST,
00039       BABY_STEP_NONE},
00040     /*F */
00041     { MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST, BABY_STEP_NONE,
00042       BABY_STEP_NONE },
00043     /*U */
00044     { MODE_BSTEP_UNDO_COMMIT, MODE_BSTEP_UNDO_SET, MODE_BSTEP_UNDO_CLEANUP,
00045       MODE_BSTEP_POST_REQUEST}
00046 };
00047 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00048 
00049 static int
00050 _baby_steps_helper(netsnmp_mib_handler *handler,
00051                    netsnmp_handler_registration *reginfo,
00052                    netsnmp_agent_request_info *reqinfo,
00053                    netsnmp_request_info *requests);
00054 static int
00055 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
00056                                netsnmp_handler_registration *reginfo,
00057                                netsnmp_agent_request_info *reqinfo,
00058                                netsnmp_request_info *requests);
00059     
00066 static netsnmp_baby_steps_modes *
00067 netsnmp_baby_steps_modes_ref(netsnmp_baby_steps_modes *md)
00068 {
00069     md->refcnt++;
00070     return md;
00071 }
00072 
00073 static void
00074 netsnmp_baby_steps_modes_deref(netsnmp_baby_steps_modes *md)
00075 {
00076     if (--md->refcnt == 0)
00077         free(md);
00078 }
00079 
00083 netsnmp_mib_handler *
00084 netsnmp_baby_steps_handler_get(u_long modes)
00085 {
00086     netsnmp_mib_handler *mh;
00087     netsnmp_baby_steps_modes *md;
00088 
00089     mh = netsnmp_create_handler("baby_steps", _baby_steps_helper);
00090     if(!mh)
00091         return NULL;
00092 
00093     md = SNMP_MALLOC_TYPEDEF(netsnmp_baby_steps_modes);
00094     if (NULL == md) {
00095         snmp_log(LOG_ERR,"malloc failed in netsnmp_baby_steps_handler_get\n");
00096         netsnmp_handler_free(mh);
00097         mh = NULL;
00098     }
00099     else {
00100         md->refcnt = 1;
00101         mh->myvoid = md;
00102         mh->data_clone = (void *(*)(void *))netsnmp_baby_steps_modes_ref;
00103         mh->data_free = (void (*)(void *))netsnmp_baby_steps_modes_deref;
00104         if (0 == modes)
00105             modes = BABY_STEP_ALL;
00106         md->registered = modes;
00107     }
00108 
00109     /*
00110      * don't set MIB_HANDLER_AUTO_NEXT, since we need to call lower
00111      * handlers with a munged mode.
00112      */
00113     
00114     return mh;
00115 }
00116 
00118 static int
00119 _baby_steps_helper(netsnmp_mib_handler *handler,
00120                          netsnmp_handler_registration *reginfo,
00121                          netsnmp_agent_request_info *reqinfo,
00122                          netsnmp_request_info *requests)
00123 {
00124     netsnmp_baby_steps_modes *bs_modes;
00125     int save_mode, i, rc = SNMP_ERR_NOERROR;
00126     u_short *mode_map_ptr;
00127     
00128     DEBUGMSGTL(("baby_steps", "Got request, mode %s\n",
00129                 se_find_label_in_slist("agent_mode",reqinfo->mode)));
00130 
00131     bs_modes = (netsnmp_baby_steps_modes*)handler->myvoid;
00132     netsnmp_assert(NULL != bs_modes);
00133 
00134     switch (reqinfo->mode) {
00135 
00136 #ifndef NETSNMP_NO_WRITE_SUPPORT
00137     case MODE_SET_RESERVE1:
00138         /*
00139          * clear completed modes
00140          * xxx-rks: this will break for pdus with set requests to different
00141          * rows in the same table when the handler is set up to use the row
00142          * merge helper as well (or if requests are serialized).
00143          */
00144         bs_modes->completed = 0;
00147     case MODE_SET_RESERVE2:
00148     case MODE_SET_ACTION:
00149     case MODE_SET_COMMIT:
00150     case MODE_SET_FREE:
00151     case MODE_SET_UNDO:
00152         mode_map_ptr = set_mode_map[reqinfo->mode];
00153         break;
00154 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00155             
00156     default:
00157         /*
00158          * clear completed modes
00159          */
00160         bs_modes->completed = 0;
00161 
00162         mode_map_ptr = get_mode_map;
00163     }
00164 
00165     /*
00166      * NOTE: if you update this chart, please update the versions in
00167      *       local/mib2c-conf.d/parent-set.m2i
00168      *       agent/mibgroup/helpers/baby_steps.c
00169      * while you're at it.
00170      */
00171     /*
00172      ***********************************************************************
00173      * Baby Steps Flow Chart (2004.06.05)                                  *
00174      *                                                                     *
00175      * +--------------+    +================+    U = unconditional path    *
00176      * |optional state|    ||required state||    S = path for success      *
00177      * +--------------+    +================+    E = path for error        *
00178      ***********************************************************************
00179      *
00180      *                        +--------------+
00181      *                        |     pre      |
00182      *                        |   request    |
00183      *                        +--------------+
00184      *                               | U
00185      * +-------------+        +==============+
00186      * |    row    |f|<-------||  object    ||
00187      * |  create   |1|      E ||  lookup    ||
00188      * +-------------+        +==============+
00189      *     E |   | S                 | S
00190      *       |   +------------------>|
00191      *       |                +==============+
00192      *       |              E ||   check    ||
00193      *       |<---------------||   values   ||
00194      *       |                +==============+
00195      *       |                       | S
00196      *       |                +==============+
00197      *       |       +<-------||   undo     ||
00198      *       |       |      E ||   setup    ||
00199      *       |       |        +==============+
00200      *       |       |               | S
00201      *       |       |        +==============+
00202      *       |       |        ||    set     ||-------------------------->+
00203      *       |       |        ||   value    || E                         |
00204      *       |       |        +==============+                           |
00205      *       |       |               | S                                 |
00206      *       |       |        +--------------+                           |
00207      *       |       |        |    check     |-------------------------->|
00208      *       |       |        |  consistency | E                         |
00209      *       |       |        +--------------+                           |
00210      *       |       |               | S                                 |
00211      *       |       |        +==============+         +==============+  |
00212      *       |       |        ||   commit   ||-------->||     undo   ||  |
00213      *       |       |        ||            || E       ||    commit  ||  |
00214      *       |       |        +==============+         +==============+  |
00215      *       |       |               | S                     U |<--------+
00216      *       |       |        +--------------+         +==============+
00217      *       |       |        | irreversible |         ||    undo    ||
00218      *       |       |        |    commit    |         ||     set    ||
00219      *       |       |        +--------------+         +==============+
00220      *       |       |               | U                     U |
00221      *       |       +-------------->|<------------------------+
00222      *       |                +==============+
00223      *       |                ||   undo     ||
00224      *       |                ||  cleanup   ||
00225      *       |                +==============+
00226      *       +---------------------->| U
00227      *                               |
00228      *                          (err && f1)------------------->+
00229      *                               |                         |
00230      *                        +--------------+         +--------------+
00231      *                        |    post      |<--------|      row     |
00232      *                        |   request    |       U |    release   |
00233      *                        +--------------+         +--------------+
00234      *
00235      */
00236     /*
00237      * save original mode
00238      */
00239     save_mode = reqinfo->mode;
00240     for(i = 0; i < BABY_STEPS_PER_MODE_MAX; ++i ) {
00241         /*
00242          * break if we run out of baby steps for this mode
00243          */
00244         if(mode_map_ptr[i] == BABY_STEP_NONE)
00245             break;
00246 
00247         DEBUGMSGTL(("baby_steps", " baby step mode %s\n",
00248                     se_find_label_in_slist("babystep_mode",mode_map_ptr[i])));
00249 
00250         /*
00251          * skip modes the handler didn't register for
00252          */
00253         if (BSTEP_USE_ORIGINAL != mode_map_ptr[i]) {
00254             u_int    mode_flag;
00255 
00256 #ifndef NETSNMP_NO_WRITE_SUPPORT
00257             /*
00258              * skip undo commit if commit wasn't hit, and
00259              * undo_cleanup if undo_setup wasn't hit.
00260              */
00261             if((MODE_SET_UNDO == save_mode) &&
00262                (MODE_BSTEP_UNDO_COMMIT == mode_map_ptr[i]) &&
00263                !(BABY_STEP_COMMIT & bs_modes->completed)) {
00264                 DEBUGMSGTL(("baby_steps",
00265                             "   skipping commit undo (no commit)\n"));
00266                 continue;
00267             }
00268             else if((MODE_SET_FREE == save_mode) &&
00269                (MODE_BSTEP_UNDO_CLEANUP == mode_map_ptr[i]) &&
00270                !(BABY_STEP_UNDO_SETUP & bs_modes->completed)) {
00271                 DEBUGMSGTL(("baby_steps",
00272                             "   skipping undo cleanup (no undo setup)\n"));
00273                 continue;
00274             }
00275 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00276 
00277             reqinfo->mode = mode_map_ptr[i];
00278             mode_flag = netsnmp_baby_step_mode2flag( mode_map_ptr[i] );
00279             if((mode_flag & bs_modes->registered))
00280                 bs_modes->completed |= mode_flag;
00281             else {
00282                 DEBUGMSGTL(("baby_steps",
00283                             "   skipping mode (not registered)\n"));
00284                 continue;
00285             }
00286 
00287         
00288         }
00289         else {
00290             reqinfo->mode = save_mode;
00291         }
00292 
00293 #ifdef BABY_STEPS_NEXT_MODE
00294         /*
00295          * I can't remember why I wanted the next mode in the request,
00296          * but it's not used anywhere, so don't use this code. saved,
00297          * in case I remember why I thought needed it. - rstory 040911
00298          */
00299         if((BABY_STEPS_PER_MODE_MAX - 1) == i)
00300             reqinfo->next_mode_ok = BABY_STEP_NONE;
00301         else {
00302             if(BSTEP_USE_ORIGINAL == mode_map_ptr[i+1])
00303                 reqinfo->next_mode_ok = save_mode;
00304             else
00305                 reqinfo->next_mode_ok = mode_map_ptr[i+1];
00306         }
00307 #endif
00308 
00309         /*
00310          * call handlers for baby step
00311          */
00312         rc = netsnmp_call_next_handler(handler, reginfo, reqinfo,
00313                                        requests);
00314 
00315         /*
00316          * check for error calling handler (unlikely, but...)
00317          */
00318         if(rc) {
00319             DEBUGMSGTL(("baby_steps", "   ERROR:handler error\n"));
00320             break;
00321         }
00322 
00323         /*
00324          * check for errors in any of the requests for GET-like, reserve1,
00325          * reserve2 and action. (there is no recovery from errors
00326          * in commit, free or undo.)
00327          */
00328         if (MODE_IS_GET(save_mode)
00329 #ifndef NETSNMP_NO_WRITE_SUPPORT
00330             || (save_mode < SNMP_MSG_INTERNAL_SET_COMMIT)
00331 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00332             ) {
00333             rc = netsnmp_check_requests_error(requests);
00334             if(rc) {
00335                 DEBUGMSGTL(("baby_steps", "   ERROR:request error\n"));
00336                 break;
00337             }
00338         }
00339     }
00340 
00341     /*
00342      * restore original mode
00343      */
00344     reqinfo->mode = save_mode;
00345 
00346     
00347     return rc;
00348 }
00349 
00354 netsnmp_feature_child_of(netsnmp_baby_steps_handler_init,netsnmp_unused)
00355 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT
00356 void
00357 netsnmp_baby_steps_handler_init(void)
00358 {
00359     netsnmp_register_handler_by_name("baby_steps",
00360                                      netsnmp_baby_steps_handler_get(BABY_STEP_ALL));
00361 }
00362 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT */
00363 
00374 netsnmp_mib_handler *
00375 netsnmp_baby_steps_access_multiplexer_get(netsnmp_baby_steps_access_methods *am)
00376 {
00377     netsnmp_mib_handler *mh;
00378 
00379     mh = netsnmp_create_handler("baby_steps_mux",
00380                                 _baby_steps_access_multiplexer);
00381     if(!mh)
00382         return NULL;
00383 
00384     mh->myvoid = am;
00385     mh->flags |= MIB_HANDLER_AUTO_NEXT;
00386     
00387     return mh;
00388 }
00389 
00391 static int
00392 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
00393                                netsnmp_handler_registration *reginfo,
00394                                netsnmp_agent_request_info *reqinfo,
00395                                netsnmp_request_info *requests)
00396 {
00397     void *temp_void;
00398     Netsnmp_Node_Handler *method = NULL;
00399     netsnmp_baby_steps_access_methods *access_methods;
00400     int rc = SNMP_ERR_NOERROR;
00401 
00403     netsnmp_assert((handler!=NULL) && (reginfo!=NULL) && (reqinfo!=NULL) &&
00404                    (requests!=NULL));
00405 
00406     DEBUGMSGT(("baby_steps_mux", "mode %s\n",
00407                se_find_label_in_slist("babystep_mode",reqinfo->mode)));
00408 
00409     access_methods = (netsnmp_baby_steps_access_methods *)handler->myvoid;
00410     if(!access_methods) {
00411         snmp_log(LOG_ERR,"baby_steps_access_multiplexer has no methods\n");
00412         return SNMPERR_GENERR;
00413     }
00414 
00415     switch(reqinfo->mode) {
00416         
00417     case MODE_BSTEP_PRE_REQUEST:
00418         if( access_methods->pre_request )
00419             method = access_methods->pre_request;
00420         break;
00421         
00422     case MODE_BSTEP_OBJECT_LOOKUP:
00423         if( access_methods->object_lookup )
00424             method = access_methods->object_lookup;
00425         break;
00426 
00427     case SNMP_MSG_GET:
00428     case SNMP_MSG_GETNEXT:
00429         if( access_methods->get_values )
00430             method = access_methods->get_values;
00431         break;
00432         
00433 #ifndef NETSNMP_NO_WRITE_SUPPORT
00434     case MODE_BSTEP_CHECK_VALUE:
00435         if( access_methods->object_syntax_checks )
00436             method = access_methods->object_syntax_checks;
00437         break;
00438 
00439     case MODE_BSTEP_ROW_CREATE:
00440         if( access_methods->row_creation )
00441             method = access_methods->row_creation;
00442         break;
00443 
00444     case MODE_BSTEP_UNDO_SETUP:
00445         if( access_methods->undo_setup )
00446             method = access_methods->undo_setup;
00447         break;
00448 
00449     case MODE_BSTEP_SET_VALUE:
00450         if( access_methods->set_values )
00451             method = access_methods->set_values;
00452         break;
00453 
00454     case MODE_BSTEP_CHECK_CONSISTENCY:
00455         if( access_methods->consistency_checks )
00456             method = access_methods->consistency_checks;
00457         break;
00458 
00459     case MODE_BSTEP_UNDO_SET:
00460         if( access_methods->undo_sets )
00461             method = access_methods->undo_sets;
00462         break;
00463 
00464     case MODE_BSTEP_COMMIT:
00465         if( access_methods->commit )
00466             method = access_methods->commit;
00467         break;
00468 
00469     case MODE_BSTEP_UNDO_COMMIT:
00470         if( access_methods->undo_commit )
00471             method = access_methods->undo_commit;
00472         break;
00473 
00474     case MODE_BSTEP_IRREVERSIBLE_COMMIT:
00475         if( access_methods->irreversible_commit )
00476             method = access_methods->irreversible_commit;
00477         break;
00478 
00479     case MODE_BSTEP_UNDO_CLEANUP:
00480         if( access_methods->undo_cleanup )
00481             method = access_methods->undo_cleanup;
00482         break;
00483 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00484         
00485     case MODE_BSTEP_POST_REQUEST:
00486         if( access_methods->post_request )
00487             method = access_methods->post_request;
00488         break;
00489 
00490     default:
00491         snmp_log(LOG_ERR,"unknown mode %d\n", reqinfo->mode);
00492         return SNMP_ERR_GENERR;
00493     }
00494 
00495     /*
00496      * if method exists, set up handler void and call method.
00497      */
00498     if(NULL != method) {
00499         temp_void = handler->myvoid;
00500         handler->myvoid = access_methods->my_access_void;
00501         rc = (*method)(handler, reginfo, reqinfo, requests);
00502         handler->myvoid = temp_void;
00503     }
00504     else {
00505         rc = SNMP_ERR_GENERR;
00506         snmp_log(LOG_ERR,"baby steps multiplexer handler called for a mode "
00507                  "with no handler\n");
00508         netsnmp_assert(NULL != method);
00509     }
00510 
00511     /*
00512      * don't call any lower handlers, it will be done for us 
00513      * since we set MIB_HANDLER_AUTO_NEXT
00514      */
00515 
00516     return rc;
00517 }
00518 
00519 /*
00520  * give a baby step mode, return the flag for that mode
00521  */
00522 int
00523 netsnmp_baby_step_mode2flag( u_int mode )
00524 {
00525     switch( mode ) {
00526         case MODE_BSTEP_OBJECT_LOOKUP:
00527             return BABY_STEP_OBJECT_LOOKUP;
00528 #ifndef NETSNMP_NO_WRITE_SUPPORT
00529         case MODE_BSTEP_SET_VALUE:
00530             return BABY_STEP_SET_VALUE;
00531         case MODE_BSTEP_IRREVERSIBLE_COMMIT:
00532             return BABY_STEP_IRREVERSIBLE_COMMIT;
00533         case MODE_BSTEP_CHECK_VALUE:
00534             return BABY_STEP_CHECK_VALUE;
00535         case MODE_BSTEP_PRE_REQUEST:
00536             return BABY_STEP_PRE_REQUEST;
00537         case MODE_BSTEP_POST_REQUEST:
00538             return BABY_STEP_POST_REQUEST;
00539         case MODE_BSTEP_UNDO_SETUP:
00540             return BABY_STEP_UNDO_SETUP;
00541         case MODE_BSTEP_UNDO_CLEANUP:
00542             return BABY_STEP_UNDO_CLEANUP;
00543         case MODE_BSTEP_UNDO_SET:
00544             return BABY_STEP_UNDO_SET;
00545         case MODE_BSTEP_ROW_CREATE:
00546             return BABY_STEP_ROW_CREATE;
00547         case MODE_BSTEP_CHECK_CONSISTENCY:
00548             return BABY_STEP_CHECK_CONSISTENCY;
00549         case MODE_BSTEP_COMMIT:
00550             return BABY_STEP_COMMIT;
00551         case MODE_BSTEP_UNDO_COMMIT:
00552             return BABY_STEP_UNDO_COMMIT;
00553 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00554         default:
00555             netsnmp_assert("unknown flag");
00556             break;
00557     }
00558     return 0;
00559 }
00562 #else  /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
00563 netsnmp_feature_unused(baby_steps);
00564 #endif /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
00565