1 /**************************************************************
2  * Copyright (C) 2001 Alex Rozin, Optical Access
3  *
4  *                     All Rights Reserved
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation for any purpose and without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and that
9  * both that copyright notice and this permission notice appear in
10  * supporting documentation.
11  *
12  * ALEX ROZIN DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
14  * ALEX ROZIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
17  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
18  * SOFTWARE.
19  ******************************************************************/
20 
21 #include <net-snmp/net-snmp-config.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <stdlib.h>
25 
26 #include <net-snmp/net-snmp-includes.h>
27 #include <net-snmp/agent/net-snmp-agent-includes.h>
28 
29 #include "agutil_api.h"
30 #include "rows.h"
31 #include "row_api.h"
32 
33 #define MAX_CREATION_TIME	60
34 
35 /*
36  * ***************************
37  */
38 /*
39  * static file scope functions
40  */
41 /*
42  * ***************************
43  */
44 
45 static void
rowapi_delete(RMON_ENTRY_T * eold)46 rowapi_delete(RMON_ENTRY_T * eold)
47 {
48     register RMON_ENTRY_T *eptr;
49     register RMON_ENTRY_T *prev = NULL;
50     TABLE_DEFINTION_T *table_ptr;
51 
52     table_ptr = (TABLE_DEFINTION_T *) eold->table_ptr;
53 
54     /*
55      * delete timout scheduling
56      */
57     snmp_alarm_unregister(eold->timer_id);
58     ag_trace("Entry %ld in %s has been deleted",
59              eold->ctrl_index, table_ptr->name);
60 
61     /*
62      * It it was valid entry => deactivate it
63      */
64     if (RMON1_ENTRY_VALID == eold->status) {
65         if (table_ptr->ClbkDeactivate)
66             table_ptr->ClbkDeactivate(eold);
67     }
68 
69     /*
70      * delete it in users's sence
71      */
72     if (table_ptr->ClbkDelete)
73         table_ptr->ClbkDelete((RMON_ENTRY_T *) eold->body);
74 
75     if (eold->body) {
76         AGFREE(eold->body);
77     }
78 
79     if (eold->owner)
80         AGFREE(eold->owner);
81 
82     /*
83      * delete it from the list in table
84      */
85 
86     table_ptr->current_number_of_entries--;
87 
88     for (eptr = table_ptr->first; eptr; eptr = eptr->next) {
89         if (eptr == eold)
90             break;
91         prev = eptr;
92     }
93 
94     if (prev)
95         prev->next = eold->next;
96     else
97         table_ptr->first = eold->next;
98 
99     AGFREE(eold);
100 }
101 
102 static void
rowapi_too_long_creation_callback(unsigned int clientreg,void * clientarg)103 rowapi_too_long_creation_callback(unsigned int clientreg, void *clientarg)
104 {
105     RMON_ENTRY_T   *eptr;
106     TABLE_DEFINTION_T *table_ptr;
107 
108     eptr = (RMON_ENTRY_T *) clientarg;
109     table_ptr = (TABLE_DEFINTION_T *) eptr->table_ptr;
110     if (RMON1_ENTRY_VALID != eptr->status) {
111         ag_trace("row #%d in %s was under creation more then %ld sec.",
112                  eptr->ctrl_index, table_ptr->name,
113                  (long) MAX_CREATION_TIME);
114         rowapi_delete(eptr);
115     } else {
116         snmp_alarm_unregister(eptr->timer_id);
117     }
118 }
119 
120 static int
rowapi_deactivate(TABLE_DEFINTION_T * table_ptr,RMON_ENTRY_T * eptr)121 rowapi_deactivate(TABLE_DEFINTION_T * table_ptr, RMON_ENTRY_T * eptr)
122 {
123     if (RMON1_ENTRY_UNDER_CREATION == eptr->status) {
124         /*
125          * nothing to do
126          */
127         return SNMP_ERR_NOERROR;
128     }
129 
130     if (table_ptr->ClbkDeactivate)
131         table_ptr->ClbkDeactivate(eptr);
132     eptr->status = RMON1_ENTRY_UNDER_CREATION;
133     eptr->timer_id = snmp_alarm_register(MAX_CREATION_TIME, 0,
134                                          rowapi_too_long_creation_callback,
135                                          eptr);
136     ag_trace("Entry %ld in %s has been deactivated",
137              eptr->ctrl_index, table_ptr->name);
138 
139     return SNMP_ERR_NOERROR;
140 }
141 
142 static int
rowapi_activate(TABLE_DEFINTION_T * table_ptr,RMON_ENTRY_T * eptr)143 rowapi_activate(TABLE_DEFINTION_T * table_ptr, RMON_ENTRY_T * eptr)
144 {
145     RMON1_ENTRY_STATUS_T prev_status = eptr->status;
146 
147     eptr->status = RMON1_ENTRY_VALID;
148 
149     if (table_ptr->ClbkActivate) {
150         if (0 != table_ptr->ClbkActivate(eptr)) {
151             ag_trace("Can't activate entry #%ld in %s",
152                      eptr->ctrl_index, table_ptr->name);
153             eptr->status = prev_status;
154             return SNMP_ERR_BADVALUE;
155         }
156     }
157 
158     snmp_alarm_unregister(eptr->timer_id);
159     eptr->timer_id = 0;
160     ag_trace("Entry %ld in %s has been activated",
161              eptr->ctrl_index, table_ptr->name);
162     return SNMP_ERR_NOERROR;
163 }
164 
165 /*
166  * creates an entry, locats it in proper sorted order by index
167  * Row is initialized to zero,
168  * except: 'next', 'table_ptr', 'index',
169  * 'timer_id' & 'status'=(RMON1_ENTRY_UNDER_CREATION)
170  * Calls (if need) ClbkCreate.
171  * Schedules for timeout under entry creation (id of this
172  * scheduling is saved in 'timer_id').
173  * Returns 0: OK,
174  -1:max. number exedes;
175  -2:malloc failed;
176  -3:ClbkCreate failed */
177 int
ROWAPI_new(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index)178 ROWAPI_new(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index)
179 {
180     register RMON_ENTRY_T *eptr;
181     register RMON_ENTRY_T *prev = NULL;
182     register RMON_ENTRY_T *enew;
183 
184     /*
185      * check on 'max.number'
186      */
187     if (table_ptr->max_number_of_entries > 0 &&
188         table_ptr->current_number_of_entries >=
189         table_ptr->max_number_of_entries)
190         return -1;
191 
192     /*
193      * allocate memory for the header
194      */
195     enew = (RMON_ENTRY_T *) AGMALLOC(sizeof(RMON_ENTRY_T));
196     if (!enew)
197         return -2;
198 
199     /*
200      * init the header
201      */
202     memset(enew, 0, sizeof(RMON_ENTRY_T));
203     enew->ctrl_index = ctrl_index;
204     enew->table_ptr = (void *) table_ptr;
205     enew->status = RMON1_ENTRY_UNDER_CREATION;
206     enew->only_just_created = 1;
207 
208     /*
209      * create the body: alloc it and set defaults
210      */
211     if (table_ptr->ClbkCreate) {
212         if (0 != table_ptr->ClbkCreate(enew)) {
213             AGFREE(enew);
214             return -3;
215         }
216     }
217 
218     table_ptr->current_number_of_entries++;
219 
220     /*
221      * find the place : before 'eptr' and after 'prev'
222      */
223     for (eptr = table_ptr->first; eptr; eptr = eptr->next) {
224         if (ctrl_index < eptr->ctrl_index)
225             break;
226         prev = eptr;
227     }
228 
229     /*
230      * insert it
231      */
232     enew->next = eptr;
233     if (prev)
234         prev->next = enew;
235     else
236         table_ptr->first = enew;
237 
238     enew->timer_id = snmp_alarm_register(MAX_CREATION_TIME, 0,
239                                          rowapi_too_long_creation_callback,
240                                          enew);
241     ag_trace("Entry %ld in %s has been created",
242              enew->ctrl_index, table_ptr->name);
243     return 0;
244 }
245 
246 /*
247  * ******************************
248  */
249 /*
250  * external usage (API) functions
251  */
252 /*
253  * ******************************
254  */
255 
256 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)257 ROWAPI_init_table(TABLE_DEFINTION_T * table_ptr,
258                   const char *name,
259                   u_long max_number_of_entries,
260                   ENTRY_CALLBACK_T * ClbkCreate,
261                   ENTRY_CALLBACK_T * ClbkClone,
262                   ENTRY_CALLBACK_T * ClbkDelete,
263                   ENTRY_CALLBACK_T * ClbkValidate,
264                   ENTRY_CALLBACK_T * ClbkActivate,
265                   ENTRY_CALLBACK_T * ClbkDeactivate,
266                   ENTRY_CALLBACK_T * ClbkCopy)
267 {
268     table_ptr->name = name;
269     if (!table_ptr->name)
270         table_ptr->name = NETSNMP_REMOVE_CONST(char*,"Unknown");
271 
272     table_ptr->max_number_of_entries = max_number_of_entries;
273     table_ptr->ClbkCreate = ClbkCreate;
274     table_ptr->ClbkClone = ClbkClone;
275     table_ptr->ClbkDelete = ClbkDelete;
276     table_ptr->ClbkValidate = ClbkValidate;
277     table_ptr->ClbkActivate = ClbkActivate;
278     table_ptr->ClbkDeactivate = ClbkDeactivate;
279     table_ptr->ClbkCopy = ClbkCopy;
280 
281     table_ptr->first = NULL;
282     table_ptr->current_number_of_entries = 0;
283 }
284 
285 void
ROWAPI_delete_clone(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index)286 ROWAPI_delete_clone(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index)
287 {
288     register RMON_ENTRY_T *eptr;
289 
290     eptr = ROWAPI_find(table_ptr, ctrl_index);
291     if (eptr) {
292         if (eptr->new_owner)
293             AGFREE(eptr->new_owner);
294 
295         if (eptr->tmp) {
296             if (table_ptr->ClbkDelete)
297                 table_ptr->ClbkDelete((RMON_ENTRY_T *) eptr->tmp);
298             AGFREE(eptr->tmp);
299         }
300 
301         if (eptr->only_just_created) {
302             rowapi_delete(eptr);
303         }
304     }
305 }
306 
307 RMON_ENTRY_T   *
ROWAPI_get_clone(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index,size_t body_size)308 ROWAPI_get_clone(TABLE_DEFINTION_T * table_ptr,
309                  u_long ctrl_index, size_t body_size)
310 {
311     register RMON_ENTRY_T *eptr;
312 
313     if (ctrl_index < 1 || ctrl_index > 0xFFFFu) {
314         ag_trace("%s: index %ld out of range (1..65535)",
315                  table_ptr->name, (long) ctrl_index);
316         return NULL;
317     }
318 
319     /*
320      * get it
321      */
322     eptr = ROWAPI_find(table_ptr, ctrl_index);
323 
324     if (!eptr) {                /* try to create */
325         if (0 != ROWAPI_new(table_ptr, ctrl_index)) {
326             return NULL;
327         }
328 
329         /*
330          * get it
331          */
332         eptr = ROWAPI_find(table_ptr, ctrl_index);
333         if (!eptr)              /* it is unbelievable, but ... :( */
334             return NULL;
335     }
336 
337     eptr->new_status = eptr->status;
338 
339     eptr->tmp = AGMALLOC(body_size);
340     if (!eptr->tmp) {
341         if (eptr->only_just_created)
342             rowapi_delete(eptr);
343         return NULL;
344     }
345 
346     memcpy(eptr->tmp, eptr->body, body_size);
347     if (table_ptr->ClbkClone)
348         table_ptr->ClbkClone(eptr);
349 
350     if (eptr->new_owner)
351         AGFREE(eptr->new_owner);
352     return eptr->tmp;
353 }
354 
355 RMON_ENTRY_T   *
ROWAPI_first(TABLE_DEFINTION_T * table_ptr)356 ROWAPI_first(TABLE_DEFINTION_T * table_ptr)
357 {
358     return table_ptr->first;
359 }
360 
361 /*
362  * returns an entry with the smallest index
363  * which index > prev_index
364  */
365 RMON_ENTRY_T   *
ROWAPI_next(TABLE_DEFINTION_T * table_ptr,u_long prev_index)366 ROWAPI_next(TABLE_DEFINTION_T * table_ptr, u_long prev_index)
367 {
368     register RMON_ENTRY_T *eptr;
369 
370     for (eptr = table_ptr->first; eptr; eptr = eptr->next)
371         if (eptr->ctrl_index > prev_index)
372             return eptr;
373 
374     return NULL;
375 }
376 
377 RMON_ENTRY_T   *
ROWAPI_find(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index)378 ROWAPI_find(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index)
379 {
380     register RMON_ENTRY_T *eptr;
381 
382     for (eptr = table_ptr->first; eptr; eptr = eptr->next) {
383         if (eptr->ctrl_index == ctrl_index)
384             return eptr;
385         if (eptr->ctrl_index > ctrl_index)
386             break;
387     }
388 
389     return NULL;
390 }
391 
392 int
ROWAPI_action_check(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index)393 ROWAPI_action_check(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index)
394 {
395     register RMON_ENTRY_T *eptr;
396 
397     eptr = ROWAPI_find(table_ptr, ctrl_index);
398     if (!eptr) {
399         ag_trace("Smth wrong ?");
400         return SNMP_ERR_GENERR;
401     }
402 
403     /*
404      * test owner string
405      */
406     if (RMON1_ENTRY_UNDER_CREATION != eptr->status) {
407         /*
408          * Only the same value is allowed
409          */
410         if (eptr->new_owner &&
411             (!eptr->owner
412              || strncmp(eptr->new_owner, eptr->owner, MAX_OWNERSTRING))) {
413             ag_trace("invalid owner string in ROWAPI_action_check");
414             ag_trace("eptr->new_owner=%p eptr->owner=%p", eptr->new_owner,
415                      eptr->owner);
416             return SNMP_ERR_BADVALUE;
417         }
418     }
419 
420     switch (eptr->new_status) { /* this status we want to set */
421     case RMON1_ENTRY_CREATE_REQUEST:
422         if (RMON1_ENTRY_UNDER_CREATION != eptr->status)
423             return SNMP_ERR_BADVALUE;
424         break;
425     case RMON1_ENTRY_INVALID:
426         break;
427     case RMON1_ENTRY_VALID:
428         if (RMON1_ENTRY_VALID == eptr->status) {
429             break;              /* nothing to do */
430         }
431         if (RMON1_ENTRY_UNDER_CREATION != eptr->status) {
432             ag_trace("Validate %s: entry %ld has wrong status %d",
433                      table_ptr->name, (long) ctrl_index,
434                      (int) eptr->status);
435             return SNMP_ERR_BADVALUE;
436         }
437 
438         /*
439          * Our MIB understanding extension: we permit to set
440          * VALID when entry doesn't exit, in this case PDU has to have
441          * the nessessary & valid set of non-default values
442          */
443         if (table_ptr->ClbkValidate) {
444             return table_ptr->ClbkValidate(eptr);
445         }
446         break;
447     case RMON1_ENTRY_UNDER_CREATION:
448         /*
449          * Our MIB understanding extension: we permit to travel from
450          * VALID to 'UNDER_CREATION' state
451          */
452         break;
453     }
454 
455     return SNMP_ERR_NOERROR;
456 }
457 
458 int
ROWAPI_commit(TABLE_DEFINTION_T * table_ptr,u_long ctrl_index)459 ROWAPI_commit(TABLE_DEFINTION_T * table_ptr, u_long ctrl_index)
460 {
461     register RMON_ENTRY_T *eptr;
462 
463     eptr = ROWAPI_find(table_ptr, ctrl_index);
464     if (!eptr) {
465         ag_trace("Smth wrong ?");
466         return SNMP_ERR_GENERR;
467     }
468 
469     eptr->only_just_created = 0;
470 
471     switch (eptr->new_status) { /* this status we want to set */
472     case RMON1_ENTRY_CREATE_REQUEST:   /* copy tmp => eprt */
473         if (eptr->new_owner) {
474             if (eptr->owner)
475                 AGFREE(eptr->owner);
476             eptr->owner = AGSTRDUP(eptr->new_owner);
477         }
478 
479         if (table_ptr->ClbkCopy && eptr->tmp)
480             table_ptr->ClbkCopy(eptr);
481         break;
482     case RMON1_ENTRY_INVALID:
483         ROWAPI_delete_clone(table_ptr, ctrl_index);
484         rowapi_delete(eptr);
485 #if 0                           /* for debug */
486         dbg_f_AG_MEM_REPORT();
487 #endif
488         break;
489     case RMON1_ENTRY_VALID:    /* copy tmp => eprt and activate */
490         /*
491          * Our MIB understanding extension: we permit to set
492          * VALID when entry doesn't exit, in this case PDU has to have
493          * the nessessary & valid set of non-default values
494          */
495         if (eptr->new_owner) {
496             if (eptr->owner)
497                 AGFREE(eptr->owner);
498             eptr->owner = AGSTRDUP(eptr->new_owner);
499         }
500         if (table_ptr->ClbkCopy && eptr->tmp)
501             table_ptr->ClbkCopy(eptr);
502         if (RMON1_ENTRY_VALID != eptr->status) {
503             rowapi_activate(table_ptr, eptr);
504         }
505         break;
506     case RMON1_ENTRY_UNDER_CREATION:   /* deactivate (if need) and copy tmp => eprt */
507         /*
508          * Our MIB understanding extension: we permit to travel from
509          * VALID to 'UNDER_CREATION' state
510          */
511         rowapi_deactivate(table_ptr, eptr);
512         if (eptr->new_owner) {
513             if (eptr->owner)
514                 AGFREE(eptr->owner);
515             eptr->owner = AGSTRDUP(eptr->new_owner);
516         }
517         if (table_ptr->ClbkCopy && eptr->tmp)
518             table_ptr->ClbkCopy(eptr);
519         break;
520     }
521 
522     ROWAPI_delete_clone(table_ptr, ctrl_index);
523     return SNMP_ERR_NOERROR;
524 }
525 
526 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)527 ROWAPI_header_ControlEntry(struct variable * vp, oid * name,
528                            size_t * length, int exact,
529                            size_t * var_len,
530                            TABLE_DEFINTION_T * table_ptr,
531                            void *entry_ptr, size_t entry_size)
532 {
533     long            ctrl_index;
534     RMON_ENTRY_T   *hdr = NULL;
535 
536     if (0 != AGUTIL_advance_index_name(vp, name, length, exact)) {
537         ag_trace("cannot advance_index_name");
538         return NULL;
539     }
540 
541     ctrl_index = vp->namelen >= *length ? 0 : name[vp->namelen];
542 
543     if (exact) {
544         if (ctrl_index)
545             hdr = ROWAPI_find(table_ptr, ctrl_index);
546     } else {
547         if (ctrl_index)
548             hdr = ROWAPI_next(table_ptr, ctrl_index);
549         else
550             hdr = ROWAPI_first(table_ptr);
551 
552         if (hdr) {              /* set new index */
553             name[vp->namelen] = hdr->ctrl_index;
554             *length = vp->namelen + 1;
555         }
556     }
557 
558     if (hdr)
559         memcpy(entry_ptr, hdr->body, entry_size);
560     return hdr;
561 }
562 
563 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)564 ROWAPI_do_another_action(oid * name, int tbl_first_index_begin,
565                          int action, int *prev_action,
566                          TABLE_DEFINTION_T * table_ptr, size_t entry_size)
567 {
568     long            long_temp;
569     RMON_ENTRY_T   *tmp;
570 
571     if (action == *prev_action)
572         return SNMP_ERR_NOERROR;        /* I want to process it only once ! */
573     *prev_action = action;
574 
575     long_temp = name[tbl_first_index_begin];
576 
577     switch (action) {
578     case RESERVE1:
579         tmp = ROWAPI_get_clone(table_ptr, long_temp, entry_size);
580         if (!tmp) {
581             ag_trace("RESERVE1: cannot get clone\n");
582             return SNMP_ERR_TOOBIG;
583         }
584         break;
585 
586     case FREE:                 /* if RESERVEx failed: release any resources that have been allocated */
587     case UNDO:                 /* if ACTION failed: release any resources that have been allocated */
588         ROWAPI_delete_clone(table_ptr, long_temp);
589         break;
590 
591     case ACTION:
592         long_temp = ROWAPI_action_check(table_ptr, long_temp);
593         if (0 != long_temp)
594             return long_temp;
595         break;
596 
597     case COMMIT:
598         long_temp = ROWAPI_commit(table_ptr, long_temp);
599         if (0 != long_temp)     /* it MUST NOT be */
600             return long_temp;
601         break;
602     default:
603         ag_trace("Unknown action %d", (int) action);
604         return SNMP_ERR_GENERR;
605     }                           /* of switch by actions */
606 
607     return SNMP_ERR_NOERROR;
608 }
609 
610 /*
611  * data tables API section
612  */
613 
614 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 *))615 ROWDATAAPI_init(SCROLLER_T * scrlr,
616                 u_long data_requested,
617                 u_long max_number_of_entries,
618                 size_t data_size,
619                 int (*data_destructor) (struct data_scroller *, void *))
620 {
621     scrlr->data_granted = 0;
622     scrlr->data_created = 0;
623     scrlr->data_total_number = 0;
624     scrlr->first_data_ptr =
625         scrlr->last_data_ptr = scrlr->current_data_ptr = NULL;
626 
627     scrlr->max_number_of_entries = max_number_of_entries;
628     scrlr->data_size = data_size;
629 
630     scrlr->data_destructor = data_destructor;
631 
632     ROWDATAAPI_set_size(scrlr, data_requested, 0);
633 
634     return 0;
635 }
636 
637 static int
delete_data_entry(SCROLLER_T * scrlr,void * delete_me)638 delete_data_entry(SCROLLER_T * scrlr, void *delete_me)
639 {
640     NEXTED_PTR_T   *data_ptr = delete_me;
641     register NEXTED_PTR_T *tmp;
642 
643     if (data_ptr == scrlr->first_data_ptr) {
644         scrlr->first_data_ptr = data_ptr->next;
645         if (data_ptr == scrlr->last_data_ptr)
646             scrlr->last_data_ptr = NULL;
647     } else {                    /* not first */
648         for (tmp = scrlr->first_data_ptr; tmp; tmp = tmp->next) {
649             if (tmp->next == data_ptr) {
650                 if (data_ptr == scrlr->last_data_ptr)
651                     scrlr->last_data_ptr = tmp;
652                 tmp->next = data_ptr->next;
653                 break;
654             }
655         }                       /* for */
656     }                           /* not first */
657 
658     if (data_ptr == scrlr->current_data_ptr)
659         scrlr->current_data_ptr = data_ptr->next;
660 
661     if (scrlr->data_destructor)
662         scrlr->data_destructor(scrlr, data_ptr);
663     AGFREE(data_ptr);
664     scrlr->data_created--;
665     scrlr->data_stored--;
666 
667     return 0;
668 }
669 
670 static void
realloc_number_of_data(SCROLLER_T * scrlr,long dlong)671 realloc_number_of_data(SCROLLER_T * scrlr, long dlong)
672 {
673     void           *bptr;       /* DATA_ENTRY_T */
674     NEXTED_PTR_T   *prev = NULL;
675     void           *first = NULL;
676 
677     if (dlong > 0) {
678         for (; dlong; dlong--, prev = bptr, scrlr->data_created++) {
679             bptr = AGMALLOC(scrlr->data_size);
680             if (!bptr) {
681                 ag_trace("Err: no memory for data");
682                 break;
683             }
684             memset(bptr, 0, scrlr->data_size);
685             if (prev)
686                 prev->next = bptr;
687             else
688                 first = bptr;
689         }                       /* of loop by malloc bucket */
690 
691         if (!scrlr->current_data_ptr)
692             scrlr->current_data_ptr = first;
693         if (scrlr->last_data_ptr) {
694             scrlr->last_data_ptr->next = first;
695         } else
696             scrlr->first_data_ptr = first;
697 
698         scrlr->last_data_ptr = bptr;
699 
700     } else {
701         for (; dlong && scrlr->data_created > 0; dlong++) {
702             if (scrlr->current_data_ptr)
703                 delete_data_entry(scrlr, scrlr->current_data_ptr);
704             else
705                 delete_data_entry(scrlr, scrlr->first_data_ptr);
706         }
707     }
708 }
709 
710 void
ROWDATAAPI_set_size(SCROLLER_T * scrlr,u_long data_requested,u_char do_allocation)711 ROWDATAAPI_set_size(SCROLLER_T * scrlr,
712                     u_long data_requested, u_char do_allocation)
713 {
714     long            dlong;
715 
716     scrlr->data_requested = data_requested;
717     scrlr->data_granted = (data_requested < scrlr->max_number_of_entries) ?
718         data_requested : scrlr->max_number_of_entries;
719     if (do_allocation) {
720         dlong = (long) scrlr->data_granted - (long) scrlr->data_created;
721         realloc_number_of_data(scrlr, dlong);
722     }
723 }
724 
725 void
ROWDATAAPI_descructor(SCROLLER_T * scrlr)726 ROWDATAAPI_descructor(SCROLLER_T * scrlr)
727 {
728     register NEXTED_PTR_T *bptr;
729     register void  *next;
730 
731     for (bptr = scrlr->first_data_ptr; bptr; bptr = next) {
732         next = bptr->next;
733         if (scrlr->data_destructor)
734             scrlr->data_destructor(scrlr, bptr);
735         AGFREE(bptr);
736     }
737     scrlr->data_created = 0;
738     scrlr->data_granted = 0;
739     scrlr->first_data_ptr =
740         scrlr->last_data_ptr = scrlr->current_data_ptr = NULL;
741 }
742 
743 void           *
ROWDATAAPI_locate_new_data(SCROLLER_T * scrlr)744 ROWDATAAPI_locate_new_data(SCROLLER_T * scrlr)
745 {
746     register NEXTED_PTR_T *bptr;
747 
748     if (!scrlr->current_data_ptr) {     /* there was wrap */
749         bptr = scrlr->first_data_ptr;
750         if (!bptr) {
751             ag_trace("Err: SCROLLER_T:locate_new_data: internal error :(");
752             return NULL;
753         }
754         scrlr->first_data_ptr = bptr->next;
755         scrlr->last_data_ptr->next = bptr;
756         scrlr->last_data_ptr = (NEXTED_PTR_T *) bptr;
757         bptr->next = NULL;
758     } else {
759         bptr = scrlr->current_data_ptr;
760         scrlr->current_data_ptr = bptr->next;
761         ++scrlr->data_stored;
762     }
763 
764     scrlr->data_total_number++;
765 
766     return bptr;
767 }
768 
769 u_long
ROWDATAAPI_get_total_number(SCROLLER_T * scrlr)770 ROWDATAAPI_get_total_number(SCROLLER_T * scrlr)
771 {
772     return scrlr->data_total_number;
773 }
774 
775 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)776 ROWDATAAPI_header_DataEntry(struct variable * vp, oid * name,
777                             size_t * length, int exact,
778                             size_t * var_len,
779                             TABLE_DEFINTION_T * table_ptr,
780                             SCROLLER_T * (*extract_scroller) (void *body),
781                             size_t data_size, void *entry_ptr)
782 {
783     long            ctrl_indx, data_index;
784     RMON_ENTRY_T   *hdr = NULL;
785     SCROLLER_T     *scrlr;
786     NEXTED_PTR_T   *bptr = NULL;
787     register u_long iii;
788 
789     if (0 != AGUTIL_advance_index_name(vp, name, length, exact)) {
790         ag_trace("cannot advance_index_name");
791         return NULL;
792     }
793 
794     ctrl_indx = vp->namelen >= *length ? 0 : name[vp->namelen];
795     if (ctrl_indx)
796         data_index =
797             ((int)(vp->namelen + 1) >= (int)*length) ? 0 : name[vp->namelen + 1];
798     else
799         data_index = 0;
800 
801     if (exact) {
802         if (ctrl_indx && data_index) {
803             hdr = ROWAPI_find(table_ptr, ctrl_indx);
804             if (hdr) {
805                 scrlr = extract_scroller(hdr->body);
806                 bptr = scrlr->first_data_ptr;
807                 for (iii = 0; iii < scrlr->data_stored && bptr;
808                      iii++, bptr = bptr->next) {
809                     if ((long)bptr->data_index == data_index)
810                         break;
811                 }
812                 if (!bptr)
813                     hdr = NULL;
814             }
815         }
816     } else {
817         if (ctrl_indx)
818             hdr = ROWAPI_find(table_ptr, ctrl_indx);
819         else
820             hdr = ROWAPI_first(table_ptr);
821 
822         if (hdr) {
823             scrlr = extract_scroller(hdr->body);
824             /*
825              * ag_trace ("get next after (%d %d)", (int) ctrl_indx, (int) data_index);
826              */
827             bptr = scrlr->first_data_ptr;
828             for (iii = 0; iii < scrlr->data_stored && bptr;
829                  iii++, bptr = bptr->next) {
830                 if (bptr->data_index && (long)bptr->data_index > data_index)
831                     break;
832             }
833 
834             if (bptr && (long)bptr->data_index <= data_index)
835                 bptr = NULL;
836 
837             if (!bptr) {        /* travel to next row */
838                 /*
839                  * ag_trace ("Dbg: travel to next row");
840                  */
841                 for (hdr = hdr->next; hdr; hdr = hdr->next) {
842                     if (RMON1_ENTRY_VALID != hdr->status)
843                         continue;
844 
845                     scrlr = extract_scroller(hdr->body);
846                     if (scrlr->data_stored <= 0)
847                         continue;
848                     for (bptr = scrlr->first_data_ptr; bptr;
849                          bptr = bptr->next) {
850                         if (bptr->data_index)
851                             break;
852                     }
853 
854                     if (bptr)
855                         break;
856                 }
857             }
858             if (bptr) {         /* set new index */
859                 /*
860                  * ag_trace ("Dbg: So (%d %d)", (int) hdr->index, (int) bptr->data_index);
861                  */
862                 name[vp->namelen] = hdr->ctrl_index;
863                 name[vp->namelen + 1] = bptr->data_index;
864                 *length = vp->namelen + 2;
865             } else
866                 hdr = NULL;
867         }
868     }
869 
870     if (hdr)
871         memcpy(entry_ptr, bptr, data_size);
872     return hdr;
873 }
874 
875 void
init_rows(void)876 init_rows(void)
877 {
878 }
879