Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

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

Generated on Fri Dec 30 13:47:44 2005 for net-snmp by  doxygen 1.3.9.1

Valid CSS!


Last modified: Thursday, 01-Mar-2007 16:20:03 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.