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 #if HAVE_SYS_SOCKET_H 00042 #include <sys/socket.h> 00043 #endif 00044 #if HAVE_SYS_TIME_H 00045 #include <sys/time.h> 00046 #endif 00047 00048 #include <net-snmp/types.h> 00049 #include <net-snmp/output_api.h> 00050 #include <net-snmp/utilities.h> 00051 00052 #include <net-snmp/library/callback.h> 00053 #include <net-snmp/library/snmp_api.h> 00054 00055 /* 00056 * the inline callback methods use major/minor to index into arrays. 00057 * all users in this function do range checking before calling these 00058 * functions, so it is redundant for them to check again. But if you 00059 * want to be paranoid, define this var, and additional range checks 00060 * will be performed. 00061 * #define NETSNMP_PARANOID_LEVEL_HIGH 1 00062 */ 00063 00064 static int _callback_need_init = 1; 00065 static struct snmp_gen_callback 00066 *thecallbacks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00067 00068 #define CALLBACK_NAME_LOGGING 1 00069 #ifdef CALLBACK_NAME_LOGGING 00070 static const char *types[MAX_CALLBACK_IDS] = { "LIB", "APP" }; 00071 static const char *lib[MAX_CALLBACK_SUBIDS] = { 00072 "POST_READ_CONFIG", /* 0 */ 00073 "STORE_DATA", /* 1 */ 00074 "SHUTDOWN", /* 2 */ 00075 "POST_PREMIB_READ_CONFIG", /* 3 */ 00076 "LOGGING", /* 4 */ 00077 "SESSION_INIT", /* 5 */ 00078 NULL, /* 6 */ 00079 NULL, /* 7 */ 00080 NULL, /* 8 */ 00081 NULL, /* 9 */ 00082 NULL, /* 10 */ 00083 NULL, /* 11 */ 00084 NULL, /* 12 */ 00085 NULL, /* 13 */ 00086 NULL, /* 14 */ 00087 NULL /* 15 */ 00088 }; 00089 #endif 00090 00091 /* 00092 * extremely simplistic locking, just to find problems were the 00093 * callback list is modified while being traversed. Not intended 00094 * to do any real protection, or in any way imply that this code 00095 * has been evaluated for use in a multi-threaded environment. 00096 * In 5.2, it was a single lock. For 5.3, it has been updated to 00097 * a lock per callback, since a particular callback may trigger 00098 * registration/unregistration of other callbacks (eg AgentX 00099 * subagents do this). 00100 */ 00101 #define LOCK_PER_CALLBACK_SUBID 1 00102 #ifdef LOCK_PER_CALLBACK_SUBID 00103 static int _locks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00104 #define CALLBACK_LOCK(maj,min) ++_locks[maj][min] 00105 #define CALLBACK_UNLOCK(maj,min) --_locks[maj][min] 00106 #define CALLBACK_LOCK_COUNT(maj,min) _locks[maj][min] 00107 #else 00108 static int _lock; 00109 #define CALLBACK_LOCK(maj,min) ++_lock 00110 #define CALLBACK_UNLOCK(maj,min) --_lock 00111 #define CALLBACK_LOCK_COUNT(maj,min) _lock 00112 #endif 00113 00114 NETSNMP_STATIC_INLINE int 00115 _callback_lock(int major, int minor, const char* warn, int assert) 00116 { 00117 int lock_holded=0; 00118 struct timeval lock_time = { 0, 1000 }; 00119 00120 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00121 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00122 netsnmp_assert("bad callback id"); 00123 return 1; 00124 } 00125 #endif 00126 00127 #ifdef CALLBACK_NAME_LOGGING 00128 DEBUGMSGTL(("9:callback:lock", "locked (%s,%s)\n", 00129 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00130 SNMP_STRORNULL(lib[minor]) : "null")); 00131 #endif 00132 while (CALLBACK_LOCK_COUNT(major,minor) >= 1 && ++lock_holded < 100) 00133 select(0, NULL, NULL, NULL, &lock_time); 00134 00135 if(lock_holded >= 100) { 00136 if (NULL != warn) 00137 snmp_log(LOG_WARNING, 00138 "lock in _callback_lock sleeps more than 100 milliseconds in %s\n", warn); 00139 if (assert) 00140 netsnmp_assert(lock_holded < 100); 00141 00142 return 1; 00143 } 00144 00145 CALLBACK_LOCK(major,minor); 00146 return 0; 00147 } 00148 00149 NETSNMP_STATIC_INLINE void 00150 _callback_unlock(int major, int minor) 00151 { 00152 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00153 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00154 netsnmp_assert("bad callback id"); 00155 return; 00156 } 00157 #endif 00158 00159 CALLBACK_UNLOCK(major,minor); 00160 00161 #ifdef CALLBACK_NAME_LOGGING 00162 DEBUGMSGTL(("9:callback:lock", "unlocked (%s,%s)\n", 00163 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00164 SNMP_STRORNULL(lib[minor]) : "null")); 00165 #endif 00166 } 00167 00168 00169 /* 00170 * the chicken. or the egg. You pick. 00171 */ 00172 void 00173 init_callbacks(void) 00174 { 00175 /* 00176 * (poses a problem if you put init_callbacks() inside of 00177 * init_snmp() and then want the app to register a callback before 00178 * init_snmp() is called in the first place. -- Wes 00179 */ 00180 if (0 == _callback_need_init) 00181 return; 00182 00183 _callback_need_init = 0; 00184 00185 memset(thecallbacks, 0, sizeof(thecallbacks)); 00186 #ifdef LOCK_PER_CALLBACK_SUBID 00187 memset(_locks, 0, sizeof(_locks)); 00188 #else 00189 _lock = 0; 00190 #endif 00191 00192 DEBUGMSGTL(("callback", "initialized\n")); 00193 } 00194 00229 int 00230 snmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00231 void *arg) 00232 { 00233 return netsnmp_register_callback( major, minor, new_callback, arg, 00234 NETSNMP_CALLBACK_DEFAULT_PRIORITY); 00235 } 00236 00237 int 00238 netsnmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00239 void *arg, int priority) 00240 { 00241 struct snmp_gen_callback *newscp = NULL, *scp = NULL; 00242 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00243 00244 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00245 return SNMPERR_GENERR; 00246 } 00247 00248 if (_callback_need_init) 00249 init_callbacks(); 00250 00251 _callback_lock(major,minor, "netsnmp_register_callback", 1); 00252 00253 if ((newscp = SNMP_MALLOC_STRUCT(snmp_gen_callback)) == NULL) { 00254 _callback_unlock(major,minor); 00255 return SNMPERR_GENERR; 00256 } else { 00257 newscp->priority = priority; 00258 newscp->sc_client_arg = arg; 00259 newscp->sc_callback = new_callback; 00260 newscp->next = NULL; 00261 00262 for (scp = thecallbacks[major][minor]; scp != NULL; 00263 scp = scp->next) { 00264 if (newscp->priority < scp->priority) { 00265 newscp->next = scp; 00266 break; 00267 } 00268 prevNext = &(scp->next); 00269 } 00270 00271 *prevNext = newscp; 00272 00273 DEBUGMSGTL(("callback", "registered (%d,%d) at %p with priority %d\n", 00274 major, minor, newscp, priority)); 00275 _callback_unlock(major,minor); 00276 return SNMPERR_SUCCESS; 00277 } 00278 } 00279 00297 int 00298 snmp_call_callbacks(int major, int minor, void *caller_arg) 00299 { 00300 struct snmp_gen_callback *scp; 00301 unsigned int count = 0; 00302 00303 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00304 return SNMPERR_GENERR; 00305 } 00306 00307 if (_callback_need_init) 00308 init_callbacks(); 00309 00310 #ifdef LOCK_PER_CALLBACK_SUBID 00311 _callback_lock(major,minor,"snmp_call_callbacks", 1); 00312 #else 00313 /* 00314 * Notes: 00315 * - this gets hit the first time a trap is sent after a new trap 00316 * destination has been added (session init cb during send trap cb) 00317 */ 00318 _callback_lock(major,minor, NULL, 0); 00319 #endif 00320 00321 DEBUGMSGTL(("callback", "START calling callbacks for maj=%d min=%d\n", 00322 major, minor)); 00323 00324 /* 00325 * for each registered callback of type major and minor 00326 */ 00327 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00328 00329 /* 00330 * skip unregistered callbacks 00331 */ 00332 if(NULL == scp->sc_callback) 00333 continue; 00334 00335 DEBUGMSGTL(("callback", "calling a callback for maj=%d min=%d\n", 00336 major, minor)); 00337 00338 /* 00339 * call them 00340 */ 00341 (*(scp->sc_callback)) (major, minor, caller_arg, 00342 scp->sc_client_arg); 00343 count++; 00344 } 00345 00346 DEBUGMSGTL(("callback", 00347 "END calling callbacks for maj=%d min=%d (%d called)\n", 00348 major, minor, count)); 00349 00350 _callback_unlock(major,minor); 00351 return SNMPERR_SUCCESS; 00352 } 00353 00354 int 00355 snmp_count_callbacks(int major, int minor) 00356 { 00357 int count = 0; 00358 struct snmp_gen_callback *scp; 00359 00360 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00361 return SNMPERR_GENERR; 00362 } 00363 00364 if (_callback_need_init) 00365 init_callbacks(); 00366 00367 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00368 count++; 00369 } 00370 00371 return count; 00372 } 00373 00374 int 00375 snmp_callback_available(int major, int minor) 00376 { 00377 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00378 return SNMPERR_GENERR; 00379 } 00380 00381 if (_callback_need_init) 00382 init_callbacks(); 00383 00384 if (thecallbacks[major][minor] != NULL) { 00385 return SNMPERR_SUCCESS; 00386 } 00387 00388 return SNMPERR_GENERR; 00389 } 00390 00417 int 00418 snmp_unregister_callback(int major, int minor, SNMPCallback * target, 00419 void *arg, int matchargs) 00420 { 00421 struct snmp_gen_callback *scp = thecallbacks[major][minor]; 00422 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00423 int count = 0; 00424 00425 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) 00426 return SNMPERR_GENERR; 00427 00428 if (_callback_need_init) 00429 init_callbacks(); 00430 00431 #ifdef LOCK_PER_CALLBACK_SUBID 00432 _callback_lock(major,minor,"snmp_unregister_callback", 1); 00433 #else 00434 /* 00435 * Notes; 00436 * - this gets hit at shutdown, during cleanup. No easy fix. 00437 */ 00438 _callback_lock(major,minor,"snmp_unregister_callback", 0); 00439 #endif 00440 00441 while (scp != NULL) { 00442 if ((scp->sc_callback == target) && 00443 (!matchargs || (scp->sc_client_arg == arg))) { 00444 DEBUGMSGTL(("callback", "unregistering (%d,%d) at %p\n", major, 00445 minor, scp)); 00446 if(1 == CALLBACK_LOCK_COUNT(major,minor)) { 00447 *prevNext = scp->next; 00448 SNMP_FREE(scp); 00449 scp = *prevNext; 00450 } 00451 else { 00452 scp->sc_callback = NULL; 00454 } 00455 count++; 00456 } else { 00457 prevNext = &(scp->next); 00458 scp = scp->next; 00459 } 00460 } 00461 00462 _callback_unlock(major,minor); 00463 return count; 00464 } 00465 00473 int 00474 netsnmp_callback_clear_client_arg(void *ptr, int i, int j) 00475 { 00476 struct snmp_gen_callback *scp = NULL; 00477 int rc = 0; 00478 00479 /* 00480 * don't init i and j before loop, since the caller specified 00481 * the starting point explicitly. But *after* the i loop has 00482 * finished executing once, init j to 0 for the next pass 00483 * through the subids. 00484 */ 00485 for (; i < MAX_CALLBACK_IDS; i++,j=0) { 00486 for (; j < MAX_CALLBACK_SUBIDS; j++) { 00487 scp = thecallbacks[i][j]; 00488 while (scp != NULL) { 00489 if ((NULL != scp->sc_callback) && 00490 (scp->sc_client_arg != NULL) && 00491 (scp->sc_client_arg == ptr)) { 00492 DEBUGMSGTL(("9:callback", " clearing %p at [%d,%d]\n", ptr, i, j)); 00493 scp->sc_client_arg = NULL; 00494 ++rc; 00495 } 00496 scp = scp->next; 00497 } 00498 } 00499 } 00500 00501 if (0 != rc) { 00502 DEBUGMSGTL(("callback", "removed %d client args\n", rc)); 00503 } 00504 00505 return rc; 00506 } 00507 00508 void 00509 clear_callback(void) 00510 { 00511 unsigned int i = 0, j = 0; 00512 struct snmp_gen_callback *scp = NULL; 00513 00514 if (_callback_need_init) 00515 init_callbacks(); 00516 00517 DEBUGMSGTL(("callback", "clear callback\n")); 00518 for (i = 0; i < MAX_CALLBACK_IDS; i++) { 00519 for (j = 0; j < MAX_CALLBACK_SUBIDS; j++) { 00520 _callback_lock(i,j, "clear_callback", 1); 00521 scp = thecallbacks[i][j]; 00522 while (scp != NULL) { 00523 thecallbacks[i][j] = scp->next; 00524 /* 00525 * if there is a client arg, check for duplicates 00526 * and then free it. 00527 */ 00528 if ((NULL != scp->sc_callback) && 00529 (scp->sc_client_arg != NULL)) { 00530 void *tmp_arg; 00531 /* 00532 * save the client arg, then set it to null so that it 00533 * won't look like a duplicate, then check for duplicates 00534 * starting at the current i,j (earlier dups should have 00535 * already been found) and free the pointer. 00536 */ 00537 tmp_arg = scp->sc_client_arg; 00538 scp->sc_client_arg = NULL; 00539 DEBUGMSGTL(("9:callback", " freeing %p at [%d,%d]\n", tmp_arg, i, j)); 00540 (void)netsnmp_callback_clear_client_arg(tmp_arg, i, j); 00541 free(tmp_arg); 00542 } 00543 SNMP_FREE(scp); 00544 scp = thecallbacks[i][j]; 00545 } 00546 _callback_unlock(i,j); 00547 } 00548 } 00549 } 00550 00551 struct snmp_gen_callback * 00552 snmp_callback_list(int major, int minor) 00553 { 00554 if (_callback_need_init) 00555 init_callbacks(); 00556 00557 return (thecallbacks[major][minor]); 00558 }
1.5.7.1
Last modified: Tuesday, 23-Dec-2025 17:22:04 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.