| ######################################################################## |
| ## generic include for XXX. Do not use directly. |
| ## $Id$ |
| ######################################################################## |
| @if $m2c_mark_boundary == 1@ |
| /** START code generated by $RCSfile$ $Revision$ */ |
| @end@ |
| ######################################################################## |
| @if $m2c_processing_type eq 'h'@ |
| /* ********************************************************************* |
| * Persistent declarations |
| */ |
| /* |
| * persistence |
| */ |
| #define LINE_TERM_CHAR '$' |
| |
| void ${context}_container_init_persistence( netsnmp_container * container ); |
| int ${context}_container_should_save(${context}_rowreq_ctx * rowreq_ctx); |
| |
| @end@ // m2c_processing_type eq 'h' |
| ###################################################################### |
| ###################################################################### |
| ###################################################################### |
| @if $m2c_processing_type eq 'c'@ |
| /************************************************************ |
| * the *_should_save routine is called to determine if a row |
| * should be stored persistently. |
| * |
| * Note that this is not a 'dirty' check (i.e. if a row has changed), |
| * but a check for volatile rows that should not be saved between |
| * restarts. |
| * |
| * return 1 if the row should be stored |
| * return 0 if the row should not be stored |
| */ |
| int |
| ${context}_container_should_save(${context}_rowreq_ctx * rowreq_ctx) |
| { |
| @ foreach $node column@ |
| @ if "$node.syntax" eq "StorageType"@ |
| @ include m2c_setup_node.m2i@ |
| if (SNMP_STORAGE_VOLATILE == $m2c_ctx_rh ) |
| return 0; |
| @ end@ |
| @ end@ |
| |
| return 1; /* save the row */ |
| } |
| |
| @end@ // m2c_processing_type eq 'h' |
| ###################################################################### |
| ###################################################################### |
| ###################################################################### |
| @if $m2c_processing_type eq 'i'@ |
| /*********************************************************************** |
| * |
| * PERSISTENCE |
| * |
| ***********************************************************************/ |
| |
| static int _${context}_container_save_rows(int majorID, int minorID, void *serverarg, void *clientarg); |
| static void _${context}_container_row_restore(const char *token, char *buf); |
| static int _${context}_container_row_save( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| void *type); |
| static char * _${context}_container_col_restore( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| u_int col, char* buf); |
| static char * _${context}_container_col_save( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| u_int col, char* buf); |
| |
| static char row_token[] = "${context}"; |
| |
| /************************************************************ |
| * *_init_persistence should be called from the main table |
| * init routine. |
| * |
| * If your table depends on rows in another table, |
| * you should register your callback after the other table, |
| * which should ensure the rows on which you depend are saved |
| * (and re-created) before the dependent rows. |
| */ |
| void |
| ${context}_container_init_persistence( netsnmp_container * container ) |
| { |
| int rc; |
| |
| register_config_handler(NULL, row_token, |
| _${context}_container_row_restore, NULL, NULL); |
| rc = snmp_register_callback( SNMP_CALLBACK_LIBRARY, |
| SNMP_CALLBACK_STORE_DATA, |
| _${context}_container_save_rows, |
| container); |
| |
| if( rc != SNMP_ERR_NOERROR ) |
| snmp_log(LOG_ERR, "error registering for STORE_DATA callback " |
| "in _${context}_container_init_persistence\n"); |
| } |
| |
| static int |
| _${context}_container_save_rows(int majorID, int minorID, void *serverarg, void *clientarg) |
| { |
| char sep[] = |
| "##############################################################"; |
| char buf[] = |
| "#\n" |
| "# $context persistent data\n" |
| "#"; |
| char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE); |
| |
| read_config_store((char*)type, sep); |
| read_config_store((char*)type, buf); |
| |
| /* |
| * save all rows |
| */ |
| CONTAINER_FOR_EACH((netsnmp_container*)clientarg, |
| (netsnmp_container_obj_func*)_${context}_container_row_save, |
| type); |
| |
| read_config_store((char*)type, sep); |
| read_config_store((char*)type, "\n"); |
| |
| /* |
| * never fails |
| */ |
| return SNMPERR_SUCCESS; |
| } |
| |
| |
| |
| /************************************************************ |
| * _${context}_container_row_save |
| */ |
| static int |
| _${context}_container_row_save( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| void *type) |
| { |
| /* |
| * Allocate space for a line with all data for a row. An |
| * attempt is made to come up with a default maximum size, but |
| * there is no guarantee it will be enough. It probably will be, |
| * unless you are dealing with large values or you have external |
| * indexes. |
| * |
| * 1) allocate space for each column. Comment out columns you don't |
| * intend to save. You may also need to add room for any non- |
| * column data you want to store. Remeber, data will be stored in |
| * ASCII form, so you need to allow for that. Here are some |
| * general guidelines: |
| * |
| * Object ID : 12 * len [ASCII len of max int + 1 for .] |
| * Octet String: (2 * len) + 2 [2 ASCII chars per byte + "0x"] |
| * Integers : 12 [ASCII len for smallest negative number] |
| * |
| * 2) You also need to allocate space for the row index. This will |
| * be stored as an OID, which means that Octet Strings need to |
| * be treated a little differently. Specifically, you will need |
| * (4 * len) + 4 [3 ASCII chars per byte + 1 for ., + 4 for len]. |
| * |
| * 3) Also, remeber to add space for the identifier and seperator |
| * characters (for example, each column is prefixed by the |
| * column number and a semicolon. To allow for the maximum |
| * column values, 12 bytes [11 for oid + 1 for ':'] per |
| * column are added). |
| */ |
| /** xxx: add storage for external index(s)! */ |
| #define MAX_ROW_SIZE (sizeof(row_token) + 1 + \ |
| @ if $ext_index != 0@ |
| ( 12 * 128 ) + /* external interfaces - max 128 subids */ \ |
| @ end@ |
| @ foreach $node nonindex@ |
| @ include m2c_setup_node.m2i@ |
| @ if ($node.settable == 1)@ |
| @ if "$node.type" eq "ASN_OBJECT_ID"@ |
| ( ( 12 * sizeof(${m2c_ctx_rh}) ) + 3 ) + /* $node.type */ \ |
| @ elsif "$node.type" eq "ASN_OCTET_STR"@ |
| ( ( 2 * sizeof(${m2c_ctx_rh}) ) + 3 ) + /* $node.type */ \ |
| @ else@ |
| ( 12 ) + /* $node.type $node */ \ |
| @ end@ |
| @ end@ |
| @ end@ |
| ( $table.uc_MAX_COL * 12 ) + /* column num prefix + : */ \ |
| 2 /* LINE_TERM_CHAR + \n */ ) |
| |
| char buf[MAX_ROW_SIZE], *pos = buf, *max = &buf[MAX_ROW_SIZE-1]; |
| char *tmp; |
| int i; |
| |
| if (${context}_container_should_save(rowreq_ctx) == 0) { |
| return SNMP_ERR_NOERROR; |
| } |
| |
| /* |
| * build the line |
| */ |
| pos += sprintf(pos, "%s ", row_token); |
| pos = read_config_save_objid(pos, rowreq_ctx->oid_idx.oids, |
| rowreq_ctx->oid_idx.len); |
| if(NULL == pos) { |
| snmp_log(LOG_ERR,"error saving ${context} row " |
| "to persistent file\n"); |
| return SNMP_ERR_GENERR; |
| } |
| *pos++ = ' '; |
| if(pos > max) { |
| snmp_log(LOG_ERR,"error saving ${context} row " |
| "to persistent file (too long)\n"); |
| return SNMP_ERR_GENERR; |
| } |
| |
| /* |
| * add each column |
| */ |
| for(i = $table.uc_MIN_COL; i <= $table.uc_MAX_COL; ++i ) { |
| |
| if ((0x1 << (i-1)) & ~$context.uc_SETTABLE_COLS) |
| continue; |
| |
| tmp = pos; |
| pos = _${context}_container_col_save(rowreq_ctx, i, pos); |
| if(NULL == pos) |
| pos = tmp; |
| else |
| *pos++ = ' '; |
| if(pos > max) { |
| snmp_log(LOG_ERR,"error saving ${context} row " |
| "to persistent file (too long)\n"); |
| return SNMP_ERR_GENERR; |
| } |
| } |
| |
| /* |
| * if you have non-column data, add it here |
| */ |
| |
| |
| /* |
| * store the line |
| */ |
| pos += sprintf(pos, "%c", LINE_TERM_CHAR); |
| if(pos > max) { |
| snmp_log(LOG_ERR,"error saving ${context} row " |
| "to persistent file (too long)\n"); |
| return SNMP_ERR_GENERR; |
| } |
| read_config_store((char*)type, buf); |
| |
| DEBUGMSGTL(("internal:${context}:_${context}_container_row_save", |
| "saving line '%s'\n", buf)); |
| |
| return SNMP_ERR_NOERROR; |
| } |
| |
| static void |
| _${context}_container_row_restore(const char *token, char *buf) |
| { |
| ${context}_rowreq_ctx * rowreq_ctx; |
| netsnmp_index index; |
| oid tmp_oid[ MAX_${context}_IDX_LEN]; |
| u_int col = 0, found = 0; |
| |
| |
| if (strncmp(token, row_token, sizeof(row_token)) != 0) { |
| snmp_log(LOG_ERR, "unknown token in _${context}_container_row_restore\n"); |
| return; |
| } |
| |
| DEBUGMSGTL(("internal:${context}:_${context}_container_row_restore", |
| "parsing line '%s'\n", buf)); |
| |
| /* |
| * pull out index and create default row |
| */ |
| index.oids = tmp_oid; |
| index.len = OID_LENGTH(tmp_oid); |
| buf = read_config_read_objid(buf, &index.oids, |
| &index.len); |
| if (NULL == buf) { |
| snmp_log(LOG_ERR, "error reading row index in " |
| "_${context}_container_row_restore\n"); |
| return; |
| } |
| rowreq_ctx = _mfd_${context}_rowreq_from_index( &index, NULL ); |
| if (NULL == rowreq_ctx) { |
| snmp_log(LOG_ERR, "error creating row index in " |
| "_${context}_container_row_restore\n"); |
| return; |
| } |
| |
| /* |
| * loop through and get each column |
| */ |
| buf = skip_white(buf); |
| while ( (NULL != buf) && isdigit(*buf) ) { |
| /* |
| * extract column, skip ':' |
| */ |
| col = (u_int)strtol(buf, &buf, 10); |
| if (NULL == buf) |
| break; |
| if (*buf != ':') { |
| buf = NULL; |
| break; |
| } |
| ++buf; /* skip : */ |
| |
| /* |
| * parse value |
| */ |
| DEBUGMSGTL(("_${context}_container_row_restore", |
| "parsing column %d\n", col)); |
| buf = _${context}_container_col_restore( rowreq_ctx, col, buf ); |
| ++found; |
| } |
| if (0 == found) { |
| snmp_log(LOG_ERR, "error parsing ${context} row; no columns found\n"); |
| ${context}_release_rowreq_ctx( rowreq_ctx ); |
| return; |
| } |
| |
| /* |
| * if you added any non-column data, this is where |
| * you should handle it. |
| */ |
| |
| /* |
| * if the pointer is NULL and we didn't reach the |
| * end of the line, something went wrong. Log message, |
| * delete the row and bail. |
| */ |
| if ((buf == NULL) || (*buf != LINE_TERM_CHAR)) { |
| snmp_log(LOG_ERR, "error parsing ${context} row around column %d\n", |
| col); |
| ${context}_release_rowreq_ctx( rowreq_ctx ); |
| return; |
| } |
| |
| DEBUGMSGTL(("internal:${context}:_${context}_container_row_restore", |
| "inserting row\n")); |
| |
| /* |
| * copy oid index and insert row |
| */ |
| rowreq_ctx->oid_idx.len = index.len; |
| memcpy(rowreq_ctx->oid_idx.oids, index.oids, index.len * sizeof(oid)); |
| |
| CONTAINER_INSERT(${context}_if_ctx.container, rowreq_ctx); |
| } |
| |
| /************************************************************ |
| * _${context}_container_col_save |
| */ |
| static char * |
| _${context}_container_col_save( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| u_int col, char* buf) |
| { |
| if( ( NULL == rowreq_ctx ) || ( NULL == buf )) { |
| snmp_log(LOG_ERR, "bad parameter in " |
| "_${context}_container_col_save\n"); |
| return NULL; |
| } |
| |
| DEBUGMSGTL(("internal:${context}:_${context}_container_col_save", |
| "processing column %d\n", col)); |
| |
| /* |
| * prefix with column number, so we don't ever depend on |
| * order saved. |
| */ |
| buf += sprintf(buf, "%u:", col); |
| |
| /* |
| * save data for the column |
| */ |
| switch(col) { |
| |
| @ foreach $node nonindex@ |
| @ include m2c_setup_node.m2i@ |
| case COLUMN_$node.uc: /** $node.syntax = $node.type */ |
| @ if $m2c_node_needlength == 1@ |
| @ if "$node.type" eq "ASN_OBJECT_ID"@ |
| buf = read_config_save_objid(buf, ${m2c_ctx_rh}, |
| ${m2c_ctx_rhs} ); |
| @ else@ # "$node.type" eq "ASN_OCTET_STR"@ |
| buf = read_config_save_octet_string(buf, ${m2c_ctx_rh}, |
| ${m2c_ctx_rhs} ); |
| @ end@ |
| @ elsif "$node.type" eq "ASN_INTEGER"@ |
| buf += sprintf(buf,"%ld",${m2c_ctx_rh}); |
| @ else@ |
| buf += sprintf(buf,"%lu",${m2c_ctx_rh}); |
| @ end@ |
| break; |
| |
| @ end@ # for each |
| default: /** We shouldn't get here */ |
| snmp_log(LOG_ERR, "unknown column %d in " |
| "_${context}_container_col_save\n", col); |
| return NULL; |
| } |
| |
| return buf; |
| } |
| |
| /************************************************************ |
| * _${context}_container_col_restore |
| */ |
| static char * |
| _${context}_container_col_restore( |
| ${context}_rowreq_ctx *rowreq_ctx, |
| u_int col, char* buf) |
| { |
| size_t len; |
| if( ( NULL == rowreq_ctx ) || ( NULL == buf )) { |
| snmp_log(LOG_ERR, "bad parameter in " |
| "_${context}_container_col_restore\n"); |
| return NULL; |
| } |
| |
| DEBUGMSGTL(("verbose:${context}:_${context}_container_col_restore", |
| "processing column %d\n", col)); |
| |
| /* |
| * restore data for the column |
| */ |
| switch(col) { |
| |
| @ foreach $node nonindex@ |
| @ include m2c_setup_node.m2i@ |
| case COLUMN_$node.uc: /** $node.syntax = $node.type */ |
| @ if $m2c_node_needlength == 1@ |
| ${m2c_ctx_rhs} = sizeof(${m2c_ctx_rh}); |
| buf = read_config_read_memory($node.type,buf, |
| (char*)&${m2c_ctx_rh}, |
| (size_t*)&${m2c_ctx_rhs} ); |
| @ if "$node.type" eq "ASN_OBJECT_ID"@ |
| ${m2c_ctx_rhs} /= sizeof(oid); |
| @ end@ |
| @ else@ |
| len = sizeof(${m2c_ctx_rh}); |
| @ if "$node.type" eq "ASN_OCTET_STR"@ # BITS |
| @ eval $m2c_tmp = "ASN_INTEGER"@ |
| @ else@ |
| @ eval $m2c_tmp = $node.type@ |
| @ end@ |
| buf = read_config_read_memory($m2c_tmp, buf, |
| (char*)&${m2c_ctx_rh}, |
| &len); |
| @ end@ |
| @ if $m2c_table_sparse == 1@ |
| if (NULL != buf) |
| rowreq_ctx->column_exists_flags |= COLUMN_$node.uc_FLAG; |
| @ end@ # table sparse |
| break; |
| |
| @ end@ # foreach col |
| default: /** We shouldn't get here */ |
| snmp_log(LOG_ERR, "unknown column %d in " |
| "_${context}_container_col_restore\n", col); |
| return NULL; |
| } |
| |
| return buf; |
| } |
| |
| ## |
| @end@ // $m2c_processing_type eq 'i' |
| ######################################################################## |
| @if $m2c_mark_boundary == 1@ |
| /** END code generated by $RCSfile$ $Revision$ */ |
| @end@ |