| ############################################################# -*- c -*- |
| ## generic include for XXX. Do not use directly. |
| ## |
| ## $Id$ |
| ######################################################################## |
| ## |
| @eval $mfd_aue_wrap_param = "wrap_ctx"@ |
| @eval $mfd_aue_wrap_param_type = "${context}_interface_ctx *"@ |
| @eval $mfd_aue_wrap_param_decl = "$mfd_aue_wrap_param_type $mfd_aue_wrap_param"@ |
| ## |
| @eval $mfd_aue_param = "${context}_reg"@ |
| @eval $mfd_aue_param_type = "${context}_registration *"@ |
| @eval $mfd_aue_param_decl = "$mfd_aue_param_type $mfd_aue_param"@ |
| @eval $mfd_aue_param_cmt = "$mfd_aue_param Pointer to a $mfd_aue_param_type" |
| ## |
| @if $m2c_mark_boundary == 1@ |
| /** START code generated by $RCSfile$ $Revision$ */ |
| @end@ |
| ##//#################################################################### |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| @if $m2c_processing_type eq 'h'@ |
| ## |
| @if $m2c_include_examples == 1@ |
| $example_start |
| /* ********************************************************************* |
| * Since we have no idea how you really access your data, we'll go with |
| * a worst case example: a flat text file. |
| @ if $m2c_data_transient != 2@ |
| @ print Example code is for fully transient data. Either turn off@ |
| @ print m2c_include_examples or set m2c_data_transient to 2.@ |
| @ exit@ |
| @ end@ |
| */ |
| #define MAX_LINE_SIZE 256 |
| $example_end |
| |
| @end@ |
| /** |
| * loop context |
| * |
| * ToDo: |
| * define loop context structure |
| * |
| * Since the actual loop is in the MFD handler, a loop contex parameter |
| * is provided to help you keep track of where you are in between calls |
| * to functions that you wrote and the master MFD handler calls. The |
| * structure of this context is user defineable, and is defined in the |
| * file ${table}_data_access.h. |
| * |
| * E.G., if your data is stored in a linked list, the obvious thing you |
| * want to know from one function call to the next is your current |
| * position in the linked list. Thus the easiest context to use is a |
| * pointer within the linked list. For an array, the current index to |
| * that array would be easiest. |
| * |
| * The funtion calls are actually passed a reference to the loop |
| * context, to allow the loop context to be allocated memory. Here are |
| * some simple examples definitions for various data formats. These |
| * definitions are used in examples later on. |
| * |
| */ |
| typedef struct ${context}_loop_context_s { |
| /* |
| * temporary context used during iteration |
| */ |
| ${context}_rowreq_ctx *rowreq_ctx; |
| @if $m2c_include_examples == 1@ |
| |
| /* |
| * this example code is based on a data source that is a |
| * text file to be read and parsed. |
| */ |
| FILE *filep; |
| char line[MAX_LINE_SIZE]; |
| @end@ |
| } ${context}_loop_context; |
| |
| /* |
| * define a reference to the loop context |
| * |
| * NOTE: DO NOT ADD ITEMS TO THIS STRUCTURE! |
| */ |
| typedef struct ${context}_ref_loop_ctx_s { |
| ${context}_loop_context *loop_ctx; |
| } ${context}_ref_loop_ctx; |
| |
| int ${context}_loop_get_first( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref); |
| int ${context}_loop_get_next( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref); |
| int ${context}_loop_get_data( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref); |
| int ${context}_loop_save_position($mfd_aue_param_decl, |
| ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_loop_ctx *save_loop_ctx_ref, int reuse); |
| int ${context}_loop_cleanup_context( $mfd_aue_param_decl, ${context}_ref_loop_ctx *ref); |
| |
| ## |
| @end@ // m2c_processing_type eq 'h' |
| ######################################################################## |
| ##//#################################################################### |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| @if $m2c_processing_type eq 'i'@ |
| /** |
| * @internal |
| * wrapper around clean up a loop reference |
| */ |
| static int |
| _${context}_loop_cleanup_context( $mfd_aue_wrap_param_decl, |
| ${context}_ref_loop_ctx *ref) |
| { |
| DEBUGMSGTL(("internal:${context}:_${context}_loop_cleanup_context","called\n")); |
| |
| return ${context}_loop_cleanup_context($mfd_aue_wrap_param->user_ctx, ref); |
| } /* _${context}_loop_cleanup_context */ |
| |
| /** |
| * @internal |
| * wrapper around save position |
| */ |
| static int |
| _${context}_loop_save_position( $mfd_aue_wrap_param_decl, ${context}_ref_loop_ctx *ref, |
| ${context}_ref_loop_ctx *ref_copy, int reuse) |
| { |
| DEBUGMSGTL(("internal:${context}:_${context}_loop_save_position","called\n")); |
| |
| return ${context}_loop_save_position($mfd_aue_wrap_param->user_ctx, ref, |
| ref_copy, reuse); |
| } /* _${context}_loop_save_position */ |
| |
| /** |
| * @internal |
| * wrapper around user get_first to setup the index oid |
| */ |
| static int |
| _${context}_loop_get_first_wrapper($mfd_aue_wrap_param_decl, |
| ${context}_ref_loop_ctx * loop_ctx_ref, |
| ${context}_ref_rowreq_ctx * rowreq_ctx_ref) |
| { |
| int rc; |
| DEBUGMSGTL(("internal:${context}:_${context}_loop_get_first_wrapper","called\n")); |
| |
| rc = ${context}_loop_get_first($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, |
| rowreq_ctx_ref); |
| /* |
| * convert index to OID |
| */ |
| if(SNMPERR_SUCCESS == rc ) { |
| netsnmp_assert((NULL != rowreq_ctx_ref) && |
| (rowreq_ctx_ref->rowreq_ctx->oid_idx.oids == rowreq_ctx_ref->rowreq_ctx->oid_tmp)); |
| rowreq_ctx_ref->rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp); |
| rc = ${context}_index_to_oid(&rowreq_ctx_ref->rowreq_ctx->oid_idx, |
| &rowreq_ctx_ref->rowreq_ctx->tbl_idx); |
| netsnmp_assert(rowreq_ctx_ref->rowreq_ctx->oid_idx.len != |
| sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp)); |
| } |
| |
| return rc; |
| } /* _${context}_loop_get_first_wrapper */ |
| |
| /** |
| * @internal |
| * wrapper around user get_next to setup the index oid |
| */ |
| static int |
| _${context}_loop_get_next_wrapper($mfd_aue_wrap_param_decl, |
| ${context}_ref_loop_ctx * loop_ctx_ref, |
| ${context}_ref_rowreq_ctx * rowreq_ctx_ref) |
| { |
| int rc; |
| DEBUGMSGTL(("internal:${context}:_${context}_loop_get_next_wrapper","called\n")); |
| |
| rc = ${context}_loop_get_next($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, |
| rowreq_ctx_ref); |
| /* |
| * convert index to OID |
| */ |
| if(SNMPERR_SUCCESS == rc ) { |
| netsnmp_assert((NULL != rowreq_ctx_ref) && |
| (rowreq_ctx_ref->rowreq_ctx->oid_idx.oids == rowreq_ctx_ref->rowreq_ctx->oid_tmp)); |
| rowreq_ctx_ref->rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp); |
| rc = ${context}_index_to_oid(&rowreq_ctx_ref->rowreq_ctx->oid_idx, |
| &rowreq_ctx_ref->rowreq_ctx->tbl_idx); |
| netsnmp_assert(rowreq_ctx_ref->rowreq_ctx->oid_idx.len != |
| sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp)); |
| } |
| |
| return rc; |
| } /* _${context}_loop_get_next_wrapper */ |
| |
| @if $m2c_data_transient != 0@ # |
| /** |
| * @internal |
| * get data wrapper to allocate context for the user |
| */ |
| static int |
| _${context}_loop_get_data_wrapper($mfd_aue_wrap_param_decl, |
| ${context}_ref_loop_ctx * loop_ctx_ref, |
| ${context}_ref_rowreq_ctx * rowreq_ctx_ref) |
| { |
| // ${context}_rowreq_ctx *orig_ctx = rowreq_ctx_ref->rowreq_ctx; |
| |
| DEBUGMSGTL(("internal:${context}:_${context}_loop_get_data_wrapper","called\n")); |
| |
| return ${context}_loop_get_data($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, rowreq_ctx_ref); |
| } /* _${context}_loop_get_data_wrapper */ |
| |
| @end@ // transient != 0 |
| /** |
| * @internal |
| * initialize the iterator container with functions or wrappers |
| */ |
| void |
| _${context}_container_init(${context}_interface_ctx *if_ctx) |
| { |
| DEBUGMSGTL(("internal:${context}:_${context}_container_init","called\n")); |
| |
| if_ctx->container = netsnmp_container_iterator_get(/** registration */ |
| if_ctx, |
| /** compare */ |
| NULL, |
| /** get_first */ |
| (Netsnmp_Iterator_Loop_Key*)_${context}_loop_get_first_wrapper, |
| /** get_next */ |
| (Netsnmp_Iterator_Loop_Key*)_${context}_loop_get_next_wrapper, |
| /** get_data */ |
| @if $m2c_data_transient != 0@ # |
| (Netsnmp_Iterator_Loop_Data*)_${context}_loop_get_data_wrapper, |
| @else@ |
| NULL, |
| @end@ |
| /** save_pos */ |
| (Netsnmp_Iterator_Ctx_Dup*)_${context}_loop_save_position, |
| /** init_context */ |
| (Netsnmp_Iterator_Ctx*)NULL, |
| /** cleanup_context */ |
| (Netsnmp_Iterator_Ctx*)_${context}_loop_cleanup_context, |
| /** free_user_ctx */ |
| NULL, |
| /** sorted */ |
| 0); |
| } /* _${context}_container_init */ |
| |
| ## |
| @end@ // m2c_processing_type eq 'i' |
| ######################################################################## |
| ##//#################################################################### |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| @if $m2c_processing_type eq 'c'@ |
| /** |
| * unsorted-external overview |
| * |
| * The unsorted external data access code works by calling a few simple |
| * functions to get the index value for each row. Once the agent determines |
| * which row is needed to process an incoming request, another function |
| * is called to retrieve the data for that row. |
| * |
| * A simplified version of the pseudo-code looks like this: |
| * |
| * ${context}_loop_get_first(loop,data) |
| * while( no_error ) { |
| * if( best_match(data, key) |
| * ${context}_loop_save_position(loop,pos); |
| * ${context}_loop_get_next(loop,data) |
| * } |
| * ${context}_loop_get_data(pos,data) |
| * ${context}_loop_cleanup_context(loop) |
| */ |
| |
| /*********************************************************************** |
| * |
| * ITERATION |
| * |
| ***********************************************************************/ |
| |
| /** |
| * get the first data index |
| * |
| * Summary |
| * ------- |
| * This function is called to initialize the iterator loop context for a |
| * new iteration loop and return the index(es) for the first |
| * ${context}_data in the data set. |
| * |
| * Note that during the loop, the only important thing is the indexes. |
| * If access to your data is cheap/fast (e.g. you have a pointer to a |
| * structure in memory), it would make sense to update the data here. |
| * If, however, the accessing the data invovles more work (e.g. parsing |
| * some other existing data, or peforming calculations to derive the data), |
| * then you should limit yourself to setting the indexes. Extracting the |
| * can be put off until the desired row is found. See the notes on |
| * ${context}_loop_get_data(). |
| * |
| * Note that this function does not correspond to a SNMP GET pdu, and |
| * you should return data items in whatever order they are already in. |
| * (In fact, if your data is already ordered in the same order as the |
| * SNMP indexes, you shouldn't be using the unsorted-access code). |
| * |
| * This function should update the table index (rowreq_ctx_ref->rowreq_ctx->tbl_idx) |
| * values for the raw data (rowreq_ctx_ref->rowreq_ctx->data). |
| * |
| * More Details |
| * ------------ |
| * If there is currently no data available, return MFD_END_OF_DATA. |
| * Otherwise, you should set rowreq_ctx_ref->rowreq_ctx and its indexes. |
| * |
| * rowreq_ctx_ref->rowreq_ctx will be NULL. You should allocate a new context |
| * for this loop. [Alternatively, you could allocate one in |
| * ${context}_loop_init_context, save it in your |
| * ${context}_ref_loop_ctx, and use it here.] |
| * |
| * Once you have your context pointer, you should set the index (or indexes) |
| * in rowreq_ctx_ref->rowreq_ctx->tbl_idx to the appropriate value for this row. [If you |
| * use your loop_ctx_ref cleverly, you might be able to put this work in |
| * ${context}_loop_get_next, and simply call that function.] |
| * |
| * @param $mfd_aue_param_cmt |
| * @param loop_ctx_ref Pointer to your loop reference. |
| * @param rowreq_ctx_ref Pointer to a context reference. |
| * |
| * @retval MFD_SUCCESS : success. |
| * @retval MFD_END_OF_DATA : no data available |
| * @retval MFD_ERROR : error. |
| */ |
| int |
| ${context}_loop_get_first( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref) |
| { |
| DEBUGMSGTL(("verbose:${context}:${context}_loop_get_first","called\n")); |
| |
| netsnmp_assert(rowreq_ctx_ref); |
| netsnmp_assert(loop_ctx_ref); |
| |
| /* |
| * allocate memory for new structure |
| */ |
| loop_ctx_ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); |
| if(NULL == loop_ctx_ref->loop_ctx) |
| return MFD_ERROR; |
| |
| /* |
| * allocate a temporary context to use during iteration |
| */ |
| @ eval $m2c_tmp = ""@ |
| @ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ |
| @ eval $m2c_tmp = "NULL"@ |
| @ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ |
| @ eval $m2c_tmp = "$m2c_tmp, NULL"@ |
| @ @end@ |
| @ end@ |
| loop_ctx_ref->loop_ctx->rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); |
| if(NULL == loop_ctx_ref->loop_ctx->rowreq_ctx) { |
| SNMP_FREE(loop_ctx_ref->loop_ctx); |
| return MFD_RESOURCE_UNAVAILABLE; |
| } |
| |
| /* |
| * ToDo: |
| * set up loop context |
| */ |
| @if $m2c_include_examples == 1@ |
| $example_start |
| /* |
| * open our data file. |
| */ |
| loop_ctx_ref->loop_ctx->filep = fopen("/etc/dummy.conf", "r"); |
| if(NULL == loop_ctx_ref->loop_ctx->filep) { |
| return MFD_RESOURCE_UNAVAILABLE; |
| } |
| |
| $example_end |
| @end@ |
| |
| @ifconf ${table}_update_idx.m2i@ |
| @ include ${table}_update_idx.m2i@ |
| @else@ |
| @ if $m2c_include_examples == 1@ |
| $example_start |
| /* |
| * in this example, after opening the file, get next does the same thing |
| * as get first, we let's just call get next... |
| */ |
| return ${context}_loop_get_next($mfd_aue_param, loop_ctx_ref, rowreq_ctx_ref); |
| $example_end |
| @ else@ |
| /* |
| * we just need the index for now. Reuse the one in the loop context's |
| * temporary row request context. (rowreq_ctx_ref->rowreq_ctx->tbl_idx) |
| */ |
| rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; |
| |
| /* |
| * ToDo: |
| * set local vars for index from loop_ctx_ref->loop_ctx |
| * this can be done in one of two ways: |
| */ |
| |
| /* |
| * 1) individually |
| */ |
| @ foreach $node index@ |
| @ include m2c_setup_node.m2i@ |
| /* |
| * ToDo: |
| * set rowreq_ctx_ref->rowreq_ctx->tbl_idx->$node |
| @ if $m2c_node_needlength == 1@ |
| * and rowreq_ctx_ref->tbl_idx->${node}_len |
| @ end@ |
| */ |
| @ end@ #foreach |
| |
| /* |
| * OR |
| */ |
| |
| /* |
| * 2) by calling ${context}_indexes_set() |
| * ${context}_indexes_set(rowreq_ctx_ref->tbl_idx, |
| @ foreach $node index@ |
| @ include m2c_setup_node.m2i@ |
| @ if $m2c_node_needlength == 1@ |
| * ${node}_ptr, ${node}_len |
| @ else@ |
| * $node |
| @ end@ |
| @ end@ # foreach index |
| * ); |
| */ |
| @ end@ # example |
| @end@ #ifconf |
| |
| return MFD_SUCCESS; |
| } /* ${context}_loop_get_first */ |
| |
| /** |
| * get the next data index |
| * |
| * Summary |
| * ------- |
| * This function returns the next data item in the data set. The same |
| * caveat applies here as did above. The indexes are the important parts |
| * during loop processing. |
| * |
| * Note that this function does not correspond to a SNMP GET-NEXT pdu, and |
| * you should return data items in whatever order they are already in. |
| * (In fact, if your data is already ordered in the same order as the |
| * SNMP indexes, you shouldn't be using the unsorted-access code). |
| * |
| * More Details |
| * ------------ |
| * rowreq_ctx_ref->rowreq_ctx will have been set in ${context}_loop_get_first. |
| * |
| * If there is currently no data available, return MFD_END_OF_DATA. |
| * Otherwise, you should set the indexes in rowreq_ctx_ref->rowreq_ctx->tbl_idx. |
| * |
| * You should set the index (or indexes) in rowreq_ctx_ref->rowreq_ctx->tbl_idx to the |
| * appropriate value for this row. |
| * |
| * @param $mfd_aue_param_cmt |
| * @param loop_ctx_ref Pointer to your loop reference. |
| * @param rowreq_ctx_ref Pointer to a context reference. |
| * |
| * @retval MFD_SUCCESS : success. |
| * @retval MFD_END_OF_DATA : no more data available |
| * @retval MFD_ERROR : error. |
| */ |
| int |
| ${context}_loop_get_next( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref) |
| { |
| DEBUGMSGTL(("verbose:${context}:${context}_loop_get_next","called\n")); |
| |
| netsnmp_assert(loop_ctx_ref && loop_ctx_ref->loop_ctx); |
| netsnmp_assert(rowreq_ctx_ref); |
| |
| /* |
| * we just need the index for now. Reuse the one in the loop context's |
| * temporary row request context. (rowreq_ctx_ref->rowreq_ctx->tbl_idx) |
| */ |
| rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; |
| |
| @ if $m2c_include_examples == 1@ |
| $example_start |
| /* |
| * get a line (skip blank lines) |
| */ |
| do { |
| if (!fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), |
| loop_ctx_ref->loop_ctx->filep)) { |
| /* we're done */ |
| fclose(loop_ctx_ref->loop_ctx->filep); |
| loop_ctx_ref->loop_ctx->filep = NULL; |
| } |
| } while (loop_ctx_ref->loop_ctx->filep && (loop_ctx_ref->loop_ctx->line[0] == '\n')); |
| |
| /* |
| * check for end of data |
| */ |
| if(NULL == loop_ctx_ref->loop_ctx->filep) |
| return MFD_END_OF_DATA; |
| |
| /* |
| * ToDo: |
| * set local vars for index from loop_ctx_ref->loop_ctx |
| * this can be done in one of two ways: |
| */ |
| |
| /* |
| * 1) individually |
| */ |
| @ foreach $node index@ |
| @ include m2c_setup_node.m2i@ |
| /* |
| * ToDo: |
| * set rowreq_ctx_ref->rowreq_ctx->tbl_idx->$node |
| @ if $m2c_node_needlength == 1@ |
| * and rowreq_ctx_ref->tbl_idx->${node}_len |
| @ end@ |
| */ |
| @ end@ #foreach |
| |
| /* |
| * OR |
| */ |
| |
| /* |
| * 2) by calling ${context}_indexes_set() |
| * ${context}_indexes_set(rowreq_ctx_ref->tbl_idx, |
| @ foreach $node index@ |
| @ include m2c_setup_node.m2i@ |
| @ if $m2c_node_needlength == 1@ |
| * ${node}_ptr, ${node}_len |
| @ else@ |
| * $node |
| @ end@ |
| @ end@ # foreach index |
| * ); |
| */ |
| $example_end |
| @ end@ # example |
| |
| return MFD_SUCCESS; |
| } /* ${context}_loop_get_next */ |
| |
| /** |
| * duplicate the current loop reference |
| * |
| * Summary |
| * ------- |
| * During loop iteration, the iterator keeps track of the row that |
| * is the current best match. This function is called when the |
| * current row is a better match than any previous row. |
| * |
| * You should save any information you need to be able to locate this row |
| * again from the current loop context to a new loop context. |
| * |
| * At the end of the loop, when the best match has been found, the saved |
| * loop context will be used to get the data for the row by calling |
| * ${context}_loop_get_data(). |
| @if $m2c_data_transient != 0@ # persistent |
| * |
| * Since your data is transient, you need to make a copy of it before |
| * the iterator moves on to the next row. |
| @end@ |
| * |
| @if $m2c_data_transient != 0@ # persistent |
| * More Details |
| * ------------ |
| @ if $m2c_data_transient == 1@ # short term |
| * Since your data is semi-TRANSIENT data, you could just keep a pointer |
| * to the data in the loop reference. The data should then be copied in |
| * ${context}_loop_get_data(). |
| @ else@ # $m2c_data_transient == 2@ # copy immediately |
| * One idea would be to copy it space allocated in the loop reference |
| * structure. Another would be to simply have a pointer in the loop |
| * reference structure, and allocate memory here. |
| * |
| @ end@ |
| @end@ |
| * @param $mfd_aue_param_cmt |
| * @param loop_ctx_ref Reference to current loop context. |
| * @param save_loop_ctx_ref Reference to a loop context for saving the current |
| * position. If reuse is not set or |
| * save_loop_ctx_ref->loop_ctx is NULL, allocate |
| * a new one. If reuse is set, you may reuse the existing |
| * loop_ctx. |
| * @param reuse Indicates if an existing save_loop_ctx_ref->loop_ctx |
| * may be reused. |
| * |
| * @retval MFD_SUCCESS : success. |
| * @retval MFD_ERROR : error. |
| */ |
| int |
| ${context}_loop_save_position($mfd_aue_param_decl, |
| ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_loop_ctx *save_loop_ctx_ref, |
| int reuse) |
| { |
| DEBUGMSGTL(("verbose:${context}:${context}_loop_save_position","called\n")); |
| |
| netsnmp_assert(loop_ctx_ref && save_loop_ctx_ref); |
| |
| /* |
| * ToDo: |
| * 1) allocate new loop context, unless you can reuse a previous pointer. |
| * 2) save information for the position of loop_ctx_ref in save_loop_ctx_ref. |
| */ |
| if((0 == reuse) || (NULL == save_loop_ctx_ref->loop_ctx)) |
| save_loop_ctx_ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); |
| if(NULL == save_loop_ctx_ref->loop_ctx) { |
| snmp_log(LOG_ERR, "could not allocate memory\n"); |
| return MFD_ERROR; |
| } |
| |
| /* |
| * if you can reuse a previously saved contex, just swap |
| * it out with the loop iterator |
| */ |
| if(reuse && save_loop_ctx_ref->loop_ctx->rowreq_ctx) { |
| ${context}_rowreq_ctx * tmp_rowreq_ctx = save_loop_ctx_ref->loop_ctx->rowreq_ctx; |
| save_loop_ctx_ref->loop_ctx->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; |
| loop_ctx_ref->loop_ctx->rowreq_ctx = tmp_rowreq_ctx; |
| } |
| else { |
| /* |
| * take the current pointer |
| */ |
| save_loop_ctx_ref->loop_ctx->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; |
| |
| /* |
| * allocate a new context to replace the one you just took. |
| */ |
| @ eval $m2c_tmp = ""@ |
| @ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ |
| @ eval $m2c_tmp = "NULL"@ |
| @ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ |
| @ eval $m2c_tmp = "$m2c_tmp, NULL"@ |
| @ @end@ |
| @ end@ |
| loop_ctx_ref->loop_ctx->rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); |
| if(NULL == loop_ctx_ref->loop_ctx->rowreq_ctx) { |
| SNMP_FREE(loop_ctx_ref->loop_ctx); |
| return MFD_ERROR; |
| } |
| } |
| |
| @if $m2c_data_transient == 0@ # persistent |
| /** non-TRANSIENT data: no need to copy */ |
| @elsif $m2c_data_transient == 1@ # short term |
| /** semi-TRANSIENT data: will copy data when index found */ |
| /** only need to copy pertinent data from loop context */ |
| @elsif $m2c_data_transient == 2@ # copy immediately |
| /* |
| * TRANSIENT data: copy all the data. |
| */ |
| @end@ |
| @if $m2c_include_examples == 1@ |
| $example_start |
| @ if $m2c_data_transient == 1@ # short term |
| /** save line to do that */ |
| memcpy(save_loop_ctx_ref->loop_ctx->line, loop_ctx_ref->loop_ctx->line, |
| sizeof(loop_ctx_ref->loop_ctx->line)); |
| @ elsif $m2c_data_transient == 2@ # copy immediately |
| @ foreach $node nonindex@ |
| @ include m2c_setup_node.m2i@ |
| /* |
| * ToDo: |
| * set rowreq_ctx_ref->${m2c_data_item}$node |
| * from the loop context |
| */ |
| @ end@ |
| @ end@ |
| $example_end |
| @end@ # example |
| |
| return MFD_SUCCESS; |
| } /* ${context}_loop_save_position */ |
| |
| @if $m2c_data_transient != 0@ # semi-transient |
| /** |
| * set ${context}_data from a data context |
| * |
| * Summary |
| * ------- |
| * At the end of the loop, when the best match has been found, the saved |
| * loop context will be used to get the data for the row by calling |
| * ${context}_loop_get_data(). |
| * |
| * You should return a fully populated row request context in |
| * rowreq_ctx_ref->rowreq_ctx. |
| * |
| * More Details |
| * ------------ |
| * @param $mfd_aue_param_cmt |
| * @param loop_ctx_ref pointer to your loop reference. |
| * @param rowreq_ctx_ref pointer to a context reference. |
| */ |
| int |
| ${context}_loop_get_data( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, |
| ${context}_ref_rowreq_ctx *rowreq_ctx_ref) |
| { |
| DEBUGMSGTL(("verbose:${context}:${context}_loop_get_data","called\n")); |
| |
| netsnmp_assert((NULL != loop_ctx_ref) && (NULL != loop_ctx_ref->loop_ctx)); |
| netsnmp_assert(NULL != rowreq_ctx_ref); |
| netsnmp_assert(NULL != rowreq_ctx_ref->rowreq_ctx); |
| |
| /* |
| * take temporary row request context from loop context |
| */ |
| rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; |
| loop_ctx_ref->loop_ctx->rowreq_ctx = NULL; |
| |
| /* |
| * copy data to the data context (rowreq_ctx_ref->${m2c_data_item}) |
| @ if $m2c_include_examples == 1@ |
| * in loop_save_position, we saved line to do that |
| @ end@ |
| */ |
| @ foreach $node nonindex@ |
| @ include m2c_setup_node.m2i@ |
| /* |
| * $m2c_node_summary |
| */ |
| @ eval $m2c_ctx_lh = "rowreq_ctx_ref->$m2c_ctx_rh"@ |
| @ eval $m2c_ctx_lhs = "rowreq_ctx_ref->$m2c_ctx_rhs"@ |
| @ eval $m2c_ctx_rh = "loop_ctx_ref->loop_ctx->$node"@ |
| @ eval $m2c_ctx_rhs = "loop_ctx_ref->loop_ctx->${node}_len"@ |
| @ include generic-value-map.m2i@ |
| |
| @ end@ |
| |
| return MFD_SUCCESS; |
| } /* ${context}_loop_get_data */ |
| |
| @end@ // if $m2c_data_transient != 0 |
| |
| /** |
| * clean up a loop reference |
| * |
| * Summary |
| * ------- |
| * This function will be called once the loop iteration has completed |
| * to release any memory or resources allocated for the loop context. |
| * |
| * More Details |
| * ------------ |
| * @param $mfd_aue_param_cmt |
| * @param loop_ctx_ref Pointer to your loop reference. |
| * |
| * @retval MFD_SUCCESS : success. |
| * @retval MFD_ERROR : error. |
| */ |
| int |
| ${context}_loop_cleanup_context( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref) |
| { |
| DEBUGMSGTL(("verbose:${context}:${context}_loop_cleanup_context","called\n")); |
| |
| netsnmp_assert(loop_ctx_ref); |
| |
| if(!loop_ctx_ref->loop_ctx) |
| return MFD_ERROR; |
| |
| /* |
| * release the row request context, if it wasn't taken |
| */ |
| if(loop_ctx_ref->loop_ctx->rowreq_ctx) |
| ${context}_release_rowreq_ctx(loop_ctx_ref->loop_ctx->rowreq_ctx); |
| |
| /* |
| * ToDo: |
| * release resources |
| */ |
| @if $m2c_include_examples == 1@ |
| $example_start |
| /* |
| * close file |
| */ |
| if(loop_ctx_ref->loop_ctx->filep) |
| fclose(loop_ctx_ref->loop_ctx->filep); |
| $example_end |
| |
| @end@ |
| /* |
| * free loop context |
| */ |
| free(loop_ctx_ref->loop_ctx); |
| |
| return MFD_SUCCESS; |
| } /* ${context}_loop_cleanup_context */ |
| |
| @end@ // m2c_processing_type eq 'c' |
| ######################################################################## |
| ##//#################################################################### |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| ##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| @if $m2c_processing_type eq 'r'@ |
| ## |
| unsorted-external summary |
| ------------------------- |
| The unsorted-external data access code is for cases when you data is |
| kept UNSORTED and EXTERNAL to the agent/sub-agent. |
| |
| This code was generated based on the following assumptions or settings: |
| |
| 1) The raw data for this table is UNSORTED. |
| @if $mfd_readme_verbose != 0@ |
| |
| UNSORTED data is data that is not kept in the same order as the way |
| SNMP expects the index(es) for the table to be kept. [It could very |
| well be sorted in some other order, but for the purpose of SNMP, the |
| order is incorrect.] If you're not sure if your data is sorted |
| in an SNMP compliant way, its likely not. |
| |
| Because the raw data is unsorted, to satisfy a particular request, the |
| entire data set must be examined to find the apropriate index. This |
| is done via a simple loop. The MFD handler will call your get_first |
| function and the call the get_next function repeatedly, until it |
| returns SNMPERR_NO_VARS. |
| @end@ |
| |
| 2) The raw data for this table is EXTERNAL. |
| @if $mfd_readme_verbose != 0@ |
| |
| EXTERNAL data is data that is owned by some other process, |
| device, file or mechanism. The agent must use some interface to |
| read or modify the data. An external process may modify the data |
| without the agent's knowledge. For example, the Net-SNMP agent |
| implements the interface table (ifTable), which reports on |
| network interfaces. The host operating system owns this data, and |
| Net-SNMP must use system calls to report or manipulate the data. |
| Examples of external data include data stored in kernel space, in |
| files, in another non-memory shared process, and data stored in |
| devices. |
| @end@ |
| |
| 3) The raw data for this table is TRANSIENT. |
| @if $mfd_readme_verbose != 0@ |
| |
| TRANSIENT data is data that may be overwritten by another funtion |
| or process. For example, many OS functions return data in a |
| static buffer that will be reused the next time the function is |
| called. Because of this, we will assume that you will copy the |
| raw data retrieved from these other sources to a generated |
| structure for use within the Net-SNMP agent. (Don't worry, we'll |
| help you) |
| @end@ |
| |
| |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| The unsorted external data access code works by calling a few simple |
| functions to get the index value for each row. Once the agent determines |
| which row is needed to process an incoming request, another function |
| is called to retrieve the data for that row. |
| |
| A simplified version of the pseudo-code looks like this: |
| |
| ${context}_loop_init_context(loop) |
| ${context}_loop_get_first(loop,data) |
| while( no_error ) { |
| if( best_match(data, key) |
| ${context}_loop_save_position(loop,pos); |
| ${context}_loop_get_next(loop,data) |
| } |
| ${context}_loop_get_data(pos,data) |
| ${context}_loop_cleanup_context(loop) |
| ## |
| ## end sync |
| ## |
| |
| We will talk about each individual step below. |
| |
| |
| ######################################################################## |
| Defining context for the loop |
| ----------------------------- |
| ToDo : typedef ${context}_loop_context |
| WHERE: ${table}_data_access.h |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| Since the actual loop is in the MFD handler, a loop contex parameter |
| is provided to help you keep track of where you are in between calls |
| to functions that you wrote and the master MFD handler calls. The |
| structure of this context is user defineable, and is defined in the |
| file ${table}_data_access.h. |
| |
| E.G., if your data is stored in a linked list, the obvious thing you |
| want to know from one function call to the next is your current |
| position in the linked list. Thus the easiest context to use is a |
| pointer within the linked list. For an array, the current index to |
| that array would be easiest. |
| |
| The funtion calls are actually passed a reference to the loop |
| context, to allow the loop context to be allocated memory. Here are |
| some simple examples definitions for various data formats. These |
| definitions are used in examples later on. |
| ## |
| ## end sync |
| ## |
| |
| Linked list |
| ----------- |
| typedef list_node ${context}_loop_context; |
| |
| Array |
| ----- |
| typedef integer ${context}_loop_context; |
| |
| File |
| ---- |
| typedef struct ${context}_loop_context_s { |
| char * file_name; |
| FILE * f; |
| char line[128]; |
| } ${context}_loop_context; |
| |
| @end@ |
| |
| ######################################################################## |
| Initialization |
| -------------- |
| ToDo : Initialization |
| FUNC : ${context}_loop_init_data |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| The ${context}_loop_init_data function will be called during startup to |
| allow for any initialization needed for the data access routines. |
| |
| @end@ |
| |
| ######################################################################## |
| Preparing for the loop |
| ---------------------- |
| ToDo : initialize loop context |
| FUNC : ${context}_loop_init_context |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| This function will be called before the start of a new itertion over |
| the data. The loop context that is initialized here will be passed to |
| ${context}_loop_get_first and ${context}_loop_get_next. |
| |
| Set the loop context variable ref->loop_ctx so that the iteration |
| functions (get_first and get_next) can locate the apropriate data |
| context. |
| ## |
| ## end sync |
| ## |
| |
| The primary purpose of the loop_init_context call is to initialize |
| the loop context data (ref). Here are some simple examples, based on the |
| earlier example loop contexts. |
| |
| Linked list |
| ----------- |
| ref->loop_ctx = my_table_head_ptr; |
| |
| Array |
| ----- |
| /* instead of actually allocating memory, just use the pointer */ |
| /* as an integer */ |
| (integer)(ref->loop_ctx) = 0; |
| |
| File |
| ---- |
| ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); |
| /* error checking here */ |
| ref->loop_ctx->file_name = (char*) reg->mfd_user_ctx; |
| ref->loop_ctx->f = fopen( ref->loop_ctx->file_name, "r+" ); |
| |
| @end@ |
| |
| ######################################################################## |
| The Loop |
| -------- |
| ToDo : return raw data |
| FUNC : ${context}_loop_get_first |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| This function is called to return set the index(es) for the first |
| ${context}_data in the data set. |
| |
| Note that during the loop, the only important thing is the indexes. |
| If access to your data is cheap/fast (e.g. you have a pointer to a |
| structure in memory), it would make sense to update the data here. |
| If, however, the accessing the data invovles more work (e.g. parsing |
| some other existing data, or peforming calculations to derive the data), |
| then you should limit yourslef to setting the indexes. Extracting the |
| can be put off until the desired row is found See the notes on |
| ${context}_loop_get_data(). |
| |
| Note that this function does not correspond to a SNMP GET pdu, and |
| you should return data items in whatever order they are already in. |
| (In fact, if your data is already ordered in the same order as the |
| SNMP indexes, you shouldn't be using the unsorted-access code). |
| |
| This function should update the table index (rowreq_ctx_ref->rowreq_ctx->tbl_idx) |
| values for the raw data (rowreq_ctx_ref->rowreq_ctx->data). |
| ## |
| ## end sync |
| ## |
| |
| Linked list |
| ----------- |
| rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx; |
| |
| Array |
| ----- |
| /* assuming registration has array of pointers */ |
| rowreq_ctx_ref->rowreq_ctx->data = reg->mfd_user_ctx[(integer)(ref->loop_ctx)]; |
| |
| File |
| ---- |
| fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), |
| loop_ctx_ref->loop_ctx->f); |
| rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx->line; |
| |
| @end@ |
| |
| ToDo : return raw data |
| FUNC : ${context}_loop_get_next |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| This function returns the next data item in the data set. The same |
| caveat applies here as did above. The indexes are the important parts |
| during loop processing. |
| |
| Note that this function does not correspond to a SNMP GET-NEXT pdu, and |
| you should return data items in whatever order they are already in. |
| (In fact, if your data is already ordered in the same order as the |
| SNMP indexes, you shouldn't be using the unsorted-access code). |
| ## |
| ## end sync |
| ## |
| |
| Linked list |
| ----------- |
| loop_ctx_ref->loop_ctx = loop_ctx_ref->loop_ctx->next; |
| rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx; |
| |
| Array |
| ----- |
| ++((integer)(ref->loop_ctx)); |
| /* assuming registration has array of pointers */ |
| rowreq_ctx_ref->rowreq_ctx->data = reg->mfd_user_ctx[(integer)(ref->loop_ctx)]; |
| |
| File |
| ---- |
| fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), |
| loop_ctx_ref->loop_ctx->f); |
| rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx->line; |
| |
| @end@ |
| |
| ######################################################################## |
| Updating the Index |
| ------------------ |
| ToDo : update index for the raw data |
| FUNC : ${context}_indexes_set |
| WHERE: ${table}_data_access.c |
| |
| This is a convenience function for setting the index context from |
| the native C data. Where necessary, value mapping should be done. |
| |
| @if $mfd_readme_verbose == 1@ |
| This function should update the table index values (found in |
| tbl_idx) for the given raw data. |
| |
| @end@ |
| |
| ######################################################################## |
| Saving a position in the loop |
| ----------------------------- |
| ToDo : Saving a position in the loop |
| FUNC : ${context}_loop_save_position |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| During loop iteration, the iterator keeps track of the row that |
| is the current best match. This function is called when the |
| current row is a better match than any previous row. |
| |
| You should save any information you need to be able to locate this row |
| again from the current loop context to a new loop context. |
| |
| At the end of the loop, when the best match has been found, the saved |
| loop context will be used to get the data for the row by calling |
| ${context}_loop_get_data(). |
| @if $m2c_data_transient != 0@ # persistent |
| |
| Since your data is transient, you need to make a copy of it before |
| the iterator moves on to the next row. |
| @end@ |
| ## |
| ## end sync |
| ## |
| |
| @end@ |
| |
| ######################################################################## |
| Returning Data For an Index |
| --------------------------- |
| ToDo : copy transient raw data to generated structure |
| FUNC : ${context}_loop_get_data |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| At the end of the loop, when the best match has been found, the saved |
| loop context will be used to get the data for the row by calling |
| ${context}_loop_get_data(). |
| ## |
| ## end sync |
| ## |
| |
| @end@ |
| |
| ######################################################################## |
| Cleaning up after the loop |
| -------------------------- |
| ToDo : release any allocated memory |
| FUNC : ${context}_loop_cleanup_context |
| WHERE: ${table}_data_access.c |
| |
| @if $mfd_readme_verbose != 0@ |
| ## |
| ## this should be syncronized with master version of comments in |
| ## mfd-access-unsorted-external-body.m2i You should be able to copy |
| ## the comments here and replace " * " with " ". |
| ## |
| This function will be called once the loop iteration has completed |
| to release any memory allocated for loop reference. |
| ## |
| ## end sync |
| ## |
| The purpose of the loop_cleanup_context call is to release any memory |
| allocated for the loop context data. Here are some simple examples, based |
| on the earlier example loop contexts. |
| |
| Linked list |
| ----------- |
| /* nothing to do */ |
| |
| Array |
| ----- |
| /* nothing to do */ |
| |
| File |
| ---- |
| free(ref->loop_ctx); |
| |
| @end@ |
| |
| ## |
| @end@ // m2c_processing_type eq 'r |
| ######################################################################## |
| @if $m2c_mark_boundary == 1@ |
| /** END code generated by $RCSfile$ $Revision$ */ |
| @end@ |