net-snmp
5.4.1
|
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/unregistration 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 #define CALLBACK_LOCK_COUNT(maj,min) _locks[major][minor] 00100 #else 00101 static int _lock; 00102 #define CALLBACK_LOCK(maj,min) ++_lock 00103 #define CALLBACK_UNLOCK(maj,min) --_lock 00104 #define CALLBACK_LOCK_COUNT(maj,min) _lock 00105 #endif 00106 00107 NETSNMP_STATIC_INLINE int 00108 _callback_lock(int major, int minor, const char* warn, int assert) 00109 { 00110 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00111 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00112 netsnmp_assert("bad callback id"); 00113 return 1; 00114 } 00115 #endif 00116 00117 #ifdef CALLBACK_NAME_LOGGING 00118 DEBUGMSGTL(("9:callback:lock", "locked (%s,%s)\n", 00119 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00120 SNMP_STRORNULL(lib[minor]) : "null")); 00121 #endif 00122 if (CALLBACK_LOCK(major,minor) > 1) 00123 { 00124 if (NULL != warn) 00125 snmp_log(LOG_WARNING, 00126 "_callback_lock already locket in %s\n", warn); 00127 if (assert) 00128 netsnmp_assert(1==CALLBACK_LOCK_COUNT(major,minor)); 00129 00130 return 1; 00131 } 00132 else 00133 return 0; 00134 00135 } 00136 00137 NETSNMP_STATIC_INLINE void 00138 _callback_unlock(int major, int minor) 00139 { 00140 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00141 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00142 netsnmp_assert("bad callback id"); 00143 return; 00144 } 00145 #endif 00146 00147 CALLBACK_UNLOCK(major,minor); 00148 00149 #ifdef CALLBACK_NAME_LOGGING 00150 DEBUGMSGTL(("9:callback:lock", "unlocked (%s,%s)\n", 00151 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00152 SNMP_STRORNULL(lib[minor]) : "null")); 00153 #endif 00154 } 00155 00156 00157 /* 00158 * the chicken. or the egg. You pick. 00159 */ 00160 void 00161 init_callbacks(void) 00162 { 00163 /* 00164 * (poses a problem if you put init_callbacks() inside of 00165 * init_snmp() and then want the app to register a callback before 00166 * init_snmp() is called in the first place. -- Wes 00167 */ 00168 if (0 == _callback_need_init) 00169 return; 00170 00171 _callback_need_init = 0; 00172 00173 memset(thecallbacks, 0, sizeof(thecallbacks)); 00174 #ifdef LOCK_PER_CALLBACK_SUBID 00175 memset(_locks, 0, sizeof(_locks)); 00176 #else 00177 _lock = 0; 00178 #endif 00179 00180 DEBUGMSGTL(("callback", "initialized\n")); 00181 } 00182 00217 int 00218 snmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00219 void *arg) 00220 { 00221 return netsnmp_register_callback( major, minor, new_callback, arg, 00222 NETSNMP_CALLBACK_DEFAULT_PRIORITY); 00223 } 00224 00225 int 00226 netsnmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00227 void *arg, int priority) 00228 { 00229 struct snmp_gen_callback *newscp = NULL, *scp = NULL; 00230 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00231 00232 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00233 return SNMPERR_GENERR; 00234 } 00235 00236 if (_callback_need_init) 00237 init_callbacks(); 00238 00239 _callback_lock(major,minor, "netsnmp_register_callback", 1); 00240 00241 if ((newscp = SNMP_MALLOC_STRUCT(snmp_gen_callback)) == NULL) { 00242 _callback_unlock(major,minor); 00243 return SNMPERR_GENERR; 00244 } else { 00245 newscp->priority = priority; 00246 newscp->sc_client_arg = arg; 00247 newscp->sc_callback = new_callback; 00248 newscp->next = NULL; 00249 00250 for (scp = thecallbacks[major][minor]; scp != NULL; 00251 scp = scp->next) { 00252 if (newscp->priority < scp->priority) { 00253 newscp->next = scp; 00254 break; 00255 } 00256 prevNext = &(scp->next); 00257 } 00258 00259 *prevNext = newscp; 00260 00261 DEBUGMSGTL(("callback", "registered (%d,%d) at %p with priority %d\n", 00262 major, minor, newscp, priority)); 00263 _callback_unlock(major,minor); 00264 return SNMPERR_SUCCESS; 00265 } 00266 } 00267 00285 int 00286 snmp_call_callbacks(int major, int minor, void *caller_arg) 00287 { 00288 struct snmp_gen_callback *scp; 00289 unsigned int count = 0; 00290 00291 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00292 return SNMPERR_GENERR; 00293 } 00294 00295 if (_callback_need_init) 00296 init_callbacks(); 00297 00298 #ifdef LOCK_PER_CALLBACK_SUBID 00299 _callback_lock(major,minor,"snmp_call_callbacks", 1); 00300 #else 00301 /* 00302 * Notes: 00303 * - this gets hit the first time a trap is sent after a new trap 00304 * destination has been added (session init cb during send trap cb) 00305 */ 00306 _callback_lock(major,minor, NULL, 0); 00307 #endif 00308 00309 DEBUGMSGTL(("callback", "START calling callbacks for maj=%d min=%d\n", 00310 major, minor)); 00311 00312 /* 00313 * for each registered callback of type major and minor 00314 */ 00315 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00316 00317 /* 00318 * skip unregistered callbacks 00319 */ 00320 if(NULL == scp->sc_callback) 00321 continue; 00322 00323 DEBUGMSGTL(("callback", "calling a callback for maj=%d min=%d\n", 00324 major, minor)); 00325 00326 /* 00327 * call them 00328 */ 00329 (*(scp->sc_callback)) (major, minor, caller_arg, 00330 scp->sc_client_arg); 00331 count++; 00332 } 00333 00334 DEBUGMSGTL(("callback", 00335 "END calling callbacks for maj=%d min=%d (%d called)\n", 00336 major, minor, count)); 00337 00338 _callback_unlock(major,minor); 00339 return SNMPERR_SUCCESS; 00340 } 00341 00342 int 00343 snmp_count_callbacks(int major, int minor) 00344 { 00345 int count = 0; 00346 struct snmp_gen_callback *scp; 00347 00348 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00349 return SNMPERR_GENERR; 00350 } 00351 00352 if (_callback_need_init) 00353 init_callbacks(); 00354 00355 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00356 count++; 00357 } 00358 00359 return count; 00360 } 00361 00362 int 00363 snmp_callback_available(int major, int minor) 00364 { 00365 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00366 return SNMPERR_GENERR; 00367 } 00368 00369 if (_callback_need_init) 00370 init_callbacks(); 00371 00372 if (thecallbacks[major][minor] != NULL) { 00373 return SNMPERR_SUCCESS; 00374 } 00375 00376 return SNMPERR_GENERR; 00377 } 00378 00405 int 00406 snmp_unregister_callback(int major, int minor, SNMPCallback * target, 00407 void *arg, int matchargs) 00408 { 00409 struct snmp_gen_callback *scp = thecallbacks[major][minor]; 00410 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00411 int count = 0; 00412 00413 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) 00414 return SNMPERR_GENERR; 00415 00416 if (_callback_need_init) 00417 init_callbacks(); 00418 00419 #ifdef LOCK_PER_CALLBACK_SUBID 00420 _callback_lock(major,minor,"snmp_unregister_callback", 1); 00421 #else 00422 /* 00423 * Notes; 00424 * - this gets hit at shutdown, during cleanup. No easy fix. 00425 */ 00426 _callback_lock(major,minor,"snmp_unregister_callback", 0); 00427 #endif 00428 00429 while (scp != NULL) { 00430 if ((scp->sc_callback == target) && 00431 (!matchargs || (scp->sc_client_arg == arg))) { 00432 DEBUGMSGTL(("callback", "unregistering (%d,%d) at %p\n", major, 00433 minor, scp)); 00434 if(1 == CALLBACK_LOCK_COUNT(major,minor)) { 00435 *prevNext = scp->next; 00436 SNMP_FREE(scp); 00437 scp = *prevNext; 00438 } 00439 else { 00440 scp->sc_callback = NULL; 00442 } 00443 count++; 00444 } else { 00445 prevNext = &(scp->next); 00446 scp = scp->next; 00447 } 00448 } 00449 00450 _callback_unlock(major,minor); 00451 return count; 00452 } 00453 00461 int 00462 netsnmp_callback_clear_client_arg(void *ptr, int i, int j) 00463 { 00464 struct snmp_gen_callback *scp = NULL; 00465 int rc = 0; 00466 00467 /* 00468 * don't init i and j before loop, since the caller specified 00469 * the starting point explicitly. But *after* the i loop has 00470 * finished executing once, init j to 0 for the next pass 00471 * through the subids. 00472 */ 00473 for (; i < MAX_CALLBACK_IDS; i++,j=0) { 00474 for (; j < MAX_CALLBACK_SUBIDS; j++) { 00475 scp = thecallbacks[i][j]; 00476 while (scp != NULL) { 00477 if ((NULL != scp->sc_callback) && 00478 (scp->sc_client_arg != NULL) && 00479 (scp->sc_client_arg == ptr)) { 00480 DEBUGMSGTL(("9:callback", " clearing %p at [%d,%d]\n", ptr, i, j)); 00481 scp->sc_client_arg = NULL; 00482 ++rc; 00483 } 00484 scp = scp->next; 00485 } 00486 } 00487 } 00488 00489 if (0 != rc) { 00490 DEBUGMSGTL(("callback", "removed %d client args\n", rc)); 00491 } 00492 00493 return rc; 00494 } 00495 00496 void 00497 clear_callback(void) 00498 { 00499 unsigned int i = 0, j = 0; 00500 struct snmp_gen_callback *scp = NULL; 00501 00502 if (_callback_need_init) 00503 init_callbacks(); 00504 00505 DEBUGMSGTL(("callback", "clear callback\n")); 00506 for (i = 0; i < MAX_CALLBACK_IDS; i++) { 00507 for (j = 0; j < MAX_CALLBACK_SUBIDS; j++) { 00508 _callback_lock(i,j, "clear_callback", 1); 00509 scp = thecallbacks[i][j]; 00510 while (scp != NULL) { 00511 thecallbacks[i][j] = scp->next; 00512 /* 00513 * if there is a client arg, check for duplicates 00514 * and then free it. 00515 */ 00516 if ((NULL != scp->sc_callback) && 00517 (scp->sc_client_arg != NULL)) { 00518 void *tmp_arg; 00519 /* 00520 * save the client arg, then set it to null so that it 00521 * won't look like a duplicate, then check for duplicates 00522 * starting at the current i,j (earlier dups should have 00523 * already been found) and free the pointer. 00524 */ 00525 tmp_arg = scp->sc_client_arg; 00526 scp->sc_client_arg = NULL; 00527 DEBUGMSGTL(("9:callback", " freeing %p at [%d,%d]\n", tmp_arg, i, j)); 00528 (void)netsnmp_callback_clear_client_arg(tmp_arg, i, j); 00529 free(tmp_arg); 00530 } 00531 SNMP_FREE(scp); 00532 scp = thecallbacks[i][j]; 00533 } 00534 _callback_unlock(i,j); 00535 } 00536 } 00537 } 00538 00539 struct snmp_gen_callback * 00540 snmp_callback_list(int major, int minor) 00541 { 00542 if (_callback_need_init) 00543 init_callbacks(); 00544 00545 return (thecallbacks[major][minor]); 00546 }