fix outgoing QOS prios
[tomato.git] / release / src / router / snmp / agent / object_monitor.c
blobdb44765574e1d4d61a5fe8cce2c810c319e6fcaa
1 /*
2 * object_monitor.c
4 * $Id: object_monitor.c,v 1.1.2.1 2004/06/20 21:54:09 nikki Exp $
6 * functions and data structures for cooperating code to monitor objects.
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!
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>
24 #include "net-snmp/agent/object_monitor.h"
26 #if ! defined TRUE
27 # define TRUE 1
28 #elsif TRUE != 1
29 error "TRUE != 1"
30 #endif
31 /**************************************************************************
33 * Private data structures
35 **************************************************************************/
37 * individual callback info for an object
39 typedef struct monitor_info_s {
41 /** priority for this callback */
42 int priority;
44 /** handler that registred to watch this object */
45 netsnmp_mib_handler *watcher;
47 /** events that the watcher cares about */
48 unsigned int events;
50 /** callback function */
51 netsnmp_object_monitor_callback *cb;
53 /** pointer to data from the watcher */
54 void *watcher_data;
56 struct monitor_info_s *next;
58 } monitor_info;
61 * list of watchers for a given object
63 typedef struct watcher_list_s {
65 /** netsnmp_index must be first! */
66 netsnmp_index monitored_object;
68 monitor_info *head;
70 } watcher_list;
73 * temp holder for ordered list of callbacks
75 typedef struct callback_placeholder_s {
77 monitor_info *mi;
78 netsnmp_monitor_callback_header *cbh;
80 struct callback_placeholder_s *next;
82 } callback_placeholder;
85 /**************************************************************************
89 **************************************************************************/
92 * local statics
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;
100 * local prototypes
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);
109 /**************************************************************************
111 * Public functions
113 **************************************************************************/
118 void
119 netsnmp_monitor_init(void)
121 if (!need_init)
122 return;
124 callback_pending_list = NULL;
125 callback_ready_list = NULL;
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;
133 return;
137 /**************************************************************************
139 * Registration functions
141 **************************************************************************/
144 * Register a callback for the specified object.
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.
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.
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.
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)
173 monitor_info *mi;
174 int rc;
176 netsnmp_assert(need_init == 0);
178 mi = calloc(1, sizeof(monitor_info));
179 if (NULL == mi)
180 return SNMPERR_MALLOC;
182 mi->priority = priority;
183 mi->events = events;
184 mi->watcher_data = watcher_data;
185 mi->cb = cb;
187 rc = insert_watcher(object, oid_len, mi);
188 if (rc != SNMPERR_SUCCESS)
189 free(mi);
191 return rc;
195 * Unregister a callback for the specified object.
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.
205 netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority,
206 void *wd, netsnmp_object_monitor_callback * cb)
208 monitor_info *mi, *last;
210 watcher_list *wl = find_watchers(object, oid_len);
211 if (NULL == wl)
212 return SNMPERR_GENERR;
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;
224 if (NULL == mi)
225 return SNMPERR_GENERR;
227 if (NULL == last)
228 wl->head = mi->next;
229 else
230 last->next = mi->next;
232 if (NULL == wl->head) {
233 CONTAINER_REMOVE(monitored_objects, wl);
234 free(wl->monitored_object.oids);
235 free(wl);
238 free(mi);
240 return SNMPERR_SUCCESS;
243 /**************************************************************************
245 * object monitor functions
247 **************************************************************************/
250 * Notifies the object monitor of an event.
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.
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.
263 * @param cbh pointer to a callback header.
265 void
266 netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh)
269 netsnmp_assert(need_init == 0);
272 * put processing of until response has been sent
274 cbh->private = callback_pending_list;
275 callback_pending_list = cbh;
277 return;
281 * check to see if a registration exists for an object/event combination
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
287 * @returns TRUE(1) if a callback is registerd
288 * @returns FALSE(0) if no callback is registered
291 netsnmp_monitor_check_registered(int event, oid * o, int o_l)
293 return check_registered(event, o, o_l, NULL, NULL);
297 * Process all pending callbacks
299 * NOTE: this method is not in the public header, as it should only be
300 * called in one place, in the agent.
302 void
303 netsnmp_monitor_process_callbacks(void)
305 netsnmp_assert(need_init == 0);
306 netsnmp_assert(NULL == callback_ready_list);
308 if (NULL == callback_pending_list) {
309 DEBUGMSGT(("object_monitor", "No callbacks to process"));
310 return;
313 DEBUGMSG(("object_monitor", "Checking for registered " "callbacks."));
316 * move an pending notification which has a registered watcher to the
317 * ready list. Free any other notifications.
319 move_pending_to_ready();
322 * call callbacks
324 while (callback_ready_list) {
327 * pop off the first item
329 callback_placeholder *current_cbr;
330 current_cbr = callback_ready_list;
331 callback_ready_list = current_cbr->next;
334 * setup, then call callback
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);
341 * release memory (don't free current_cbr->mi)
343 if (--(current_cbr->cbh->refs) == 0) {
344 free(current_cbr->cbh->monitored_object.oids);
345 free(current_cbr->cbh);
347 free(current_cbr);
350 * check for any new pending notifications
352 move_pending_to_ready();
356 netsnmp_assert(callback_ready_list == NULL);
357 netsnmp_assert(callback_pending_list = NULL);
359 return;
362 /**************************************************************************
364 * COOPERATIVE helpers
366 **************************************************************************/
368 * Notifies the object monitor of a cooperative event.
370 * This convenience function will build a
371 * @link netsnmp_monitor_callback_header@endlink and call
372 * @link netsnmp_notify_monitor@endlink.
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).
386 void
387 netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal,
388 void *object_info)
390 netsnmp_monitor_callback_cooperative *cbh;
392 netsnmp_assert(need_init == 0);
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;
401 cbh->hdr.event = event;
402 cbh->hdr.object_info = object_info;
403 cbh->hdr.monitored_object.len = o_len;
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);
411 netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh);
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 *************************************************************************
430 static watcher_list *
431 find_watchers(oid * object, size_t oid_len)
433 netsnmp_index oah;
435 oah.oids = object;
436 oah.len = oid_len;
438 return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah);
441 static int
442 insert_watcher(oid * object, size_t oid_len, monitor_info * mi)
444 watcher_list *wl = find_watchers(object, oid_len);
445 int rc = SNMPERR_SUCCESS;
447 if (NULL != wl) {
449 monitor_info *last, *current;
451 netsnmp_assert(wl->head != NULL);
453 last = NULL;
454 current = wl->head;
455 while (current) {
456 if (mi->priority == current->priority) {
458 * check for duplicate
460 if (mi->watcher_data == current->watcher_data)
461 return SNMPERR_VALUE; /** duplicate! */
462 } else if (mi->priority > current->priority) {
463 break;
465 last = current;
466 current = current->next;
468 if (NULL == last) {
469 mi->next = wl->head;
470 wl->head = mi;
471 } else {
472 mi->next = last->next;
473 last->next = mi;
475 } else {
478 * first watcher for this oid; set up list
480 wl = SNMP_MALLOC_TYPEDEF(watcher_list);
481 if (NULL == wl)
482 return SNMPERR_MALLOC;
485 * copy index oid
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;
493 memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len);
496 * add watcher, and insert into array
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;
507 return rc;
511 * @internal
512 * check to see if a registration exists for an object/event combination
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.
525 * @returns TRUE(1) if a callback is registerd
526 * @returns FALSE(0) if no callback is registered
528 static int
529 check_registered(unsigned int event, oid * o, int o_l,
530 watcher_list ** pWl, monitor_info ** pMi)
532 watcher_list *wl;
533 monitor_info *mi;
535 netsnmp_assert(need_init == 0);
538 * check to see if anyone has registered for callbacks
539 * for the object.
541 wl = find_watchers(o, o_l);
542 if (pWl)
543 *pWl = wl;
544 if (NULL == wl)
545 return 0;
548 * check if any watchers are watching for this specific event
550 for (mi = wl->head; mi; mi = mi->next) {
552 if (mi->events & event) {
553 if (pMi)
554 *pMi = mi;
555 return TRUE;
559 return 0;
563 *@internal
565 NETSNMP_INLINE void
566 insert_ready(callback_placeholder * new_cbr)
568 callback_placeholder *current_cbr, *last_cbr;
571 * insert in callback ready list
573 last_cbr = NULL;
574 current_cbr = callback_ready_list;
575 while (current_cbr) {
577 if (new_cbr->mi->priority > current_cbr->mi->priority)
578 break;
580 last_cbr = current_cbr;
581 current_cbr = current_cbr->next;
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;
593 *@internal
595 * move an pending notification which has a registered watcher to the
596 * ready list. Free any other notifications.
598 static void
599 move_pending_to_ready(void)
602 * check to see if anyone has registered for callbacks
603 * for each object.
605 while (callback_pending_list) {
607 watcher_list *wl;
608 monitor_info *mi;
609 netsnmp_monitor_callback_header *cbp;
612 * pop off first item
614 cbp = callback_pending_list;
615 callback_pending_list = cbp->private; /** next */
617 if (0 == check_registered(cbp->event, cbp->monitored_object.oids,
618 cbp->monitored_object.len, &wl,
619 &mi)) {
622 * nobody watching, free memory
624 free(cbp);
625 continue;
629 * Found at least one; check the rest of the list and
630 * save callback for processing
632 for (; mi; mi = mi->next) {
634 callback_placeholder *new_cbr;
636 if (0 == (mi->events & cbp->event))
637 continue;
640 * create temprory placeholder.
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.
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.
650 new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder);
651 if (NULL == new_cbr) {
652 snmp_log(LOG_ERR, "malloc failed, callback dropped.");
653 continue;
655 new_cbr->cbh = cbp;
656 new_cbr->mi = mi;
657 ++cbp->refs;
660 * insert in callback ready list
662 insert_ready(new_cbr);
664 } /** end mi loop */
665 } /** end cbp loop */
667 netsnmp_assert(callback_pending_list == NULL);
671 #if defined TESTING_OBJECT_MONITOR
672 /**************************************************************************
674 * (untested) TEST CODE
677 void
678 dummy_callback(netsnmp_monitor_callback_header * cbh)
680 printf("Callback received.\n");
683 void
684 dump_watchers(netsnmp_index *oah, void *)
686 watcher_list *wl = (watcher_list *) oah;
687 netsnmp_monitor_callback_header *cbh = wl->head;
689 printf("Watcher List for OID ");
690 print_objid(wl->hdr->oids, wl->hdr->len);
691 printf("\n");
693 while (cbh) {
695 printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n",
696 cbh->priority, cbh->events, cbh->watcher_data);
698 cbh = cbh->private;
702 void
703 main(int argc, char **argv)
706 oid object[3] = { 1, 3, 6 };
707 int object_len = 3;
708 int rc;
711 * init
713 netsnmp_monitor_init();
716 * insert an object
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);
724 * insert same object, new priority
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);
732 * insert same object, same priority, new data
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);
740 * insert same object, same priority, same data
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);
749 * dump table
751 CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL);
753 #endif /** defined TESTING_OBJECT_MONITOR */
755 #endif /** DOXYGEN_SHOULD_IGNORE_THIS */