net-snmp 5.7
|
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 <net-snmp/net-snmp-features.h> 00021 #include <sys/types.h> 00022 #include <stdio.h> 00023 #if HAVE_STDLIB_H 00024 #include <stdlib.h> 00025 #endif 00026 #if HAVE_NETINET_IN_H 00027 #include <netinet/in.h> 00028 #endif 00029 #if HAVE_STRING_H 00030 #include <string.h> 00031 #else 00032 #include <strings.h> 00033 #endif 00034 00035 #if HAVE_UNISTD_H 00036 #include <unistd.h> 00037 #endif 00038 #if HAVE_DMALLOC_H 00039 #include <dmalloc.h> 00040 #endif 00041 00042 #if HAVE_SYS_SOCKET_H 00043 #include <sys/socket.h> 00044 #endif 00045 #if !defined(mingw32) && defined(HAVE_SYS_TIME_H) 00046 #include <sys/time.h> 00047 #endif 00048 00049 #include <net-snmp/types.h> 00050 #include <net-snmp/output_api.h> 00051 #include <net-snmp/utilities.h> 00052 00053 #include <net-snmp/library/callback.h> 00054 #include <net-snmp/library/snmp_api.h> 00055 00056 netsnmp_feature_child_of(callbacks_all, libnetsnmp) 00057 00058 netsnmp_feature_child_of(callback_count, callbacks_all) 00059 netsnmp_feature_child_of(callback_list, callbacks_all) 00060 00061 /* 00062 * the inline callback methods use major/minor to index into arrays. 00063 * all users in this function do range checking before calling these 00064 * functions, so it is redundant for them to check again. But if you 00065 * want to be paranoid, define this var, and additional range checks 00066 * will be performed. 00067 * #define NETSNMP_PARANOID_LEVEL_HIGH 1 00068 */ 00069 00070 static int _callback_need_init = 1; 00071 static struct snmp_gen_callback 00072 *thecallbacks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00073 00074 #define CALLBACK_NAME_LOGGING 1 00075 #ifdef CALLBACK_NAME_LOGGING 00076 static const char *types[MAX_CALLBACK_IDS] = { "LIB", "APP" }; 00077 static const char *lib[MAX_CALLBACK_SUBIDS] = { 00078 "POST_READ_CONFIG", /* 0 */ 00079 "STORE_DATA", /* 1 */ 00080 "SHUTDOWN", /* 2 */ 00081 "POST_PREMIB_READ_CONFIG", /* 3 */ 00082 "LOGGING", /* 4 */ 00083 "SESSION_INIT", /* 5 */ 00084 NULL, /* 6 */ 00085 NULL, /* 7 */ 00086 NULL, /* 8 */ 00087 NULL, /* 9 */ 00088 NULL, /* 10 */ 00089 NULL, /* 11 */ 00090 NULL, /* 12 */ 00091 NULL, /* 13 */ 00092 NULL, /* 14 */ 00093 NULL /* 15 */ 00094 }; 00095 #endif 00096 00097 /* 00098 * extremely simplistic locking, just to find problems were the 00099 * callback list is modified while being traversed. Not intended 00100 * to do any real protection, or in any way imply that this code 00101 * has been evaluated for use in a multi-threaded environment. 00102 * In 5.2, it was a single lock. For 5.3, it has been updated to 00103 * a lock per callback, since a particular callback may trigger 00104 * registration/unregistration of other callbacks (eg AgentX 00105 * subagents do this). 00106 */ 00107 #define LOCK_PER_CALLBACK_SUBID 1 00108 #ifdef LOCK_PER_CALLBACK_SUBID 00109 static int _locks[MAX_CALLBACK_IDS][MAX_CALLBACK_SUBIDS]; 00110 #define CALLBACK_LOCK(maj,min) ++_locks[maj][min] 00111 #define CALLBACK_UNLOCK(maj,min) --_locks[maj][min] 00112 #define CALLBACK_LOCK_COUNT(maj,min) _locks[maj][min] 00113 #else 00114 static int _lock; 00115 #define CALLBACK_LOCK(maj,min) ++_lock 00116 #define CALLBACK_UNLOCK(maj,min) --_lock 00117 #define CALLBACK_LOCK_COUNT(maj,min) _lock 00118 #endif 00119 00120 NETSNMP_STATIC_INLINE int 00121 _callback_lock(int major, int minor, const char* warn, int do_assert) 00122 { 00123 int lock_holded=0; 00124 struct timeval lock_time = { 0, 1000 }; 00125 00126 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00127 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00128 netsnmp_assert("bad callback id"); 00129 return 1; 00130 } 00131 #endif 00132 00133 #ifdef CALLBACK_NAME_LOGGING 00134 DEBUGMSGTL(("9:callback:lock", "locked (%s,%s)\n", 00135 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00136 SNMP_STRORNULL(lib[minor]) : "null")); 00137 #endif 00138 while (CALLBACK_LOCK_COUNT(major,minor) >= 1 && ++lock_holded < 100) 00139 select(0, NULL, NULL, NULL, &lock_time); 00140 00141 if(lock_holded >= 100) { 00142 if (NULL != warn) 00143 snmp_log(LOG_WARNING, 00144 "lock in _callback_lock sleeps more than 100 milliseconds in %s\n", warn); 00145 if (do_assert) 00146 netsnmp_assert(lock_holded < 100); 00147 00148 return 1; 00149 } 00150 00151 CALLBACK_LOCK(major,minor); 00152 return 0; 00153 } 00154 00155 NETSNMP_STATIC_INLINE void 00156 _callback_unlock(int major, int minor) 00157 { 00158 #ifdef NETSNMP_PARANOID_LEVEL_HIGH 00159 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00160 netsnmp_assert("bad callback id"); 00161 return; 00162 } 00163 #endif 00164 00165 CALLBACK_UNLOCK(major,minor); 00166 00167 #ifdef CALLBACK_NAME_LOGGING 00168 DEBUGMSGTL(("9:callback:lock", "unlocked (%s,%s)\n", 00169 types[major], (SNMP_CALLBACK_LIBRARY == major) ? 00170 SNMP_STRORNULL(lib[minor]) : "null")); 00171 #endif 00172 } 00173 00174 00175 /* 00176 * the chicken. or the egg. You pick. 00177 */ 00178 void 00179 init_callbacks(void) 00180 { 00181 /* 00182 * (poses a problem if you put init_callbacks() inside of 00183 * init_snmp() and then want the app to register a callback before 00184 * init_snmp() is called in the first place. -- Wes 00185 */ 00186 if (0 == _callback_need_init) 00187 return; 00188 00189 _callback_need_init = 0; 00190 00191 memset(thecallbacks, 0, sizeof(thecallbacks)); 00192 #ifdef LOCK_PER_CALLBACK_SUBID 00193 memset(_locks, 0, sizeof(_locks)); 00194 #else 00195 _lock = 0; 00196 #endif 00197 00198 DEBUGMSGTL(("callback", "initialized\n")); 00199 } 00200 00237 int 00238 snmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00239 void *arg) 00240 { 00241 return netsnmp_register_callback( major, minor, new_callback, arg, 00242 NETSNMP_CALLBACK_DEFAULT_PRIORITY); 00243 } 00244 00259 int 00260 netsnmp_register_callback(int major, int minor, SNMPCallback * new_callback, 00261 void *arg, int priority) 00262 { 00263 struct snmp_gen_callback *newscp = NULL, *scp = NULL; 00264 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00265 00266 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00267 return SNMPERR_GENERR; 00268 } 00269 00270 if (_callback_need_init) 00271 init_callbacks(); 00272 00273 _callback_lock(major,minor, "netsnmp_register_callback", 1); 00274 00275 if ((newscp = SNMP_MALLOC_STRUCT(snmp_gen_callback)) == NULL) { 00276 _callback_unlock(major,minor); 00277 return SNMPERR_GENERR; 00278 } else { 00279 newscp->priority = priority; 00280 newscp->sc_client_arg = arg; 00281 newscp->sc_callback = new_callback; 00282 newscp->next = NULL; 00283 00284 for (scp = thecallbacks[major][minor]; scp != NULL; 00285 scp = scp->next) { 00286 if (newscp->priority < scp->priority) { 00287 newscp->next = scp; 00288 break; 00289 } 00290 prevNext = &(scp->next); 00291 } 00292 00293 *prevNext = newscp; 00294 00295 DEBUGMSGTL(("callback", "registered (%d,%d) at %p with priority %d\n", 00296 major, minor, newscp, priority)); 00297 _callback_unlock(major,minor); 00298 return SNMPERR_SUCCESS; 00299 } 00300 } 00301 00319 int 00320 snmp_call_callbacks(int major, int minor, void *caller_arg) 00321 { 00322 struct snmp_gen_callback *scp; 00323 unsigned int count = 0; 00324 00325 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00326 return SNMPERR_GENERR; 00327 } 00328 00329 if (_callback_need_init) 00330 init_callbacks(); 00331 00332 #ifdef LOCK_PER_CALLBACK_SUBID 00333 _callback_lock(major,minor,"snmp_call_callbacks", 1); 00334 #else 00335 /* 00336 * Notes: 00337 * - this gets hit the first time a trap is sent after a new trap 00338 * destination has been added (session init cb during send trap cb) 00339 */ 00340 _callback_lock(major,minor, NULL, 0); 00341 #endif 00342 00343 DEBUGMSGTL(("callback", "START calling callbacks for maj=%d min=%d\n", 00344 major, minor)); 00345 00346 /* 00347 * for each registered callback of type major and minor 00348 */ 00349 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00350 00351 /* 00352 * skip unregistered callbacks 00353 */ 00354 if(NULL == scp->sc_callback) 00355 continue; 00356 00357 DEBUGMSGTL(("callback", "calling a callback for maj=%d min=%d\n", 00358 major, minor)); 00359 00360 /* 00361 * call them 00362 */ 00363 (*(scp->sc_callback)) (major, minor, caller_arg, 00364 scp->sc_client_arg); 00365 count++; 00366 } 00367 00368 DEBUGMSGTL(("callback", 00369 "END calling callbacks for maj=%d min=%d (%d called)\n", 00370 major, minor, count)); 00371 00372 _callback_unlock(major,minor); 00373 return SNMPERR_SUCCESS; 00374 } 00375 00376 #ifndef NETSNMP_FEATURE_REMOVE_CALLBACK_COUNT 00377 int 00378 snmp_count_callbacks(int major, int minor) 00379 { 00380 int count = 0; 00381 struct snmp_gen_callback *scp; 00382 00383 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00384 return SNMPERR_GENERR; 00385 } 00386 00387 if (_callback_need_init) 00388 init_callbacks(); 00389 00390 for (scp = thecallbacks[major][minor]; scp != NULL; scp = scp->next) { 00391 count++; 00392 } 00393 00394 return count; 00395 } 00396 #endif /* NETSNMP_FEATURE_REMOVE_CALLBACK_COUNT */ 00397 00398 int 00399 snmp_callback_available(int major, int minor) 00400 { 00401 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) { 00402 return SNMPERR_GENERR; 00403 } 00404 00405 if (_callback_need_init) 00406 init_callbacks(); 00407 00408 if (thecallbacks[major][minor] != NULL) { 00409 return SNMPERR_SUCCESS; 00410 } 00411 00412 return SNMPERR_GENERR; 00413 } 00414 00441 int 00442 snmp_unregister_callback(int major, int minor, SNMPCallback * target, 00443 void *arg, int matchargs) 00444 { 00445 struct snmp_gen_callback *scp = thecallbacks[major][minor]; 00446 struct snmp_gen_callback **prevNext = &(thecallbacks[major][minor]); 00447 int count = 0; 00448 00449 if (major >= MAX_CALLBACK_IDS || minor >= MAX_CALLBACK_SUBIDS) 00450 return SNMPERR_GENERR; 00451 00452 if (_callback_need_init) 00453 init_callbacks(); 00454 00455 #ifdef LOCK_PER_CALLBACK_SUBID 00456 _callback_lock(major,minor,"snmp_unregister_callback", 1); 00457 #else 00458 /* 00459 * Notes; 00460 * - this gets hit at shutdown, during cleanup. No easy fix. 00461 */ 00462 _callback_lock(major,minor,"snmp_unregister_callback", 0); 00463 #endif 00464 00465 while (scp != NULL) { 00466 if ((scp->sc_callback == target) && 00467 (!matchargs || (scp->sc_client_arg == arg))) { 00468 DEBUGMSGTL(("callback", "unregistering (%d,%d) at %p\n", major, 00469 minor, scp)); 00470 if(1 == CALLBACK_LOCK_COUNT(major,minor)) { 00471 *prevNext = scp->next; 00472 SNMP_FREE(scp); 00473 scp = *prevNext; 00474 } 00475 else { 00476 scp->sc_callback = NULL; 00478 } 00479 count++; 00480 } else { 00481 prevNext = &(scp->next); 00482 scp = scp->next; 00483 } 00484 } 00485 00486 _callback_unlock(major,minor); 00487 return count; 00488 } 00489 00497 int 00498 netsnmp_callback_clear_client_arg(void *ptr, int i, int j) 00499 { 00500 struct snmp_gen_callback *scp = NULL; 00501 int rc = 0; 00502 00503 /* 00504 * don't init i and j before loop, since the caller specified 00505 * the starting point explicitly. But *after* the i loop has 00506 * finished executing once, init j to 0 for the next pass 00507 * through the subids. 00508 */ 00509 for (; i < MAX_CALLBACK_IDS; i++,j=0) { 00510 for (; j < MAX_CALLBACK_SUBIDS; j++) { 00511 scp = thecallbacks[i][j]; 00512 while (scp != NULL) { 00513 if ((NULL != scp->sc_callback) && 00514 (scp->sc_client_arg != NULL) && 00515 (scp->sc_client_arg == ptr)) { 00516 DEBUGMSGTL(("9:callback", " clearing %p at [%d,%d]\n", ptr, i, j)); 00517 scp->sc_client_arg = NULL; 00518 ++rc; 00519 } 00520 scp = scp->next; 00521 } 00522 } 00523 } 00524 00525 if (0 != rc) { 00526 DEBUGMSGTL(("callback", "removed %d client args\n", rc)); 00527 } 00528 00529 return rc; 00530 } 00531 00532 void 00533 clear_callback(void) 00534 { 00535 unsigned int i = 0, j = 0; 00536 struct snmp_gen_callback *scp = NULL; 00537 00538 if (_callback_need_init) 00539 init_callbacks(); 00540 00541 DEBUGMSGTL(("callback", "clear callback\n")); 00542 for (i = 0; i < MAX_CALLBACK_IDS; i++) { 00543 for (j = 0; j < MAX_CALLBACK_SUBIDS; j++) { 00544 _callback_lock(i,j, "clear_callback", 1); 00545 scp = thecallbacks[i][j]; 00546 while (scp != NULL) { 00547 thecallbacks[i][j] = scp->next; 00548 /* 00549 * if there is a client arg, check for duplicates 00550 * and then free it. 00551 */ 00552 if ((NULL != scp->sc_callback) && 00553 (scp->sc_client_arg != NULL)) { 00554 void *tmp_arg; 00555 /* 00556 * save the client arg, then set it to null so that it 00557 * won't look like a duplicate, then check for duplicates 00558 * starting at the current i,j (earlier dups should have 00559 * already been found) and free the pointer. 00560 */ 00561 tmp_arg = scp->sc_client_arg; 00562 scp->sc_client_arg = NULL; 00563 DEBUGMSGTL(("9:callback", " freeing %p at [%d,%d]\n", tmp_arg, i, j)); 00564 (void)netsnmp_callback_clear_client_arg(tmp_arg, i, j); 00565 free(tmp_arg); 00566 } 00567 SNMP_FREE(scp); 00568 scp = thecallbacks[i][j]; 00569 } 00570 _callback_unlock(i,j); 00571 } 00572 } 00573 } 00574 00575 #ifndef NETSNMP_FEATURE_REMOVE_CALLBACK_LIST 00576 struct snmp_gen_callback * 00577 snmp_callback_list(int major, int minor) 00578 { 00579 if (_callback_need_init) 00580 init_callbacks(); 00581 00582 return (thecallbacks[major][minor]); 00583 } 00584 #endif /* NETSNMP_FEATURE_REMOVE_CALLBACK_LIST */ 00585