http://www.usr.com/support/gpl/USR9107_release1.1.tar.gz
[bcm963xx.git] / userapps / opensource / net-snmp / agent / object_monitor.c
1 /*
2  * object_monitor.c
3  *
4  * $Id: object_monitor.c,v 1.6.2.1 2003/03/14 17:37:07 rstory Exp $
5  *
6  * functions and data structures for cooperating code to monitor objects.
7  *
8  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
9  * WARNING!                                                       WARNING!
10  * WARNING!                                                       WARNING!
11  * WARNING!         This code is under active development         WARNING!
12  * WARNING!         and is subject to change at any time.         WARNING!
13  * WARNING!                                                       WARNING!
14  * WARNING!                                                       WARNING!
15  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
16  */
17
18 #include <net-snmp/net-snmp-config.h>
19 #include <net-snmp/net-snmp-includes.h>
20 #include <net-snmp/agent/net-snmp-agent-includes.h>
21 #include <net-snmp/library/container.h>
22 #include <net-snmp/library/snmp_assert.h>
23
24 #include "net-snmp/agent/object_monitor.h"
25
26 #if ! defined TRUE
27 #  define TRUE 1
28 #elsif TRUE != 1
29 error "TRUE != 1"
30 #endif
31 /**************************************************************************
32  *
33  * Private data structures
34  *
35  **************************************************************************/
36     /*
37      * individual callback info for an object
38      */
39     typedef struct monitor_info_s {
40
41    /** priority for this callback */
42     int             priority;
43
44    /** handler that registred to watch this object */
45     netsnmp_mib_handler *watcher;
46
47    /** events that the watcher cares about */
48     unsigned int    events;
49
50    /** callback function */
51     netsnmp_object_monitor_callback *cb;
52
53    /** pointer to data from the watcher */
54     void           *watcher_data;
55
56     struct monitor_info_s *next;
57
58 } monitor_info;
59
60 /*
61  * list of watchers for a given object
62  */
63 typedef struct watcher_list_s {
64
65    /** netsnmp_index must be first! */
66     netsnmp_index  monitored_object;
67
68     monitor_info   *head;
69
70 } watcher_list;
71
72 /*
73  * temp holder for ordered list of callbacks
74  */
75 typedef struct callback_placeholder_s {
76
77     monitor_info   *mi;
78     netsnmp_monitor_callback_header *cbh;
79
80     struct callback_placeholder_s *next;
81
82 } callback_placeholder;
83
84
85 /**************************************************************************
86  *
87  * 
88  *
89  **************************************************************************/
90
91 /*
92  * local statics
93  */
94 static char     need_init = 1;
95 static netsnmp_container *monitored_objects = NULL;
96 static netsnmp_monitor_callback_header *callback_pending_list;
97 static callback_placeholder *callback_ready_list;
98
99 /*
100  * local prototypes
101  */
102 static watcher_list *find_watchers(oid * object, size_t oid_len);
103 static int      insert_watcher(oid *, size_t, monitor_info *);
104 static int      check_registered(unsigned int event, oid * o, int o_l,
105                                  watcher_list ** pWl, monitor_info ** pMi);
106 static void     move_pending_to_ready(void);
107
108
109 /**************************************************************************
110  *
111  * Public functions
112  *
113  **************************************************************************/
114
115 /*
116  * 
117  */
118 void
119 netsnmp_monitor_init(void)
120 {
121     if (!need_init)
122         return;
123
124     callback_pending_list = NULL;
125     callback_ready_list = NULL;
126
127     monitored_objects = netsnmp_container_get("object_monitor:binary_array");
128     if (NULL != monitored_objects)
129         need_init = 0;
130     monitored_objects->compare = netsnmp_compare_netsnmp_index;
131     monitored_objects->ncompare = netsnmp_ncompare_netsnmp_index;
132     
133     return;
134 }
135
136
137 /**************************************************************************
138  *
139  * Registration functions
140  *
141  **************************************************************************/
142
143 /**
144  * Register a callback for the specified object.
145  *
146  * @param object  pointer to the OID of the object to monitor.
147  * @param oid_len length of the OID pointed to by object.
148  * @param priority the priority to associate with this callback. A
149  *                 higher number indicates higher priority. This
150  *                 allows multiple callbacks for the same object to
151  *                 coordinate the order in which they are called. If
152  *                 two callbacks register with the same priority, the
153  *                 order is undefined.
154  * @param events  the events which the callback is interested in.
155  * @param watcher_data pointer to data that will be supplied to the
156  *                     callback method when an event occurs.
157  * @param cb pointer to the function to be called when an event occurs.
158  *
159  * NOTE: the combination of the object, priority and watcher_data must
160  *       be unique, as they are the parameters for unregistering a
161  *       callback.
162  *
163  * @return SNMPERR_NOERR registration was successful
164  * @return SNMPERR_MALLOC memory allocation failed
165  * @return SNMPERR_VALUE the combination of the object, priority and
166  *                        watcher_data is not unique.
167  */
168 int
169 netsnmp_monitor_register(oid * object, size_t oid_len, int priority,
170                          unsigned int events, void *watcher_data,
171                          netsnmp_object_monitor_callback * cb)
172 {
173     monitor_info   *mi;
174     int             rc;
175
176     netsnmp_assert(need_init == 0);
177
178     mi = calloc(1, sizeof(monitor_info));
179     if (NULL == mi)
180         return SNMPERR_MALLOC;
181
182     mi->priority = priority;
183     mi->events = events;
184     mi->watcher_data = watcher_data;
185     mi->cb = cb;
186
187     rc = insert_watcher(object, oid_len, mi);
188     if (rc != SNMPERR_SUCCESS)
189         free(mi);
190
191     return rc;
192 }
193
194 /**
195  * Unregister a callback for the specified object.
196  *
197  * @param object  pointer to the OID of the object to monitor.
198  * @param oid_len length of the OID pointed to by object.
199  * @param priority the priority to associate with this callback.
200  * @param watcher_data pointer to data that was supplied when the
201  *                     callback was registered.
202  * @param cb pointer to the function to be called when an event occurs.
203  */
204 int
205 netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority,
206                            void *wd, netsnmp_object_monitor_callback * cb)
207 {
208     monitor_info   *mi, *last;
209
210     watcher_list   *wl = find_watchers(object, oid_len);
211     if (NULL == wl)
212         return SNMPERR_GENERR;
213
214     last = NULL;
215     mi = wl->head;
216     while (mi) {
217         if ((mi->cb == cb) && (mi->priority == priority) &&
218             (mi->watcher_data == wd))
219             break;
220         last = mi;
221         mi = mi->next;
222     }
223
224     if (NULL == mi)
225         return SNMPERR_GENERR;
226
227     if (NULL == last)
228         wl->head = mi->next;
229     else
230         last->next = mi->next;
231
232     if (NULL == wl->head) {
233         CONTAINER_REMOVE(monitored_objects, wl);
234         free(wl->monitored_object.oids);
235         free(wl);
236     }
237
238     free(mi);
239
240     return SNMPERR_SUCCESS;
241 }
242
243 /**************************************************************************
244  *
245  * object monitor functions
246  *
247  **************************************************************************/
248
249 /**
250  * Notifies the object monitor of an event.
251  *
252  * The object monitor funtions will save the callback information
253  * until all varbinds in the current PDU have been processed and
254  * a response has been sent. At that time, the object monitor will
255  * determine if there are any watchers monitoring for the event.
256  *
257  * NOTE: the actual type of the callback structure may vary. The
258  *       object monitor functions require only that the start of
259  *       the structure match the netsnmp_monitor_callback_header
260  *       structure. It is up to the watcher and monitored objects
261  *       to agree on the format of other data.
262  *
263  * @param cbh pointer to a callback header.
264  */
265 void
266 netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh)
267 {
268
269     netsnmp_assert(need_init == 0);
270
271     /*
272      * put processing of until response has been sent
273      */
274     cbh->private = callback_pending_list;
275     callback_pending_list = cbh;
276
277     return;
278 }
279
280 /**
281  * check to see if a registration exists for an object/event combination
282  *
283  * @param event the event type to check for
284  * @param o     the oid to check for
285  * @param o_l   the length of the oid
286  *
287  * @returns TRUE(1) if a callback is registerd
288  * @returns FALSE(0) if no callback is registered
289  */
290 int
291 netsnmp_monitor_check_registered(int event, oid * o, int o_l)
292 {
293     return check_registered(event, o, o_l, NULL, NULL);
294 }
295
296 /**
297  * Process all pending callbacks
298  *
299  * NOTE: this method is not in the public header, as it should only be
300  *       called in one place, in the agent.
301  */
302 void
303 netsnmp_monitor_process_callbacks(void)
304 {
305     netsnmp_assert(need_init == 0);
306     netsnmp_assert(NULL == callback_ready_list);
307
308     if (NULL == callback_pending_list) {
309         DEBUGMSGT(("object_monitor", "No callbacks to process"));
310         return;
311     }
312
313     DEBUGMSG(("object_monitor", "Checking for registered " "callbacks."));
314
315     /*
316      * move an pending notification which has a registered watcher to the
317      * ready list. Free any other notifications.
318      */
319     move_pending_to_ready();
320
321     /*
322      * call callbacks
323      */
324     while (callback_ready_list) {
325
326         /*
327          * pop off the first item
328          */
329         callback_placeholder *current_cbr;
330         current_cbr = callback_ready_list;
331         callback_ready_list = current_cbr->next;
332
333         /*
334          * setup, then call callback
335          */
336         current_cbr->cbh->watcher_data = current_cbr->mi->watcher_data;
337         current_cbr->cbh->priority = current_cbr->mi->priority;
338         (*current_cbr->mi->cb) (current_cbr->cbh);
339
340         /*
341          * release memory (don't free current_cbr->mi)
342          */
343         if (--(current_cbr->cbh->refs) == 0) {
344             free(current_cbr->cbh->monitored_object.oids);
345             free(current_cbr->cbh);
346         }
347         free(current_cbr);
348
349         /*
350          * check for any new pending notifications
351          */
352         move_pending_to_ready();
353
354     }
355
356     netsnmp_assert(callback_ready_list == NULL);
357     netsnmp_assert(callback_pending_list = NULL);
358
359     return;
360 }
361
362 /**************************************************************************
363  *
364  * COOPERATIVE helpers
365  *
366  **************************************************************************/
367 /**
368  * Notifies the object monitor of a cooperative event.
369  *
370  * This convenience function will build a
371  * @link netsnmp_monitor_callback_header@endlink and call
372  * @link netsnmp_notify_monitor@endlink.
373  *
374  * @param event the event type
375  * @param object pointer to the oid of the object sending the event
376  * @param len    the lenght of the oid
377  * @param oid_steal set to true if the function may keep the pointer
378  *                  to the memory (and free it later). set to false
379  *                  to make the function allocate and copy the oid.
380  * @param object_info pointer to data supplied by the object for
381  *                    the callback. This pointer must remain valid,
382  *                    will be provided to each callback registered
383  *                    for the object (i.e. it will not be copied or
384  *                    freed).
385  */
386 void
387 netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal,
388                            void *object_info)
389 {
390     netsnmp_monitor_callback_cooperative *cbh;
391
392     netsnmp_assert(need_init == 0);
393
394     cbh = SNMP_MALLOC_TYPEDEF(netsnmp_monitor_callback_cooperative);
395     if (NULL == cbh) {
396         snmp_log(LOG_ERR, "could not allocate memory for "
397                  "cooperative callback");
398         return;
399     }
400
401     cbh->hdr.event = event;
402     cbh->hdr.object_info = object_info;
403     cbh->hdr.monitored_object.len = o_len;
404
405     if (o_steal) {
406         cbh->hdr.monitored_object.oids = o;
407     } else {
408         cbh->hdr.monitored_object.oids = snmp_duplicate_objid(o, o_len);
409     }
410
411     netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh);
412 }
413
414 #ifndef DOXYGEN_SHOULD_IGNORE_THIS
415 /*************************************************************************
416  *************************************************************************
417  *************************************************************************
418  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
419  * WARNING!                                                       WARNING!
420  * WARNING!                                                       WARNING!
421  * WARNING!         This code is under active development         WARNING!
422  * WARNING!         and is subject to change at any time.         WARNING!
423  * WARNING!                                                       WARNING!
424  * WARNING!                                                       WARNING!
425  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
426  *************************************************************************
427  *************************************************************************
428  *************************************************************************
429  */
430 static watcher_list *
431 find_watchers(oid * object, size_t oid_len)
432 {
433     netsnmp_index oah;
434
435     oah.oids = object;
436     oah.len = oid_len;
437
438     return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah);
439 }
440
441 static int
442 insert_watcher(oid * object, size_t oid_len, monitor_info * mi)
443 {
444     watcher_list   *wl = find_watchers(object, oid_len);
445     int             rc = SNMPERR_SUCCESS;
446
447     if (NULL != wl) {
448
449         monitor_info   *last, *current;
450
451         netsnmp_assert(wl->head != NULL);
452
453         last = NULL;
454         current = wl->head;
455         while (current) {
456             if (mi->priority == current->priority) {
457                 /*
458                  * check for duplicate
459                  */
460                 if (mi->watcher_data == current->watcher_data)
461                     return SNMPERR_VALUE; /** duplicate! */
462             } else if (mi->priority > current->priority) {
463                 break;
464             }
465             last = current;
466             current = current->next;
467         }
468         if (NULL == last) {
469             mi->next = wl->head;
470             wl->head = mi;
471         } else {
472             mi->next = last->next;
473             last->next = mi;
474         }
475     } else {
476
477         /*
478          * first watcher for this oid; set up list
479          */
480         wl = SNMP_MALLOC_TYPEDEF(watcher_list);
481         if (NULL == wl)
482             return SNMPERR_MALLOC;
483
484         /*
485          * copy index oid
486          */
487         wl->monitored_object.len = oid_len;
488         wl->monitored_object.oids = malloc(sizeof(oid) * oid_len);
489         if (NULL == wl->monitored_object.oids) {
490             free(wl);
491             return SNMPERR_MALLOC;
492         }
493         memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len);
494
495         /*
496          * add watcher, and insert into array
497          */
498         wl->head = mi;
499         mi->next = NULL;
500         rc = CONTAINER_INSERT(monitored_objects, wl);
501         if (rc) {
502             free(wl->monitored_object.oids);
503             free(wl);
504             return rc;
505         }
506     }
507     return rc;
508 }
509
510 /**
511  * @internal
512  * check to see if a registration exists for an object/event combination
513  *
514  * @param event the event type to check for
515  * @param o     the oid to check for
516  * @param o_l   the length of the oid
517  * @param pWl   if a pointer to a watcher_list pointer is supplied,
518  *              upon return the watcher list pointer will be set to
519  *              the watcher list for the object, or NULL if there are
520  *              no watchers for the object.
521  * @param pMi   if a pointer to a monitor_info pointer is supplied,
522  *              upon return the pointer will be set to the first
523  *              monitor_info object for the specified event.
524  *
525  * @returns TRUE(1) if a callback is registerd
526  * @returns FALSE(0) if no callback is registered
527  */
528 static int
529 check_registered(unsigned int event, oid * o, int o_l,
530                  watcher_list ** pWl, monitor_info ** pMi)
531 {
532     watcher_list   *wl;
533     monitor_info   *mi;
534
535     netsnmp_assert(need_init == 0);
536
537     /*
538      * check to see if anyone has registered for callbacks
539      * for the object.
540      */
541     wl = find_watchers(o, o_l);
542     if (pWl)
543         *pWl = wl;
544     if (NULL == wl)
545         return 0;
546
547     /*
548      * check if any watchers are watching for this specific event
549      */
550     for (mi = wl->head; mi; mi = mi->next) {
551
552         if (mi->events & event) {
553             if (pMi)
554                 *pMi = mi;
555             return TRUE;
556         }
557     }
558
559     return 0;
560 }
561
562 /**
563  *@internal
564  */
565 NETSNMP_INLINE void
566 insert_ready(callback_placeholder * new_cbr)
567 {
568     callback_placeholder *current_cbr, *last_cbr;
569
570     /*
571      * insert in callback ready list
572      */
573     last_cbr = NULL;
574     current_cbr = callback_ready_list;
575     while (current_cbr) {
576
577         if (new_cbr->mi->priority > current_cbr->mi->priority)
578             break;
579
580         last_cbr = current_cbr;
581         current_cbr = current_cbr->next;
582     }
583     if (NULL == last_cbr) {
584         new_cbr->next = callback_ready_list;
585         callback_ready_list = new_cbr;
586     } else {
587         new_cbr->next = last_cbr->next;
588         last_cbr->next = new_cbr;
589     }
590 }
591
592 /**
593  *@internal
594  *
595  * move an pending notification which has a registered watcher to the
596  * ready list. Free any other notifications.
597  */
598 static void
599 move_pending_to_ready(void)
600 {
601     /*
602      * check to see if anyone has registered for callbacks
603      * for each object.
604      */
605     while (callback_pending_list) {
606
607         watcher_list   *wl;
608         monitor_info   *mi;
609         netsnmp_monitor_callback_header *cbp;
610
611         /*
612          * pop off first item
613          */
614         cbp = callback_pending_list;
615         callback_pending_list = cbp->private; /** next */
616
617         if (0 == check_registered(cbp->event, cbp->monitored_object.oids,
618                                   cbp->monitored_object.len, &wl,
619                                   &mi)) {
620
621             /*
622              * nobody watching, free memory
623              */
624             free(cbp);
625             continue;
626         }
627
628         /*
629          * Found at least one; check the rest of the list and
630          * save callback for processing
631          */
632         for (; mi; mi = mi->next) {
633
634             callback_placeholder *new_cbr;
635
636             if (0 == (mi->events & cbp->event))
637                 continue;
638
639             /*
640              * create temprory placeholder.
641              *
642              * I hate to allocate memory here, as I'd like this code to
643              * be fast and lean. But I don't have time to think of another
644              * solution os this will have to do for now.
645              *
646              * I need a list of monitor_info (mi) objects for each
647              * callback which has registered for the event, and want
648              * that list sorted by the priority required by the watcher.
649              */
650             new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder);
651             if (NULL == new_cbr) {
652                 snmp_log(LOG_ERR, "malloc failed, callback dropped.");
653                 continue;
654             }
655             new_cbr->cbh = cbp;
656             new_cbr->mi = mi;
657             ++cbp->refs;
658
659             /*
660              * insert in callback ready list
661              */
662             insert_ready(new_cbr);
663
664         } /** end mi loop */
665     } /** end cbp loop */
666
667     netsnmp_assert(callback_pending_list == NULL);
668 }
669
670
671 #if defined TESTING_OBJECT_MONITOR
672 /**************************************************************************
673  *
674  * (untested) TEST CODE
675  *
676  */
677 void
678 dummy_callback(netsnmp_monitor_callback_header * cbh)
679 {
680     printf("Callback received.\n");
681 }
682
683 void
684 dump_watchers(netsnmp_index *oah, void *)
685 {
686     watcher_list   *wl = (watcher_list *) oah;
687     netsnmp_monitor_callback_header *cbh = wl->head;
688
689     printf("Watcher List for OID ");
690     print_objid(wl->hdr->oids, wl->hdr->len);
691     printf("\n");
692
693     while (cbh) {
694
695         printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n",
696                cbh->priority, cbh->events, cbh->watcher_data);
697
698         cbh = cbh->private;
699     }
700 }
701
702 void
703 main(int argc, char **argv)
704 {
705
706     oid             object[3] = { 1, 3, 6 };
707     int             object_len = 3;
708     int             rc;
709
710     /*
711      * init
712      */
713     netsnmp_monitor_init();
714
715     /*
716      * insert an object
717      */
718     rc = netsnmp_monitor_register(object, object_len, 0,
719                                   EVENT_ROW_ADD, (void *) 0xdeadbeef,
720                                   dummy_callback);
721     printf("insert an object: %d\n", rc);
722
723     /*
724      * insert same object, new priority
725      */
726     netsnmp_monitor_register(object, object_len, 10,
727                              EVENT_ROW_ADD, (void *) 0xdeadbeef,
728                              dummy_callback);
729     printf("insert same object, new priority: %d\n", rc);
730
731     /*
732      * insert same object, same priority, new data
733      */
734     netsnmp_monitor_register(object, object_len, 10,
735                              EVENT_ROW_ADD, (void *) 0xbeefdead,
736                              dummy_callback);
737     printf("insert same object, same priority, new data: %d\n", rc);
738
739     /*
740      * insert same object, same priority, same data
741      */
742     netsnmp_monitor_register(object, object_len, 10,
743                              EVENT_ROW_ADD, (void *) 0xbeefdead,
744                              dummy_callback);
745     printf("insert same object, same priority, new data: %d\n", rc);
746
747
748     /*
749      * dump table
750      */
751     CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL);
752 }
753 #endif /** defined TESTING_OBJECT_MONITOR */
754
755 #endif /** DOXYGEN_SHOULD_IGNORE_THIS */