net-snmp 5.7
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 <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