net-snmp 5.7
|
00001 /* 00002 * object_monitor.c 00003 * 00004 * $Id$ 00005 * 00006 * functions and data structures for cooperating code to monitor objects. 00007 * 00008 * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 00009 * WARNING! WARNING! 00010 * WARNING! WARNING! 00011 * WARNING! This code is under active development WARNING! 00012 * WARNING! and is subject to change at any time. WARNING! 00013 * WARNING! WARNING! 00014 * WARNING! WARNING! 00015 * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 00016 */ 00017 00018 #include <net-snmp/net-snmp-config.h> 00019 #include <net-snmp/net-snmp-includes.h> 00020 #include <net-snmp/agent/net-snmp-agent-includes.h> 00021 #include <net-snmp/library/container.h> 00022 #include <net-snmp/library/snmp_assert.h> 00023 00024 #include "net-snmp/agent/object_monitor.h" 00025 00026 #if ! defined TRUE 00027 # define TRUE 1 00028 #elsif TRUE != 1 00029 error "TRUE != 1" 00030 #endif 00031 /************************************************************************** 00032 * 00033 * Private data structures 00034 * 00035 **************************************************************************/ 00036 /* 00037 * individual callback info for an object 00038 */ 00039 typedef struct monitor_info_s { 00040 00042 int priority; 00043 00045 netsnmp_mib_handler *watcher; 00046 00048 unsigned int events; 00049 00051 netsnmp_object_monitor_callback *cb; 00052 00054 void *watcher_data; 00055 00056 struct monitor_info_s *next; 00057 00058 } monitor_info; 00059 00060 /* 00061 * list of watchers for a given object 00062 */ 00063 typedef struct watcher_list_s { 00064 00066 netsnmp_index monitored_object; 00067 00068 monitor_info *head; 00069 00070 } watcher_list; 00071 00072 /* 00073 * temp holder for ordered list of callbacks 00074 */ 00075 typedef struct callback_placeholder_s { 00076 00077 monitor_info *mi; 00078 netsnmp_monitor_callback_header *cbh; 00079 00080 struct callback_placeholder_s *next; 00081 00082 } callback_placeholder; 00083 00084 00085 /************************************************************************** 00086 * 00087 * 00088 * 00089 **************************************************************************/ 00090 00091 /* 00092 * local statics 00093 */ 00094 static char need_init = 1; 00095 static netsnmp_container *monitored_objects = NULL; 00096 static netsnmp_monitor_callback_header *callback_pending_list; 00097 static callback_placeholder *callback_ready_list; 00098 00099 /* 00100 * local prototypes 00101 */ 00102 static watcher_list *find_watchers(oid * object, size_t oid_len); 00103 static int insert_watcher(oid *, size_t, monitor_info *); 00104 static int check_registered(unsigned int event, oid * o, int o_l, 00105 watcher_list ** pWl, monitor_info ** pMi); 00106 static void move_pending_to_ready(void); 00107 00108 00109 /************************************************************************** 00110 * 00111 * Public functions 00112 * 00113 **************************************************************************/ 00114 00115 /* 00116 * 00117 */ 00118 void 00119 netsnmp_monitor_init(void) 00120 { 00121 if (!need_init) 00122 return; 00123 00124 callback_pending_list = NULL; 00125 callback_ready_list = NULL; 00126 00127 monitored_objects = netsnmp_container_get("object_monitor:binary_array"); 00128 if (NULL != monitored_objects) 00129 need_init = 0; 00130 monitored_objects->compare = netsnmp_compare_netsnmp_index; 00131 monitored_objects->ncompare = netsnmp_ncompare_netsnmp_index; 00132 00133 return; 00134 } 00135 00136 00137 /************************************************************************** 00138 * 00139 * Registration functions 00140 * 00141 **************************************************************************/ 00142 00168 int 00169 netsnmp_monitor_register(oid * object, size_t oid_len, int priority, 00170 unsigned int events, void *watcher_data, 00171 netsnmp_object_monitor_callback * cb) 00172 { 00173 monitor_info *mi; 00174 int rc; 00175 00176 netsnmp_assert(need_init == 0); 00177 00178 mi = calloc(1, sizeof(monitor_info)); 00179 if (NULL == mi) 00180 return SNMPERR_MALLOC; 00181 00182 mi->priority = priority; 00183 mi->events = events; 00184 mi->watcher_data = watcher_data; 00185 mi->cb = cb; 00186 00187 rc = insert_watcher(object, oid_len, mi); 00188 if (rc != SNMPERR_SUCCESS) 00189 free(mi); 00190 00191 return rc; 00192 } 00193 00204 int 00205 netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority, 00206 void *wd, netsnmp_object_monitor_callback * cb) 00207 { 00208 monitor_info *mi, *last; 00209 00210 watcher_list *wl = find_watchers(object, oid_len); 00211 if (NULL == wl) 00212 return SNMPERR_GENERR; 00213 00214 last = NULL; 00215 mi = wl->head; 00216 while (mi) { 00217 if ((mi->cb == cb) && (mi->priority == priority) && 00218 (mi->watcher_data == wd)) 00219 break; 00220 last = mi; 00221 mi = mi->next; 00222 } 00223 00224 if (NULL == mi) 00225 return SNMPERR_GENERR; 00226 00227 if (NULL == last) 00228 wl->head = mi->next; 00229 else 00230 last->next = mi->next; 00231 00232 if (NULL == wl->head) { 00233 CONTAINER_REMOVE(monitored_objects, wl); 00234 free(wl->monitored_object.oids); 00235 free(wl); 00236 } 00237 00238 free(mi); 00239 00240 return SNMPERR_SUCCESS; 00241 } 00242 00243 /************************************************************************** 00244 * 00245 * object monitor functions 00246 * 00247 **************************************************************************/ 00248 00265 void 00266 netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh) 00267 { 00268 00269 netsnmp_assert(need_init == 0); 00270 00271 /* 00272 * put processing of until response has been sent 00273 */ 00274 cbh->private = callback_pending_list; 00275 callback_pending_list = cbh; 00276 00277 return; 00278 } 00279 00290 int 00291 netsnmp_monitor_check_registered(int event, oid * o, int o_l) 00292 { 00293 return check_registered(event, o, o_l, NULL, NULL); 00294 } 00295 00302 void 00303 netsnmp_monitor_process_callbacks(void) 00304 { 00305 netsnmp_assert(need_init == 0); 00306 netsnmp_assert(NULL == callback_ready_list); 00307 00308 if (NULL == callback_pending_list) { 00309 DEBUGMSGT(("object_monitor", "No callbacks to process")); 00310 return; 00311 } 00312 00313 DEBUGMSG(("object_monitor", "Checking for registered " "callbacks.")); 00314 00315 /* 00316 * move an pending notification which has a registered watcher to the 00317 * ready list. Free any other notifications. 00318 */ 00319 move_pending_to_ready(); 00320 00321 /* 00322 * call callbacks 00323 */ 00324 while (callback_ready_list) { 00325 00326 /* 00327 * pop off the first item 00328 */ 00329 callback_placeholder *current_cbr; 00330 current_cbr = callback_ready_list; 00331 callback_ready_list = current_cbr->next; 00332 00333 /* 00334 * setup, then call callback 00335 */ 00336 current_cbr->cbh->watcher_data = current_cbr->mi->watcher_data; 00337 current_cbr->cbh->priority = current_cbr->mi->priority; 00338 (*current_cbr->mi->cb) (current_cbr->cbh); 00339 00340 /* 00341 * release memory (don't free current_cbr->mi) 00342 */ 00343 if (--(current_cbr->cbh->refs) == 0) { 00344 free(current_cbr->cbh->monitored_object.oids); 00345 free(current_cbr->cbh); 00346 } 00347 free(current_cbr); 00348 00349 /* 00350 * check for any new pending notifications 00351 */ 00352 move_pending_to_ready(); 00353 00354 } 00355 00356 netsnmp_assert(callback_ready_list == NULL); 00357 netsnmp_assert(callback_pending_list = NULL); 00358 00359 return; 00360 } 00361 00362 /************************************************************************** 00363 * 00364 * COOPERATIVE helpers 00365 * 00366 **************************************************************************/ 00386 void 00387 netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal, 00388 void *object_info) 00389 { 00390 netsnmp_monitor_callback_cooperative *cbh; 00391 00392 netsnmp_assert(need_init == 0); 00393 00394 cbh = SNMP_MALLOC_TYPEDEF(netsnmp_monitor_callback_cooperative); 00395 if (NULL == cbh) { 00396 snmp_log(LOG_ERR, "could not allocate memory for " 00397 "cooperative callback"); 00398 return; 00399 } 00400 00401 cbh->hdr.event = event; 00402 cbh->hdr.object_info = object_info; 00403 cbh->hdr.monitored_object.len = o_len; 00404 00405 if (o_steal) { 00406 cbh->hdr.monitored_object.oids = o; 00407 } else { 00408 cbh->hdr.monitored_object.oids = snmp_duplicate_objid(o, o_len); 00409 } 00410 00411 netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh); 00412 } 00413 00415 /************************************************************************* 00416 ************************************************************************* 00417 ************************************************************************* 00418 * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 00419 * WARNING! WARNING! 00420 * WARNING! WARNING! 00421 * WARNING! This code is under active development WARNING! 00422 * WARNING! and is subject to change at any time. WARNING! 00423 * WARNING! WARNING! 00424 * WARNING! WARNING! 00425 * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 00426 ************************************************************************* 00427 ************************************************************************* 00428 ************************************************************************* 00429 */ 00430 static watcher_list * 00431 find_watchers(oid * object, size_t oid_len) 00432 { 00433 netsnmp_index oah; 00434 00435 oah.oids = object; 00436 oah.len = oid_len; 00437 00438 return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah); 00439 } 00440 00441 static int 00442 insert_watcher(oid * object, size_t oid_len, monitor_info * mi) 00443 { 00444 watcher_list *wl = find_watchers(object, oid_len); 00445 int rc = SNMPERR_SUCCESS; 00446 00447 if (NULL != wl) { 00448 00449 monitor_info *last, *current; 00450 00451 netsnmp_assert(wl->head != NULL); 00452 00453 last = NULL; 00454 current = wl->head; 00455 while (current) { 00456 if (mi->priority == current->priority) { 00457 /* 00458 * check for duplicate 00459 */ 00460 if (mi->watcher_data == current->watcher_data) 00461 return SNMPERR_VALUE; 00462 } else if (mi->priority > current->priority) { 00463 break; 00464 } 00465 last = current; 00466 current = current->next; 00467 } 00468 if (NULL == last) { 00469 mi->next = wl->head; 00470 wl->head = mi; 00471 } else { 00472 mi->next = last->next; 00473 last->next = mi; 00474 } 00475 } else { 00476 00477 /* 00478 * first watcher for this oid; set up list 00479 */ 00480 wl = SNMP_MALLOC_TYPEDEF(watcher_list); 00481 if (NULL == wl) 00482 return SNMPERR_MALLOC; 00483 00484 /* 00485 * copy index oid 00486 */ 00487 wl->monitored_object.len = oid_len; 00488 wl->monitored_object.oids = malloc(sizeof(oid) * oid_len); 00489 if (NULL == wl->monitored_object.oids) { 00490 free(wl); 00491 return SNMPERR_MALLOC; 00492 } 00493 memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len); 00494 00495 /* 00496 * add watcher, and insert into array 00497 */ 00498 wl->head = mi; 00499 mi->next = NULL; 00500 rc = CONTAINER_INSERT(monitored_objects, wl); 00501 if (rc) { 00502 free(wl->monitored_object.oids); 00503 free(wl); 00504 return rc; 00505 } 00506 } 00507 return rc; 00508 } 00509 00528 static int 00529 check_registered(unsigned int event, oid * o, int o_l, 00530 watcher_list ** pWl, monitor_info ** pMi) 00531 { 00532 watcher_list *wl; 00533 monitor_info *mi; 00534 00535 netsnmp_assert(need_init == 0); 00536 00537 /* 00538 * check to see if anyone has registered for callbacks 00539 * for the object. 00540 */ 00541 wl = find_watchers(o, o_l); 00542 if (pWl) 00543 *pWl = wl; 00544 if (NULL == wl) 00545 return 0; 00546 00547 /* 00548 * check if any watchers are watching for this specific event 00549 */ 00550 for (mi = wl->head; mi; mi = mi->next) { 00551 00552 if (mi->events & event) { 00553 if (pMi) 00554 *pMi = mi; 00555 return TRUE; 00556 } 00557 } 00558 00559 return 0; 00560 } 00561 00565 inline void 00566 insert_ready(callback_placeholder * new_cbr) 00567 { 00568 callback_placeholder *current_cbr, *last_cbr; 00569 00570 /* 00571 * insert in callback ready list 00572 */ 00573 last_cbr = NULL; 00574 current_cbr = callback_ready_list; 00575 while (current_cbr) { 00576 00577 if (new_cbr->mi->priority > current_cbr->mi->priority) 00578 break; 00579 00580 last_cbr = current_cbr; 00581 current_cbr = current_cbr->next; 00582 } 00583 if (NULL == last_cbr) { 00584 new_cbr->next = callback_ready_list; 00585 callback_ready_list = new_cbr; 00586 } else { 00587 new_cbr->next = last_cbr->next; 00588 last_cbr->next = new_cbr; 00589 } 00590 } 00591 00598 static void 00599 move_pending_to_ready(void) 00600 { 00601 /* 00602 * check to see if anyone has registered for callbacks 00603 * for each object. 00604 */ 00605 while (callback_pending_list) { 00606 00607 watcher_list *wl; 00608 monitor_info *mi; 00609 netsnmp_monitor_callback_header *cbp; 00610 00611 /* 00612 * pop off first item 00613 */ 00614 cbp = callback_pending_list; 00615 callback_pending_list = cbp->private; 00617 if (0 == check_registered(cbp->event, cbp->monitored_object.oids, 00618 cbp->monitored_object.len, &wl, 00619 &mi)) { 00620 00621 /* 00622 * nobody watching, free memory 00623 */ 00624 free(cbp); 00625 continue; 00626 } 00627 00628 /* 00629 * Found at least one; check the rest of the list and 00630 * save callback for processing 00631 */ 00632 for (; mi; mi = mi->next) { 00633 00634 callback_placeholder *new_cbr; 00635 00636 if (0 == (mi->events & cbp->event)) 00637 continue; 00638 00639 /* 00640 * create temprory placeholder. 00641 * 00642 * I hate to allocate memory here, as I'd like this code to 00643 * be fast and lean. But I don't have time to think of another 00644 * solution os this will have to do for now. 00645 * 00646 * I need a list of monitor_info (mi) objects for each 00647 * callback which has registered for the event, and want 00648 * that list sorted by the priority required by the watcher. 00649 */ 00650 new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder); 00651 if (NULL == new_cbr) { 00652 snmp_log(LOG_ERR, "malloc failed, callback dropped."); 00653 continue; 00654 } 00655 new_cbr->cbh = cbp; 00656 new_cbr->mi = mi; 00657 ++cbp->refs; 00658 00659 /* 00660 * insert in callback ready list 00661 */ 00662 insert_ready(new_cbr); 00663 00664 } 00665 } 00667 netsnmp_assert(callback_pending_list == NULL); 00668 } 00669 00670 00671 #if defined TESTING_OBJECT_MONITOR 00672 /************************************************************************** 00673 * 00674 * (untested) TEST CODE 00675 * 00676 */ 00677 void 00678 dummy_callback(netsnmp_monitor_callback_header * cbh) 00679 { 00680 printf("Callback received.\n"); 00681 } 00682 00683 void 00684 dump_watchers(netsnmp_index *oah, void *) 00685 { 00686 watcher_list *wl = (watcher_list *) oah; 00687 netsnmp_monitor_callback_header *cbh = wl->head; 00688 00689 printf("Watcher List for OID "); 00690 print_objid(wl->hdr->oids, wl->hdr->len); 00691 printf("\n"); 00692 00693 while (cbh) { 00694 00695 printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n", 00696 cbh->priority, cbh->events, cbh->watcher_data); 00697 00698 cbh = cbh->private; 00699 } 00700 } 00701 00702 void 00703 main(int argc, char **argv) 00704 { 00705 00706 oid object[3] = { 1, 3, 6 }; 00707 int object_len = 3; 00708 int rc; 00709 00710 /* 00711 * init 00712 */ 00713 netsnmp_monitor_init(); 00714 00715 /* 00716 * insert an object 00717 */ 00718 rc = netsnmp_monitor_register(object, object_len, 0, 00719 EVENT_ROW_ADD, (void *) 0xdeadbeef, 00720 dummy_callback); 00721 printf("insert an object: %d\n", rc); 00722 00723 /* 00724 * insert same object, new priority 00725 */ 00726 netsnmp_monitor_register(object, object_len, 10, 00727 EVENT_ROW_ADD, (void *) 0xdeadbeef, 00728 dummy_callback); 00729 printf("insert same object, new priority: %d\n", rc); 00730 00731 /* 00732 * insert same object, same priority, new data 00733 */ 00734 netsnmp_monitor_register(object, object_len, 10, 00735 EVENT_ROW_ADD, (void *) 0xbeefdead, 00736 dummy_callback); 00737 printf("insert same object, same priority, new data: %d\n", rc); 00738 00739 /* 00740 * insert same object, same priority, same data 00741 */ 00742 netsnmp_monitor_register(object, object_len, 10, 00743 EVENT_ROW_ADD, (void *) 0xbeefdead, 00744 dummy_callback); 00745 printf("insert same object, same priority, new data: %d\n", rc); 00746 00747 00748 /* 00749 * dump table 00750 */ 00751 CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL); 00752 } 00753 #endif