/** clones a dataset row, including all data. */ netsnmp_table_row * netsnmp_table_data_set_clone_row(netsnmp_table_row *row) { netsnmp_table_data_set_storage *data, **newrowdata; netsnmp_table_row *newrow; if (!row) return NULL; newrow = netsnmp_table_data_clone_row(row); if (!newrow) return NULL; data = (netsnmp_table_data_set_storage *) row->data; if (data) { for (newrowdata = (netsnmp_table_data_set_storage **) &(newrow->data); data; newrowdata = &((*newrowdata)->next), data = data->next) { memdup((u_char **) newrowdata, (u_char *) data, sizeof(netsnmp_table_data_set_storage)); if (!*newrowdata) { netsnmp_table_dataset_delete_row(newrow); return NULL; } if (data->data.voidp) { memdup((u_char **) & ((*newrowdata)->data.voidp), (u_char *) data->data.voidp, data->data_len); if (!(*newrowdata)->data.voidp) { netsnmp_table_dataset_delete_row(newrow); return NULL; } } } } return newrow; }
/** 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; }
static void parse_table_row( int cpu, char *p, struct snmp_vars *sys, netsnmp_table_data_set *data_set) { netsnmp_table_row *row; row = netsnmp_create_table_data_row(); netsnmp_table_row_add_index(row, ASN_INTEGER, (u_char *)&cpu, sizeof(cpu)); if (sys == cputable) { /* add cpuIndex as column too to break SMIv2 */ netsnmp_set_row_column(row, 1, sys->type, (char *)&cpu, sizeof(cpu)); } for (++sys; p && sys->obj; sys++) { char *val; long long val64; unsigned int uval32; int val32; struct counter64 c64; p += strspn(p, " \t"); val = p; if ((p = strpbrk(p, " \t"))) *p++ = '\0'; if (index(val, '.')) { double d = strtod(val, NULL); val64 = (long long)(d * 100); } else val64 = strtoll(val, NULL, 10); switch (sys->type) { case ASN_OCTET_STR: netsnmp_set_row_column(row, sys->obj, sys->type, (char *)val, strlen(val)); break; case ASN_INTEGER: case ASN_GAUGE: val32 = (int)val64; netsnmp_set_row_column(row, sys->obj, sys->type, (char *)&val32, sizeof(val32)); break; case ASN_COUNTER: uval32 = (unsigned int)val64; netsnmp_set_row_column(row, sys->obj, sys->type, (char *)&uval32, sizeof(uval32)); break; case ASN_COUNTER64: c64.low = (uint32_t)val64; c64.high = val64 >> 32; netsnmp_set_row_column(row, sys->obj, sys->type, (char *)&c64, sizeof(c64)); break; default: netsnmp_table_dataset_delete_row(row); continue; } } netsnmp_table_data_add_row(data_set->table, row); }