/************************************************************** * Copyright (C) 2001 Alex Rozin, Optical Access * * All Rights Reserved * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * ALEX ROZIN DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * ALEX ROZIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. ******************************************************************/ #include #include #include #include #include #include #include "agutil_api.h" #include "rows.h" #include "row_api.h" #define MAX_CREATION_TIME 60 /* * *************************** */ /* * static file scope functions */ /* * *************************** */ static void rowapi_delete(RMON_ENTRY_T * eold) { register RMON_ENTRY_T *eptr; register RMON_ENTRY_T *prev = NULL; TABLE_DEFINTION_T *table_ptr; table_ptr = (TABLE_DEFINTION_T *) eold->table_ptr; /* * delete timout scheduling */ snmp_alarm_unregister(eold->timer_id); ag_trace("Entry %ld in %s has been deleted", eold->ctrl_index, table_ptr->name); /* * It it was valid entry => deactivate it */ if (RMON1_ENTRY_VALID == eold->status) { if (table_ptr->ClbkDeactivate) table_ptr->ClbkDeactivate(eold); } /* * delete it in users's sence */ if (table_ptr->ClbkDelete) table_ptr->ClbkDelete((RMON_ENTRY_T *) eold->body); if (eold->body) { AGFREE(eold->body); } if (eold->owner) AGFREE(eold->owner); /* * delete it from the list in table */ table_ptr->current_number_of_entries--; for (eptr = table_ptr->first; eptr; eptr = eptr->next) { if (eptr == eold) break; prev = eptr; } if (prev) prev->next = eold->next; else table_ptr->first = eold->next; AGFREE(eold); } static void rowapi_too_long_creation_callback(unsigned int clientreg, void *clientarg) { RMON_ENTRY_T *eptr; TABLE_DEFINTION_T *table_ptr; eptr = (RMON_ENTRY_T *) clientarg; table_ptr = (TABLE_DEFINTION_T *) eptr->table_ptr; if (RMON1_ENTRY_VALID != eptr->status) { ag_trace("row #%d in %s was under creation more then %ld sec.", eptr->ctrl_index, table_ptr->name, (long) MAX_CREATION_TIME); rowapi_delete(eptr); } else { snmp_alarm_unregister(eptr->timer_id); } } static int rowapi_deactivate(TABLE_DEFINTION_T * table_ptr, RMON_ENTRY_T * eptr) { if (RMON1_ENTRY_UNDER_CREATION == eptr->status) { /* * nothing to do */ return SNMP_ERR_NOERROR; } if (table_ptr->ClbkDeactivate) table_ptr->ClbkDeactivate(eptr); eptr->status = RMON1_ENTRY_UNDER_CREATION; eptr->timer_id = snmp_alarm_register(MAX_CREATION_TIME, 0, rowapi_too_long_creation_callback, eptr); ag_trace("Entry %ld in %s has been deactivated", eptr->ctrl_index, table_ptr->name); return SNMP_ERR_NOERROR; } static int rowapi_activate(TABLE_DEFINTION_T * table_ptr, RMON_ENTRY_T * eptr) { RMON1_ENTRY_STATUS_T prev_status = eptr->status; eptr->status = RMON1_ENTRY_VALID; if (table_ptr->ClbkActivate) { if (0 != table_ptr->ClbkActivate(eptr)) { ag_trace("Can't activate entry #%ld in %s", eptr->ctrl_index, table_ptr->name); eptr->status = prev_status; return SNMP_ERR_BADVALUE; } } snmp_alarm_unregister(eptr->timer_id); eptr->timer_id = 0; ag_trace("Entry %ld in %s has been activated", eptr->ctrl_index, table_ptr->name); return SNMP_ERR_NOERROR; } /* * creates an entry, locats it in proper sorted order by index * Row is initialized to zero, * except: 'next', 'table_ptr', 'index', * 'timer_id' & 'status'=(RMON1_ENTRY_UNDER_CREATION) * Calls (if need) ClbkCreate. * Schedules for timeout under entry creation (id of this * scheduling is saved in 'timer_id'). * Returns 0: OK, -1:max. number exedes; -2:malloc failed; -3:ClbkCreate failed */ int ROWAPI_new(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index) { register RMON_ENTRY_T *eptr; register RMON_ENTRY_T *prev = NULL; register RMON_ENTRY_T *enew; /* * check on 'max.number' */ if (table_ptr->max_number_of_entries > 0 && table_ptr->current_number_of_entries >= table_ptr->max_number_of_entries) return -1; /* * allocate memory for the header */ enew = (RMON_ENTRY_T *) AGMALLOC(sizeof(RMON_ENTRY_T)); if (!enew) return -2; /* * init the header */ memset(enew, 0, sizeof(RMON_ENTRY_T)); enew->ctrl_index = ctrl_index; enew->table_ptr = (void *) table_ptr; enew->status = RMON1_ENTRY_UNDER_CREATION; enew->only_just_created = 1; /* * create the body: alloc it and set defaults */ if (table_ptr->ClbkCreate) { if (0 != table_ptr->ClbkCreate(enew)) { AGFREE(enew); return -3; } } table_ptr->current_number_of_entries++; /* * find the place : before 'eptr' and after 'prev' */ for (eptr = table_ptr->first; eptr; eptr = eptr->next) { if (ctrl_index < eptr->ctrl_index) break; prev = eptr; } /* * insert it */ enew->next = eptr; if (prev) prev->next = enew; else table_ptr->first = enew; enew->timer_id = snmp_alarm_register(MAX_CREATION_TIME, 0, rowapi_too_long_creation_callback, enew); ag_trace("Entry %ld in %s has been created", enew->ctrl_index, table_ptr->name); return 0; } /* * ****************************** */ /* * external usage (API) functions */ /* * ****************************** */ void ROWAPI_init_table(TABLE_DEFINTION_T * table_ptr, const char *name, u_long max_number_of_entries, ENTRY_CALLBACK_T * ClbkCreate, ENTRY_CALLBACK_T * ClbkClone, ENTRY_CALLBACK_T * ClbkDelete, ENTRY_CALLBACK_T * ClbkValidate, ENTRY_CALLBACK_T * ClbkActivate, ENTRY_CALLBACK_T * ClbkDeactivate, ENTRY_CALLBACK_T * ClbkCopy) { table_ptr->name = name; if (!table_ptr->name) table_ptr->name = NETSNMP_REMOVE_CONST(char*,"Unknown"); table_ptr->max_number_of_entries = max_number_of_entries; table_ptr->ClbkCreate = ClbkCreate; table_ptr->ClbkClone = ClbkClone; table_ptr->ClbkDelete = ClbkDelete; table_ptr->ClbkValidate = ClbkValidate; table_ptr->ClbkActivate = ClbkActivate; table_ptr->ClbkDeactivate = ClbkDeactivate; table_ptr->ClbkCopy = ClbkCopy; table_ptr->first = NULL; table_ptr->current_number_of_entries = 0; } void ROWAPI_delete_clone(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index) { register RMON_ENTRY_T *eptr; eptr = ROWAPI_find(table_ptr, ctrl_index); if (eptr) { if (eptr->new_owner) AGFREE(eptr->new_owner); if (eptr->tmp) { if (table_ptr->ClbkDelete) table_ptr->ClbkDelete((RMON_ENTRY_T *) eptr->tmp); AGFREE(eptr->tmp); } if (eptr->only_just_created) { rowapi_delete(eptr); } } } RMON_ENTRY_T * ROWAPI_get_clone(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index, size_t body_size) { register RMON_ENTRY_T *eptr; if (ctrl_index < 1 || ctrl_index > 0xFFFFu) { ag_trace("%s: index %ld out of range (1..65535)", table_ptr->name, (long) ctrl_index); return NULL; } /* * get it */ eptr = ROWAPI_find(table_ptr, ctrl_index); if (!eptr) { /* try to create */ if (0 != ROWAPI_new(table_ptr, ctrl_index)) { return NULL; } /* * get it */ eptr = ROWAPI_find(table_ptr, ctrl_index); if (!eptr) /* it is unbelievable, but ... :( */ return NULL; } eptr->new_status = eptr->status; eptr->tmp = AGMALLOC(body_size); if (!eptr->tmp) { if (eptr->only_just_created) rowapi_delete(eptr); return NULL; } memcpy(eptr->tmp, eptr->body, body_size); if (table_ptr->ClbkClone) table_ptr->ClbkClone(eptr); if (eptr->new_owner) AGFREE(eptr->new_owner); return eptr->tmp; } RMON_ENTRY_T * ROWAPI_first(TABLE_DEFINTION_T * table_ptr) { return table_ptr->first; } /* * returns an entry with the smallest index * which index > prev_index */ RMON_ENTRY_T * ROWAPI_next(TABLE_DEFINTION_T * table_ptr, u_long prev_index) { register RMON_ENTRY_T *eptr; for (eptr = table_ptr->first; eptr; eptr = eptr->next) if (eptr->ctrl_index > prev_index) return eptr; return NULL; } RMON_ENTRY_T * ROWAPI_find(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index) { register RMON_ENTRY_T *eptr; for (eptr = table_ptr->first; eptr; eptr = eptr->next) { if (eptr->ctrl_index == ctrl_index) return eptr; if (eptr->ctrl_index > ctrl_index) break; } return NULL; } int ROWAPI_action_check(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index) { register RMON_ENTRY_T *eptr; eptr = ROWAPI_find(table_ptr, ctrl_index); if (!eptr) { ag_trace("Smth wrong ?"); return SNMP_ERR_GENERR; } /* * test owner string */ if (RMON1_ENTRY_UNDER_CREATION != eptr->status) { /* * Only the same value is allowed */ if (eptr->new_owner && (!eptr->owner || strncmp(eptr->new_owner, eptr->owner, MAX_OWNERSTRING))) { ag_trace("invalid owner string in ROWAPI_action_check"); ag_trace("eptr->new_owner=%p eptr->owner=%p", eptr->new_owner, eptr->owner); return SNMP_ERR_BADVALUE; } } switch (eptr->new_status) { /* this status we want to set */ case RMON1_ENTRY_CREATE_REQUEST: if (RMON1_ENTRY_UNDER_CREATION != eptr->status) return SNMP_ERR_BADVALUE; break; case RMON1_ENTRY_INVALID: break; case RMON1_ENTRY_VALID: if (RMON1_ENTRY_VALID == eptr->status) { break; /* nothing to do */ } if (RMON1_ENTRY_UNDER_CREATION != eptr->status) { ag_trace("Validate %s: entry %ld has wrong status %d", table_ptr->name, (long) ctrl_index, (int) eptr->status); return SNMP_ERR_BADVALUE; } /* * Our MIB understanding extension: we permit to set * VALID when entry doesn't exit, in this case PDU has to have * the nessessary & valid set of non-default values */ if (table_ptr->ClbkValidate) { return table_ptr->ClbkValidate(eptr); } break; case RMON1_ENTRY_UNDER_CREATION: /* * Our MIB understanding extension: we permit to travel from * VALID to 'UNDER_CREATION' state */ break; } return SNMP_ERR_NOERROR; } int ROWAPI_commit(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index) { register RMON_ENTRY_T *eptr; eptr = ROWAPI_find(table_ptr, ctrl_index); if (!eptr) { ag_trace("Smth wrong ?"); return SNMP_ERR_GENERR; } eptr->only_just_created = 0; switch (eptr->new_status) { /* this status we want to set */ case RMON1_ENTRY_CREATE_REQUEST: /* copy tmp => eprt */ if (eptr->new_owner) { if (eptr->owner) AGFREE(eptr->owner); eptr->owner = AGSTRDUP(eptr->new_owner); } if (table_ptr->ClbkCopy && eptr->tmp) table_ptr->ClbkCopy(eptr); break; case RMON1_ENTRY_INVALID: ROWAPI_delete_clone(table_ptr, ctrl_index); rowapi_delete(eptr); #if 0 /* for debug */ dbg_f_AG_MEM_REPORT(); #endif break; case RMON1_ENTRY_VALID: /* copy tmp => eprt and activate */ /* * Our MIB understanding extension: we permit to set * VALID when entry doesn't exit, in this case PDU has to have * the nessessary & valid set of non-default values */ if (eptr->new_owner) { if (eptr->owner) AGFREE(eptr->owner); eptr->owner = AGSTRDUP(eptr->new_owner); } if (table_ptr->ClbkCopy && eptr->tmp) table_ptr->ClbkCopy(eptr); if (RMON1_ENTRY_VALID != eptr->status) { rowapi_activate(table_ptr, eptr); } break; case RMON1_ENTRY_UNDER_CREATION: /* deactivate (if need) and copy tmp => eprt */ /* * Our MIB understanding extension: we permit to travel from * VALID to 'UNDER_CREATION' state */ rowapi_deactivate(table_ptr, eptr); if (eptr->new_owner) { if (eptr->owner) AGFREE(eptr->owner); eptr->owner = AGSTRDUP(eptr->new_owner); } if (table_ptr->ClbkCopy && eptr->tmp) table_ptr->ClbkCopy(eptr); break; } ROWAPI_delete_clone(table_ptr, ctrl_index); return SNMP_ERR_NOERROR; } RMON_ENTRY_T * ROWAPI_header_ControlEntry(struct variable * vp, oid * name, size_t * length, int exact, size_t * var_len, TABLE_DEFINTION_T * table_ptr, void *entry_ptr, size_t entry_size) { long ctrl_index; RMON_ENTRY_T *hdr = NULL; if (0 != AGUTIL_advance_index_name(vp, name, length, exact)) { ag_trace("cannot advance_index_name"); return NULL; } ctrl_index = vp->namelen >= *length ? 0 : name[vp->namelen]; if (exact) { if (ctrl_index) hdr = ROWAPI_find(table_ptr, ctrl_index); } else { if (ctrl_index) hdr = ROWAPI_next(table_ptr, ctrl_index); else hdr = ROWAPI_first(table_ptr); if (hdr) { /* set new index */ name[vp->namelen] = hdr->ctrl_index; *length = vp->namelen + 1; } } if (hdr) memcpy(entry_ptr, hdr->body, entry_size); return hdr; } int ROWAPI_do_another_action(oid * name, int tbl_first_index_begin, int action, int *prev_action, TABLE_DEFINTION_T * table_ptr, size_t entry_size) { long long_temp; RMON_ENTRY_T *tmp; if (action == *prev_action) return SNMP_ERR_NOERROR; /* I want to process it only once ! */ *prev_action = action; long_temp = name[tbl_first_index_begin]; switch (action) { case RESERVE1: tmp = ROWAPI_get_clone(table_ptr, long_temp, entry_size); if (!tmp) { ag_trace("RESERVE1: cannot get clone\n"); return SNMP_ERR_TOOBIG; } break; case FREE: /* if RESERVEx failed: release any resources that have been allocated */ case UNDO: /* if ACTION failed: release any resources that have been allocated */ ROWAPI_delete_clone(table_ptr, long_temp); break; case ACTION: long_temp = ROWAPI_action_check(table_ptr, long_temp); if (0 != long_temp) return long_temp; break; case COMMIT: long_temp = ROWAPI_commit(table_ptr, long_temp); if (0 != long_temp) /* it MUST NOT be */ return long_temp; break; default: ag_trace("Unknown action %d", (int) action); return SNMP_ERR_GENERR; } /* of switch by actions */ return SNMP_ERR_NOERROR; } /* * data tables API section */ int ROWDATAAPI_init(SCROLLER_T * scrlr, u_long data_requested, u_long max_number_of_entries, size_t data_size, int (*data_destructor) (struct data_scroller *, void *)) { scrlr->data_granted = 0; scrlr->data_created = 0; scrlr->data_total_number = 0; scrlr->first_data_ptr = scrlr->last_data_ptr = scrlr->current_data_ptr = NULL; scrlr->max_number_of_entries = max_number_of_entries; scrlr->data_size = data_size; scrlr->data_destructor = data_destructor; ROWDATAAPI_set_size(scrlr, data_requested, 0); return 0; } static int delete_data_entry(SCROLLER_T * scrlr, void *delete_me) { NEXTED_PTR_T *data_ptr = delete_me; register NEXTED_PTR_T *tmp; if (data_ptr == scrlr->first_data_ptr) { scrlr->first_data_ptr = data_ptr->next; if (data_ptr == scrlr->last_data_ptr) scrlr->last_data_ptr = NULL; } else { /* not first */ for (tmp = scrlr->first_data_ptr; tmp; tmp = tmp->next) { if (tmp->next == data_ptr) { if (data_ptr == scrlr->last_data_ptr) scrlr->last_data_ptr = tmp; tmp->next = data_ptr->next; break; } } /* for */ } /* not first */ if (data_ptr == scrlr->current_data_ptr) scrlr->current_data_ptr = data_ptr->next; if (scrlr->data_destructor) scrlr->data_destructor(scrlr, data_ptr); AGFREE(data_ptr); scrlr->data_created--; scrlr->data_stored--; return 0; } static void realloc_number_of_data(SCROLLER_T * scrlr, long dlong) { void *bptr; /* DATA_ENTRY_T */ NEXTED_PTR_T *prev = NULL; void *first = NULL; if (dlong > 0) { for (; dlong; dlong--, prev = bptr, scrlr->data_created++) { bptr = AGMALLOC(scrlr->data_size); if (!bptr) { ag_trace("Err: no memory for data"); break; } memset(bptr, 0, scrlr->data_size); if (prev) prev->next = bptr; else first = bptr; } /* of loop by malloc bucket */ if (!scrlr->current_data_ptr) scrlr->current_data_ptr = first; if (scrlr->last_data_ptr) { scrlr->last_data_ptr->next = first; } else scrlr->first_data_ptr = first; scrlr->last_data_ptr = bptr; } else { for (; dlong && scrlr->data_created > 0; dlong++) { if (scrlr->current_data_ptr) delete_data_entry(scrlr, scrlr->current_data_ptr); else delete_data_entry(scrlr, scrlr->first_data_ptr); } } } void ROWDATAAPI_set_size(SCROLLER_T * scrlr, u_long data_requested, u_char do_allocation) { long dlong; scrlr->data_requested = data_requested; scrlr->data_granted = (data_requested < scrlr->max_number_of_entries) ? data_requested : scrlr->max_number_of_entries; if (do_allocation) { dlong = (long) scrlr->data_granted - (long) scrlr->data_created; realloc_number_of_data(scrlr, dlong); } } void ROWDATAAPI_descructor(SCROLLER_T * scrlr) { register NEXTED_PTR_T *bptr; register void *next; for (bptr = scrlr->first_data_ptr; bptr; bptr = next) { next = bptr->next; if (scrlr->data_destructor) scrlr->data_destructor(scrlr, bptr); AGFREE(bptr); } scrlr->data_created = 0; scrlr->data_granted = 0; scrlr->first_data_ptr = scrlr->last_data_ptr = scrlr->current_data_ptr = NULL; } void * ROWDATAAPI_locate_new_data(SCROLLER_T * scrlr) { register NEXTED_PTR_T *bptr; if (!scrlr->current_data_ptr) { /* there was wrap */ bptr = scrlr->first_data_ptr; if (!bptr) { ag_trace("Err: SCROLLER_T:locate_new_data: internal error :("); return NULL; } scrlr->first_data_ptr = bptr->next; scrlr->last_data_ptr->next = bptr; scrlr->last_data_ptr = (NEXTED_PTR_T *) bptr; bptr->next = NULL; } else { bptr = scrlr->current_data_ptr; scrlr->current_data_ptr = bptr->next; ++scrlr->data_stored; } scrlr->data_total_number++; return bptr; } u_long ROWDATAAPI_get_total_number(SCROLLER_T * scrlr) { return scrlr->data_total_number; } RMON_ENTRY_T * ROWDATAAPI_header_DataEntry(struct variable * vp, oid * name, size_t * length, int exact, size_t * var_len, TABLE_DEFINTION_T * table_ptr, SCROLLER_T * (*extract_scroller) (void *body), size_t data_size, void *entry_ptr) { long ctrl_indx, data_index; RMON_ENTRY_T *hdr = NULL; SCROLLER_T *scrlr; NEXTED_PTR_T *bptr = NULL; register u_long iii; if (0 != AGUTIL_advance_index_name(vp, name, length, exact)) { ag_trace("cannot advance_index_name"); return NULL; } ctrl_indx = vp->namelen >= *length ? 0 : name[vp->namelen]; if (ctrl_indx) data_index = ((int)(vp->namelen + 1) >= (int)*length) ? 0 : name[vp->namelen + 1]; else data_index = 0; if (exact) { if (ctrl_indx && data_index) { hdr = ROWAPI_find(table_ptr, ctrl_indx); if (hdr) { scrlr = extract_scroller(hdr->body); bptr = scrlr->first_data_ptr; for (iii = 0; iii < scrlr->data_stored && bptr; iii++, bptr = bptr->next) { if ((long)bptr->data_index == data_index) break; } if (!bptr) hdr = NULL; } } } else { if (ctrl_indx) hdr = ROWAPI_find(table_ptr, ctrl_indx); else hdr = ROWAPI_first(table_ptr); if (hdr) { scrlr = extract_scroller(hdr->body); /* * ag_trace ("get next after (%d %d)", (int) ctrl_indx, (int) data_index); */ bptr = scrlr->first_data_ptr; for (iii = 0; iii < scrlr->data_stored && bptr; iii++, bptr = bptr->next) { if (bptr->data_index && (long)bptr->data_index > data_index) break; } if (bptr && (long)bptr->data_index <= data_index) bptr = NULL; if (!bptr) { /* travel to next row */ /* * ag_trace ("Dbg: travel to next row"); */ for (hdr = hdr->next; hdr; hdr = hdr->next) { if (RMON1_ENTRY_VALID != hdr->status) continue; scrlr = extract_scroller(hdr->body); if (scrlr->data_stored <= 0) continue; for (bptr = scrlr->first_data_ptr; bptr; bptr = bptr->next) { if (bptr->data_index) break; } if (bptr) break; } } if (bptr) { /* set new index */ /* * ag_trace ("Dbg: So (%d %d)", (int) hdr->index, (int) bptr->data_index); */ name[vp->namelen] = hdr->ctrl_index; name[vp->namelen + 1] = bptr->data_index; *length = vp->namelen + 2; } else hdr = NULL; } } if (hdr) memcpy(entry_ptr, bptr, data_size); return hdr; } void init_rows(void) { }