/** * marks a given column in a row as writable or not. */ int netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column, int writable) { netsnmp_table_data_set_storage *data; if (!row) return SNMPERR_GENERR; data = (netsnmp_table_data_set_storage *) row->data; data = netsnmp_table_data_set_find_column(data, column); if (!data) { /* * create it */ data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage); if (!data) { snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column"); return SNMPERR_MALLOC; } data->column = column; data->writable = writable; data->next = row->data; row->data = data; } else { data->writable = writable; } return SNMPERR_SUCCESS; }
/** adds a new default row to a table_set. * Arguments should be the table_set, column number, variable type and * finally a 1 if it is allowed to be writable, or a 0 if not. If the * default_value field is not NULL, it will be used to populate new * valuse in that column fro newly created rows. It is copied into the * storage template (free your calling argument). * * returns SNMPERR_SUCCESS or SNMPERR_FAILURE */ int netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set, unsigned int column, int type, int writable, void *default_value, size_t default_value_len) { netsnmp_table_data_set_storage *new_col, *ptr, *pptr; if (!table_set) return SNMPERR_GENERR; /* * double check */ new_col = netsnmp_table_data_set_find_column(table_set->default_row, column); if (new_col != NULL) { if (new_col->type == type && new_col->writable == writable) return SNMPERR_SUCCESS; return SNMPERR_GENERR; } new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage); if (new_col == NULL) return SNMPERR_GENERR; new_col->type = type; new_col->writable = writable; new_col->column = column; if (default_value) { memdup((u_char **) & (new_col->data.voidp), (u_char *) default_value, default_value_len); new_col->data_len = default_value_len; } if (table_set->default_row == NULL) table_set->default_row = new_col; else { /* sort in order just because (needed for add_row support) */ for (ptr = table_set->default_row, pptr = NULL; ptr; pptr = ptr, ptr = ptr->next) { if (ptr->column > column) { new_col->next = ptr; if (pptr) pptr->next = new_col; else table_set->default_row = new_col; return SNMPERR_SUCCESS; } } if (pptr) pptr->next = new_col; else snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row"); } return SNMPERR_SUCCESS; }
/** * extracts a netsnmp_table_data_set pointer from a given request */ netsnmp_table_data_set_storage * netsnmp_extract_table_data_set_column(netsnmp_request_info *request, unsigned int column) { netsnmp_table_data_set_storage *data = netsnmp_extract_table_row_data( request ); if (data) { data = netsnmp_table_data_set_find_column(data, column); } return data; }
/** * Sets a given column in a row with data given a type, value, * and length. Data is memdup'ed by the function, at least if * type != SNMP_NOSUCHINSTANCE and if value_len > 0. * * @param[in] row Pointer to the row to be modified. * @param[in] column Index of the column to be modified. * @param[in] type Either the ASN type of the value to be set or * SNMP_NOSUCHINSTANCE. * @param[in] value If type != SNMP_NOSUCHINSTANCE, pointer to the * new value. May be NULL if value_len == 0, e.g. when storing a * zero-length octet string. Ignored when type == SNMP_NOSUCHINSTANCE. * @param[in] value_len If type != SNMP_NOSUCHINSTANCE, number of bytes * occupied by *value. Ignored when type == SNMP_NOSUCHINSTANCE. * * @return SNMPERR_SUCCESS upon success; SNMPERR_MALLOC when out of memory; * or SNMPERR_GENERR when row == 0 or when type does not match the datatype * of the data stored in *row. * */ int netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column, int type, const void *value, size_t value_len) { netsnmp_table_data_set_storage *data; if (!row) return SNMPERR_GENERR; data = (netsnmp_table_data_set_storage *) row->data; data = netsnmp_table_data_set_find_column(data, column); if (!data) { /* * create it */ data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage); if (!data) { snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column"); return SNMPERR_MALLOC; } data->column = column; data->type = type; data->next = (struct netsnmp_table_data_set_storage_s*)row->data; row->data = data; } /* Transitions from / to SNMP_NOSUCHINSTANCE are allowed, but no other transitions. */ if (data->type != type && data->type != SNMP_NOSUCHINSTANCE && type != SNMP_NOSUCHINSTANCE) return SNMPERR_GENERR; /* Return now if neither the type nor the data itself has been modified. */ if (data->type == type && data->data_len == value_len && (value == NULL || memcmp(&data->data.string, value, value_len) == 0)) return SNMPERR_SUCCESS; /* Reallocate memory and store the new value. */ data->data.voidp = realloc(data->data.voidp, value ? value_len : 0); if (value && value_len && !data->data.voidp) { data->data_len = 0; data->type = SNMP_NOSUCHINSTANCE; snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column"); return SNMPERR_MALLOC; } if (value && value_len) memcpy(data->data.string, value, value_len); data->type = type; data->data_len = value_len; return SNMPERR_SUCCESS; }
/** * sets a given column in a row with data given a type, value, and * length. Data is memdup'ed by the function. */ int netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column, int type, const char *value, size_t value_len) { netsnmp_table_data_set_storage *data; if (!row) return SNMPERR_GENERR; data = (netsnmp_table_data_set_storage *) row->data; data = netsnmp_table_data_set_find_column(data, column); if (!data) { /* * create it */ data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage); if (!data) { snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column"); return SNMPERR_MALLOC; } data->column = column; data->type = type; data->next = row->data; row->data = data; } if (value) { if (data->type != type) return SNMPERR_GENERR; SNMP_FREE(data->data.voidp); if (value_len) { if (memdup(&data->data.string, value, (value_len)) != SNMPERR_SUCCESS) { snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column"); return SNMPERR_MALLOC; } } else { data->data.string = malloc(1); } data->data_len = value_len; } return SNMPERR_SUCCESS; }
/* * send trap */ void run_mte_events(struct mteTriggerTable_data *item, oid * name_oid, size_t name_oid_len, const char *eventobjowner, const char *eventobjname) { static oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; /* snmpTrapIOD.0 */ netsnmp_variable_list *var_list = NULL; netsnmp_table_row *row, *notif_row; netsnmp_table_data_set_storage *col1, *tc, *no, *noo; for(row = table_set->table->first_row; row; row = row->next) { if (strcmp(row->indexes->val.string, eventobjowner) == 0 && strcmp(row->indexes->next_variable->val.string, eventobjname) == 0) { /* run this event */ col1 = (netsnmp_table_data_set_storage *) row->data; tc = netsnmp_table_data_set_find_column(col1, COLUMN_MTEEVENTACTIONS); if (!tc->data.bitstring[0] & 0x80) { /* not a notification. next! (XXX: do sets) */ continue; } tc = netsnmp_table_data_set_find_column(col1, COLUMN_MTEEVENTENABLED); if (*(tc->data.integer) != 1) { /* not enabled. next! */ continue; } if (!mteEventNotif_table_set) { /* no notification info */ continue; } /* send the notification */ var_list = NULL; /* XXX: get notif information */ for(notif_row = mteEventNotif_table_set->table->first_row; notif_row; notif_row = notif_row->next) { if (strcmp(notif_row->indexes->val.string, eventobjowner) == 0 && strcmp(notif_row->indexes->next_variable->val.string, eventobjname) == 0) { /* run this event */ col1 = (netsnmp_table_data_set_storage *) notif_row->data; tc = netsnmp_table_data_set_find_column(col1, COLUMN_MTEEVENTNOTIFICATION); no = netsnmp_table_data_set_find_column(col1, COLUMN_MTEEVENTNOTIFICATIONOBJECTS); noo = netsnmp_table_data_set_find_column(col1, COLUMN_MTEEVENTNOTIFICATIONOBJECTSOWNER); if (!tc) continue; /* no notification to be had. XXX: return? */ /* * snmpTrap oid */ snmp_varlist_add_variable(&var_list, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), ASN_OBJECT_ID, (u_char *) tc->data.objid, tc->data_len); /* XXX: add objects from the mteObjectsTable */ DEBUGMSGTL(("mteEventTable:send_events", "no: %x, no->data: %s", no, no->data.string)); DEBUGMSGTL(("mteEventTable:send_events", "noo: %x, noo->data: %s", noo, noo->data.string)); DEBUGMSGTL(("mteEventTable:send_events", "name_oid: %x",name_oid)); if (no && no->data.string && noo && noo->data.string && name_oid) { char *tmpowner = netsnmp_strdup_and_null(noo->data.string, noo->data_len); char *tmpname = netsnmp_strdup_and_null(no->data.string, no->data_len); DEBUGMSGTL(("mteEventTable:send_events", "Adding objects for owner=%s name=%s", tmpowner, tmpname)); mte_add_objects(var_list, item, tmpowner, tmpname, name_oid + item->mteTriggerValueIDLen, name_oid_len - item->mteTriggerValueIDLen); free(tmpowner); free(tmpname); } DEBUGMSGTL(("mteEventTable:send_events", "sending an event ")); DEBUGMSGOID(("mteEventTable:send_events", tc->data.objid, tc->data_len / sizeof(oid))); DEBUGMSG(("mteEventTable:send_events", "\n")); send_v2trap(var_list); snmp_free_varbind(var_list); } } } } }
/** implements the table data helper. This is the routine that takes * care of all SNMP requests coming into the table. */ int netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_data_set_storage *data = NULL; newrow_stash *newrowstash = NULL; netsnmp_table_row *row, *newrow = NULL; netsnmp_table_request_info *table_info; netsnmp_request_info *request; oid *suffix; size_t suffix_len; netsnmp_oid_stash_node **stashp = NULL; if (!handler) return SNMPERR_GENERR; DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n")); for (request = requests; request; request = request->next) { netsnmp_table_data_set *datatable = (netsnmp_table_data_set *) handler->myvoid; if (request->processed) continue; /* * extract our stored data and table info */ row = netsnmp_extract_table_row(request); table_info = netsnmp_extract_table_info(request); suffix = requests->requestvb->name + reginfo->rootoid_len + 2; suffix_len = requests->requestvb->name_length - (reginfo->rootoid_len + 2); if (MODE_IS_SET(reqinfo->mode)) { char buf[256]; /* is this reasonable size?? */ int rc; size_t len; /* * use a cached copy of the row for modification */ /* * cache location: may have been created already by other * SET requests in the same master request. */ rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:", datatable->table->name); if ((-1 == rc) || (rc >= sizeof(buf))) { snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } len = sizeof(buf) - rc; rc = snprint_objid(&buf[rc], len, table_info->index_oid, table_info->index_oid_len); if (-1 == rc) { snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } stashp = (netsnmp_oid_stash_node **) netsnmp_table_get_or_create_row_stash(reqinfo, buf); newrowstash = netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len); if (!newrowstash) { if (!row) { if (datatable->allow_creation) { /* * entirely new row. Create the row from the template */ newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; } else if (datatable->rowstatus_column == 0) { /* * A RowStatus object may be used to control the * creation of a new row. But if this object * isn't declared (and the table isn't marked as * 'auto-create'), then we can't create a new row. */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } } else { /* * existing row that needs to be modified */ newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash); newrow = netsnmp_table_data_set_clone_row(row); newrowstash->newrow = newrow; } netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } else { newrow = newrowstash->newrow; } /* * all future SET data modification operations use this * temp pointer */ if (reqinfo->mode == MODE_SET_RESERVE1 || reqinfo->mode == MODE_SET_RESERVE2) row = newrow; } if (row) data = (netsnmp_table_data_set_storage *) row->data; if (!row || !table_info || !data) { if (!MODE_IS_SET(reqinfo->mode)) { netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } } data = netsnmp_table_data_set_find_column(data, table_info->colnum); switch (reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ if (data && data->data.voidp) netsnmp_table_data_build_result(reginfo, reqinfo, request, row, table_info->colnum, data->type, data->data.voidp, data->data_len); break; case MODE_SET_RESERVE1: if (data) { /* * Can we modify the existing row? */ if (!data->writable) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); } else if (request->requestvb->type != data->type) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE); } } else if (datatable->rowstatus_column == table_info->colnum) { /* * Otherwise, this is where we create a new row using * the RowStatus object (essentially duplicating the * steps followed earlier in the 'allow_creation' case) */ switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: case RS_CREATEANDWAIT: newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; row = newrow; netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } } break; case MODE_SET_RESERVE2: /* * If the agent receives a SET request for an object in a non-existant * row, then the RESERVE1 pass will create the row automatically. * * But since the row doesn't exist at that point, the test for whether * the object is writable or not will be skipped. So we need to check * for this possibility again here. * * Similarly, if row creation is under the control of the RowStatus * object (i.e. allow_creation == 0), but this particular request * doesn't include such an object, then the row won't have been created, * and the writable check will also have been skipped. Again - check here. */ if (data && data->writable == 0) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); continue; } if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_ACTIVE: case RS_NOTINSERVICE: /* * Can only operate on pre-existing rows. */ if (!newrowstash || newrowstash->created) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_CREATEANDGO: case RS_CREATEANDWAIT: /* * Can only operate on newly created rows. */ if (!(newrowstash && newrowstash->created)) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_DESTROY: /* * Can operate on new or pre-existing rows. */ break; case RS_NOTREADY: default: /* * Not a valid value to Set */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE); continue; } } if (!data ) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } /* * modify row and set new value */ SNMP_FREE(data->data.string); data->data.string = netsnmp_strdup_and_null(request->requestvb->val.string, request->requestvb->val_len); if (!data->data.string) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); } data->data_len = request->requestvb->val_len; if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: /* * XXX: check legality */ *(data->data.integer) = RS_ACTIVE; break; case RS_CREATEANDWAIT: /* * XXX: check legality */ *(data->data.integer) = RS_NOTINSERVICE; break; case RS_DESTROY: newrowstash->deleted = 1; break; } } break; case MODE_SET_ACTION: /* * Install the new row into the stored table. * Do this only *once* per row .... */ if (newrowstash->state != STATE_ACTION) { newrowstash->state = STATE_ACTION; if (newrowstash->created) { netsnmp_table_dataset_add_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, row, newrow); } } /* * ... but every (relevant) varbind in the request will * need to know about this new row, so update the * per-request row information regardless */ if (newrowstash->created) { netsnmp_request_add_list_data(request, netsnmp_create_data_list(TABLE_DATA_NAME, newrow, NULL)); } break; case MODE_SET_UNDO: /* * extract the new row, replace with the old or delete */ if (newrowstash->state != STATE_UNDO) { newrowstash->state = STATE_UNDO; if (newrowstash->created) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, newrow, row); netsnmp_table_dataset_delete_row(newrow); } } break; case MODE_SET_COMMIT: if (newrowstash->state != STATE_COMMIT) { newrowstash->state = STATE_COMMIT; if (!newrowstash->created) { netsnmp_table_dataset_delete_row(row); } if (newrowstash->deleted) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); } } break; case MODE_SET_FREE: if (newrowstash && newrowstash->state != STATE_FREE) { newrowstash->state = STATE_FREE; netsnmp_table_dataset_delete_row(newrow); } break; } } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
void check_log_size(unsigned int clientreg, void *clientarg) { netsnmp_table_row *row, *deleterow, *tmprow, *deletevarrow; netsnmp_table_data_set_storage *data; u_long count = 0; struct timeval now; long tmpl; gettimeofday(&now, NULL); tmpl = netsnmp_timeval_uptime(&now); for (row = nlmLogTable->table->first_row; row; row = row->next) { /* * check max allowed count */ count++; if (max_logged && count == max_logged) break; /* * check max age */ data = (netsnmp_table_data_set_storage *) row->data; data = netsnmp_table_data_set_find_column(data, COLUMN_NLMLOGTIME); if (max_age && tmpl > (*(data->data.integer) + max_age * 100 * 60)) break; } if (!row) return; /* * we've reached the limit, so keep looping but start deleting * from the beginning */ for (deleterow = nlmLogTable->table->first_row, row = row->next; row; row = row->next) { DEBUGMSGTL(("notification_log", "deleting a log entry\n")); /* * delete contained varbinds */ for (deletevarrow = nlmLogVarTable->table->first_row; deletevarrow; deletevarrow = tmprow) { tmprow = deletevarrow->next; if (deleterow->index_oid_len == deletevarrow->index_oid_len - 1 && snmp_oid_compare(deleterow->index_oid, deleterow->index_oid_len, deletevarrow->index_oid, deleterow->index_oid_len) == 0) { netsnmp_table_dataset_remove_and_delete_row(nlmLogVarTable, deletevarrow); } } /* * delete the master row */ tmprow = deleterow->next; netsnmp_table_dataset_remove_and_delete_row(nlmLogTable, deleterow); deleterow = tmprow; num_deleted++; /* * XXX: delete vars from it's table */ } }