net-snmp  5.4.1
callback.c
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 }