00001 /* 00002 * callback.c: A generic callback mechanism 00003 */ 00004 /* Portions of this file are subject to the following copyright(s). See 00005 * the Net-SNMP's COPYING file for more details and other copyrights 00006 * that may apply: 00007 */ 00008 /* 00009 * Portions of this file are copyrighted by: 00010 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. 00011 * Use is subject to license terms specified in the COPYING file 00012 * distributed with the Net-SNMP package. 00013 */ 00019 #include <net-snmp/net-snmp-config.h> 00020 #include <sys/types.h> 00021 #include <stdio.h> 00022 #if HAVE_STDLIB_H 00023 #include <stdlib.h> 00024 #endif 00025 #if HAVE_WINSOCK_H 00026 #include <winsock.h> 00027 #endif 00028 #if HAVE_NETINET_IN_H 00029 #include <netinet/in.h> 00030 #endif 00031 #if HAVE_STRING_H 00032 #include <string.h> 00033 #else 00034 #include <strings.h> 00035 #endif 00036 00037 #if HAVE_DMALLOC_H 00038 #include <dmalloc.h> 00039 #endif 00040 00041 #include <net-snmp/types.h> 00042 #include <net-snmp/output_api.h> 00043 #include <net-snmp/utilities.h> 00044 00045 #include <net-snmp/library/callback.h> 00046 #include <net-snmp/library/snmp_api.h> 00047 00048 /* 00049 * the inline callback methods use major/minor to index into arrays. 00050 * all users in this function do range checking before calling these 00051 * functions, so it is redundant for them to check again. But if you 00052 * want to be paranoid, define this var, and additional range checks 00053 * will be performed. 00054 * #define NETSNMP_PARANOID_LEVEL_HIGH 1 00055 */ 00056 00057 static int _callback_need_init = 1; 00058 static struct snmp_gen_callback 00059 *thecallbacks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00060 00061 #define CALLBACK_NAME_LOGGING 1 00062 #ifdef CALLBACK_NAME_LOGGING 00063 static const char *types[MAX_CALLBACK_IDS] = { "LIB", "APP" }; 00064 static const char *lib[MAX_CALLBACK_SUBIDS] = { 00065 "POST_READ_CONFIG", /* 0 */ 00066 "STORE_DATA", /* 1 */ 00067 "SHUTDOWN", /* 2 */ 00068 "POST_PREMIB_READ_CONFIG", /* 3 */ 00069 "LOGGING", /* 4 */ 00070 "SESSION_INIT", /* 5 */ 00071 NULL, /* 6 */ 00072 NULL, /* 7 */ 00073 NULL, /* 8 */ 00074 NULL, /* 9 */ 00075 NULL, /* 10 */ 00076 NULL, /* 11 */ 00077 NULL, /* 12 */ 00078 NULL, /* 13 */ 00079 NULL, /* 14 */ 00080 NULL /* 15 */ 00081 }; 00082 #endif 00083 00084 /* 00085 * extremely simplistic locking, just to find problems were the 00086 * callback list is modified while being traversed. Not intended 00087 * to do any real protection, or in any way imply that this code 00088 * has been evaluated for use in a multi-threaded environment. 00089 * In 5.2, it was a single lock. For 5.3, it has been updated to 00090 * a lock per callback, since a particular callback may trigger 00091 * registration/unregistartion of other callbacks (eg AgentX 00092 * subagents do this). 00093 */ 00094 #define LOCK_PER_CALLBACK_SUBID 1 00095 #ifdef LOCK_PER_CALLBACK_SUBID 00096 static int _locks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00097 #define CALLBACK_LOCK(maj,min) ++_locks[major][minor] 00098 #define CALLBACK_UNLOCK(maj,min) --_locks[major][minor] 00099 #else 00100 static int _lock; 00101 #define CALLBACK_LOCK(maj,min) ++_lock 00102 #define CALLBACK_UNLOCK(maj,min) --_lock 00103 #endif 00104 00105 NETSNMP_STATIC_INLINE int 00106 _callback_lock(int major, int minor, const char* warn, int assert) 00107 { 00108 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00109 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00110 netsnmp_assert("bad callback id"); 00111 return 1; 00112 } 00113 #endif 00114 00115 #ifdef CALLBACK_NAME_LOGGING 00116 DEBUGMSGTL(("9:callback:lock", "locked (%s,%s)\n", 00117 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00118 lib[minor] : NULL)); 00119 #endif 00120 if (CALLBACK_LOCK(major,minor) > 1) 00121 { 00122 if (NULL != warn) 00123 snmp_log(LOG_WARNING, 00124 "_callback_lock already locket in %s\n", warn); 00125 if (assert) 00126 netsnmp_assert(1==_locks[major][minor]); 00127 00128 return 1; 00129 } 00130 else 00131 return 0; 00132 00133 } 00134 00135 NETSNMP_STATIC_INLINE void 00136 _callback_unlock(int major, int minor) 00137 { 00138 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00139 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00140 netsnmp_assert("bad callback id"); 00141 return; 00142 } 00143 #endif 00144 00145 CALLBACK_UNLOCK(major,minor); 00146 00147 #ifdef CALLBACK_NAME_LOGGING 00148 DEBUGMSGTL(("9:callback:lock", "unlocked (%s,%s)\n", 00149 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00150 lib[minor] : NULL)); 00151 #endif 00152 } 00153 00154 00155 /* 00156 * the chicken. or the egg. You pick. 00157 */ 00158 void 00159 init_callbacks(void) 00160 { 00161 /* 00162 * (poses a problem if you put init_callbacks() inside of 00163 * init_snmp() and then want the app to register a callback before 00164 * init_snmp() is called in the first place. -- Wes 00165 */ 00166 if (0 == _callback_need_init) 00167 return; 00168 00169 _callback_need_init = 0; 00170 00171 memset(thecallbacks, 0, sizeof(thecallbacks)); 00172 memset(_locks, 0, sizeof(_locks)); 00173 00174 DEBUGMSGTL(("callback", "initialized\n")); 00175 } 00176 00211 int 00212 snmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00213 void *arg) 00214 { 00215 return netsnmp_register_callback( major, minor, new_callback, arg, 00216 NETSNMP_CALLBACK_DEFAULT_PRIORITY); 00217 } 00218 00219 int 00220 netsnmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00221 void *arg, int priority) 00222 { 00223 struct snmp_gen_callback *newscp = NULL, *scp = NULL; 00224 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00225 00226 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00227 return SNMPERR_GENERR; 00228 } 00229 00230 if (_callback_need_init) 00231 init_callbacks(); 00232 00233 _callback_lock(major,minor, "netsnmp_register_callback", 1); 00234 00235 if ((newscp = SNMP_MALLOC_STRUCT(snmp_gen_callback)) == NULL) { 00236 _callback_unlock(major,minor); 00237 return SNMPERR_GENERR; 00238 } else { 00239 newscp->priority = priority; 00240 newscp->sc_client_arg = arg; 00241 newscp->sc_callback = new_callback; 00242 newscp->next = NULL; 00243 00244 for (scp = thecallbacks[major][minor]; scp != NULL; 00245 scp = scp->next) { 00246 if (newscp->priority < scp->priority) { 00247 newscp->next = scp; 00248 break; 00249 } 00250 prevNext = &(scp->next); 00251 } 00252 00253 *prevNext = newscp; 00254 00255 DEBUGMSGTL(("callback", "registered (%d,%d) at %p with priority %d\n", 00256 major, minor, newscp, priority)); 00257 _callback_unlock(major,minor); 00258 return SNMPERR_SUCCESS; 00259 } 00260 } 00261 00279 int 00280 snmp_call_callbacks(int major, int minor, void *caller_arg) 00281 { 00282 struct snmp_gen_callback *scp; 00283 unsigned int count = 0; 00284 00285 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00286 return SNMPERR_GENERR; 00287 } 00288 00289 if (_callback_need_init) 00290 init_callbacks(); 00291 00292 #ifdef LOCK_PER_CALLBACK_SUBID 00293 _callback_lock(major,minor,"snmp_call_callbacks", 1); 00294 #else 00295 /* 00296 * Notes: 00297 * - this gets hit the first time a trap is sent after a new trap 00298 * destination has been added (session init cb during send trap cb) 00299 */ 00300 _callback_lock(major,minor, NULL, 0); 00301 #endif 00302 00303 DEBUGMSGTL(("callback", "START calling callbacks for maj=%d min=%d\n", 00304 major, minor)); 00305 00306 /* 00307 * for each registered callback of type major and minor 00308 */ 00309 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00310 00311 /* 00312 * skip unregistered callbacks 00313 */ 00314 if(NULL == scp->sc_callback) 00315 continue; 00316 00317 DEBUGMSGTL(("callback", "calling a callback for maj=%d min=%d\n", 00318 major, minor)); 00319 00320 /* 00321 * call them 00322 */ 00323 (*(scp->sc_callback)) (major, minor, caller_arg, 00324 scp->sc_client_arg); 00325 count++; 00326 } 00327 00328 DEBUGMSGTL(("callback", 00329 "END calling callbacks for maj=%d min=%d (%d called)\n", 00330 major, minor, count)); 00331 00332 _callback_unlock(major,minor); 00333 return SNMPERR_SUCCESS; 00334 } 00335 00336 int 00337 snmp_count_callbacks(int major, int minor) 00338 { 00339 int count = 0; 00340 struct snmp_gen_callback *scp; 00341 00342 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00343 return SNMPERR_GENERR; 00344 } 00345 00346 if (_callback_need_init) 00347 init_callbacks(); 00348 00349 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00350 count++; 00351 } 00352 00353 return count; 00354 } 00355 00356 int 00357 snmp_callback_available(int major, int minor) 00358 { 00359 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00360 return SNMPERR_GENERR; 00361 } 00362 00363 if (_callback_need_init) 00364 init_callbacks(); 00365 00366 if (thecallbacks[major][minor] != NULL) { 00367 return SNMPERR_SUCCESS; 00368 } 00369 00370 return SNMPERR_GENERR; 00371 } 00372 00399 int 00400 snmp_unregister_callback(int major, int minor, SNMPCallback * target, 00401 void *arg, int matchargs) 00402 { 00403 struct snmp_gen_callback *scp = thecallbacks[major][minor]; 00404 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00405 int count = 0; 00406 00407 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) 00408 return SNMPERR_GENERR; 00409 00410 if (_callback_need_init) 00411 init_callbacks(); 00412 00413 #ifdef LOCK_PER_CALLBACK_SUBID 00414 _callback_lock(major,minor,"snmp_unregister_callback", 1); 00415 #else 00416 /* 00417 * Notes; 00418 * - this gets hit at shutdown, during cleanup. No easy fix. 00419 */ 00420 _callback_lock(major,minor,"snmp_unregister_callback", 0); 00421 #endif 00422 00423 while (scp != NULL) { 00424 if ((scp->sc_callback == target) && 00425 (!matchargs || (scp->sc_client_arg == arg))) { 00426 DEBUGMSGTL(("callback", "unregistering (%d,%d) at %p\n", major, 00427 minor, scp)); 00428 if(1 == _locks[major][minor]) { 00429 *prevNext = scp->next; 00430 SNMP_FREE(scp); 00431 scp = *prevNext; 00432 } 00433 else { 00434 scp->sc_callback = NULL; 00436 } 00437 count++; 00438 } else { 00439 prevNext = &(scp->next); 00440 scp = scp->next; 00441 } 00442 } 00443 00444 _callback_unlock(major,minor); 00445 return count; 00446 } 00447 00455 int 00456 netsnmp_callback_clear_client_arg(void *ptr, int i, int j) 00457 { 00458 struct snmp_gen_callback *scp = NULL; 00459 int rc = 0; 00460 00461 /* 00462 * don't init i and j before loop, since the caller specified 00463 * the starting point explicitly. But *after* the i loop has 00464 * finished executing once, init j to 0 for the next pass 00465 * through the subids. 00466 */ 00467 for (; i < MAX_CALLBACK_IDS; i++,j=0) { 00468 for (; j < MAX_CALLBACK_SUBIDS; j++) { 00469 scp = thecallbacks[i][j]; 00470 while (scp != NULL) { 00471 if ((NULL != scp->sc_callback) && 00472 (scp->sc_client_arg != NULL) && 00473 (scp->sc_client_arg == ptr)) { 00474 DEBUGMSGTL(("9:callback", " clearing %p at [%d,%d]\n", ptr, i, j)); 00475 scp->sc_client_arg = NULL; 00476 ++rc; 00477 } 00478 scp = scp->next; 00479 } 00480 } 00481 } 00482 00483 if (0 != rc) { 00484 DEBUGMSGTL(("callback", "removed %d client args\n", rc)); 00485 } 00486 00487 return rc; 00488 } 00489 00490 void 00491 clear_callback(void) 00492 { 00493 unsigned int i = 0, j = 0; 00494 struct snmp_gen_callback *scp = NULL; 00495 00496 if (_callback_need_init) 00497 init_callbacks(); 00498 00499 DEBUGMSGTL(("callback", "clear callback\n")); 00500 for (i = 0; i < MAX_CALLBACK_IDS; i++) { 00501 for (j = 0; j < MAX_CALLBACK_SUBIDS; j++) { 00502 _callback_lock(i,j, "clear_callback", 1); 00503 scp = thecallbacks[i][j]; 00504 while (scp != NULL) { 00505 thecallbacks[i][j] = scp->next; 00506 /* 00507 * if there is a client arg, check for duplicates 00508 * and then free it. 00509 */ 00510 if ((NULL != scp->sc_callback) && 00511 (scp->sc_client_arg != NULL)) { 00512 void *tmp_arg; 00513 /* 00514 * save the client arg, then set it to null so that it 00515 * won't look like a duplicate, then check for duplicates 00516 * starting at the current i,j (earlier dups should have 00517 * already been found) and free the pointer. 00518 */ 00519 tmp_arg = scp->sc_client_arg; 00520 scp->sc_client_arg = NULL; 00521 DEBUGMSGTL(("9:callback", " freeing %p at [%d,%d]\n", tmp_arg, i, j)); 00522 (void)netsnmp_callback_clear_client_arg(tmp_arg, i, j); 00523 free(tmp_arg); 00524 } 00525 SNMP_FREE(scp); 00526 scp = thecallbacks[i][j]; 00527 } 00528 _callback_unlock(i,j); 00529 } 00530 } 00531 } 00532 00533 struct snmp_gen_callback * 00534 snmp_callback_list(int major, int minor) 00535 { 00536 if (_callback_need_init) 00537 init_callbacks(); 00538 00539 return (thecallbacks[major][minor]); 00540 }
1.3.9.1
Last modified: Thursday, 01-Mar-2007 16:20:03 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.