Icinga-core 1.4.0
next gen monitoring
base/nebmods.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002  *
00003  * NEBMODS.C - Event Broker Module Functions
00004  *
00005  * Copyright (c) 2002-2008 Ethan Galstad (egalstad@nagios.org)
00006  * Copyright (c) 2009-2011 Nagios Core Development Team and Community Contributors
00007  * Copyright (c) 2009-2011 Icinga Development Team (http://www.icinga.org)
00008  *
00009  * License:
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License version 2 as
00013  * published by the Free Software Foundation.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023  *
00024  *****************************************************************************/
00025 
00026 #include "../include/config.h"
00027 #include "../include/common.h"
00028 #include "../include/nebmods.h"
00029 #include "../include/neberrors.h"
00030 #include "../include/icinga.h"
00031 
00032 
00033 #ifdef USE_EVENT_BROKER
00034 
00035 
00036 nebmodule *neb_module_list=NULL;
00037 nebcallback **neb_callback_list=NULL;
00038 
00039 extern char     *temp_path;
00040 
00041 
00042 /*#define DEBUG*/
00043 
00044 
00045 /****************************************************************************/
00046 /****************************************************************************/
00047 /* INITIALIZATION/CLEANUP FUNCTIONS                                         */
00048 /****************************************************************************/
00049 /****************************************************************************/
00050 
00051 /* initialize module routines */
00052 int neb_init_modules(void){
00053 #ifdef USE_LTDL
00054         int result=OK;
00055 #endif
00056 
00057         /* initialize library */
00058 #ifdef USE_LTDL
00059         result=lt_dlinit();
00060         if(result)
00061                 return ERROR;
00062 #endif
00063 
00064         return OK;
00065         }
00066 
00067 
00068 /* deinitialize module routines */
00069 int neb_deinit_modules(void){
00070 #ifdef USE_LTDL
00071         int result=OK;
00072 #endif
00073 
00074         /* deinitialize library */
00075 #ifdef USE_LTDL
00076         result=lt_dlexit();
00077         if(result)
00078                 return ERROR;
00079 #endif
00080 
00081         return OK;
00082         }
00083 
00084 
00085 
00086 /* add a new module to module list */
00087 int neb_add_module(char *filename,char *args,int should_be_loaded){
00088         nebmodule *new_module=NULL;
00089         int x=OK;
00090 
00091         if(filename==NULL)
00092                 return ERROR;
00093 
00094         /* allocate memory */
00095         new_module=(nebmodule *)malloc(sizeof(nebmodule));
00096         if(new_module==NULL)
00097                 return ERROR;
00098 
00099         /* initialize vars */
00100         new_module->filename=(char *)strdup(filename);
00101         new_module->args=(args==NULL)?NULL:(char *)strdup(args);
00102         new_module->should_be_loaded=should_be_loaded;
00103         new_module->is_currently_loaded=FALSE;
00104         for(x=0;x<NEBMODULE_MODINFO_NUMITEMS;x++)
00105                 new_module->info[x]=NULL;
00106         new_module->module_handle=NULL;
00107         new_module->init_func=NULL;
00108         new_module->deinit_func=NULL;
00109 #ifdef HAVE_PTHREAD_H
00110         new_module->thread_id=(pthread_t)NULL;
00111 #endif
00112 
00113         /* add module to head of list */
00114         new_module->next=neb_module_list;
00115         neb_module_list=new_module;
00116 
00117         log_debug_info(DEBUGL_EVENTBROKER,0,"Added module: name='%s', args='%s', should_be_loaded='%d'\n",filename,args,should_be_loaded);
00118 
00119         return OK;
00120         }
00121 
00122 
00123 /* free memory allocated to module list */
00124 int neb_free_module_list(void){
00125         nebmodule *temp_module=NULL;
00126         nebmodule *next_module=NULL;
00127         int x=OK;
00128 
00129         for(temp_module=neb_module_list;temp_module;){
00130                 next_module=temp_module->next;
00131                 my_free(temp_module->filename);
00132                 my_free(temp_module->args);
00133                 for(x=0;x<NEBMODULE_MODINFO_NUMITEMS;x++)
00134                         my_free(temp_module->info[x]);
00135                 my_free(temp_module);
00136                 temp_module=next_module;
00137                 }
00138 
00139         neb_module_list=NULL;
00140 
00141         return OK;
00142         }
00143 
00144 
00145 
00146 /****************************************************************************/
00147 /****************************************************************************/
00148 /* LOAD/UNLOAD FUNCTIONS                                                    */
00149 /****************************************************************************/
00150 /****************************************************************************/
00151 
00152 
00153 /* load all modules */
00154 int neb_load_all_modules(void){
00155         nebmodule *temp_module=NULL;
00156         int result=OK;
00157 
00158         for(temp_module=neb_module_list;temp_module;temp_module=temp_module->next){
00159                 result=neb_load_module(temp_module);
00160                 }
00161 
00162         return OK;
00163         }
00164 
00165 #ifndef PATH_MAX
00166 # define PATH_MAX 4096
00167 #endif
00168 /* load a particular module */
00169 int neb_load_module(nebmodule *mod){
00170         int (*initfunc)(int,char *,void *);
00171         int *module_version_ptr=NULL;
00172         int result=OK;
00173 
00174 
00175         if(mod==NULL || mod->filename==NULL)
00176                 return ERROR;
00177 
00178         /* don't reopen the module */
00179         if(mod->is_currently_loaded==TRUE)
00180                 return OK;
00181 
00182         /* don't load modules unless they should be loaded */
00183         if(mod->should_be_loaded==FALSE)
00184                 return ERROR;
00185 
00186         /**********
00187            Using dlopen() is great, but a real danger as-is.  The problem with loaded modules is that if you overwrite the original file (e.g. using 'mv'),
00188            you do not alter the inode of the original file.  Since the original file/module is memory-mapped in some fashion, Icinga will segfault the next
00189            time an event broker call is directed to one of the module's callback functions.  This is extremely problematic when it comes to upgrading NEB
00190            modules while Icinga is running.  A workaround is to (1) 'mv' the original/loaded module file to another name (on the same filesystem)
00191            and (2) copy the new module file to the location of the original one (using the original filename).  In this scenario, dlopen() will keep referencing
00192            the original file/inode for callbacks.  This is not an ideal solution.   A better one is to delete the module file once it is loaded by dlopen().
00193            This prevents other processed from unintentially overwriting the original file, which would cause Icinga to crash.  However, if we delete the file
00194            before anyone else can muck with it, things should be good.  'lsof' shows that a deleted file is still referenced by the kernel and callback
00195            functions continue to work once the module has been loaded.  Long story, but this took quite a while to figure out, as there isn't much
00196            of anything I could find on the subject other than some sketchy info on similar problems on HP-UX.  Hopefully this will save future coders some time.
00197            So... the trick is to (1) copy the module to a temp file, (2) dlopen() the temp file, and (3) immediately delete the temp file.
00198         ************/
00199 
00200         /* 2010-01-05 MF: Patch taken from OMD into Icinga Core
00201            OMD: Do not make a copy of the module, but directly load it. This prevents problems with a tmpfs which
00202            is mounted as user. OMD users surely have no problems with modules overwritten by 'cp in runtime. Anyway,
00203            the usual way to install files is 'install', which removes and recreates the file (just as tar, rpm and
00204            many other installation-tools do). */
00205 
00206         /* load the module (use the temp copy we just made) */
00207 #ifdef USE_LTDL
00208         mod->module_handle=lt_dlopen(mod->filename);
00209 #else
00210         mod->module_handle=(void *)dlopen(mod->filename,RTLD_NOW|RTLD_GLOBAL);
00211 #endif
00212         if(mod->module_handle==NULL){
00213 
00214 #ifdef USE_LTDL
00215                 logit(NSLOG_RUNTIME_ERROR,FALSE,"Error: Could not load module '%s' -> %s\n",mod->filename,lt_dlerror());
00216 #else
00217                 logit(NSLOG_RUNTIME_ERROR,FALSE,"Error: Could not load module '%s' -> %s\n",mod->filename,dlerror());
00218 #endif
00219 
00220                 return ERROR;
00221                 }
00222 
00223         /* find module API version */
00224 #ifdef USE_LTDL
00225         module_version_ptr=(int *)lt_dlsym(mod->module_handle,"__neb_api_version");
00226 #else
00227         module_version_ptr=(int *)dlsym(mod->module_handle,"__neb_api_version");
00228 #endif
00229 
00230         /* mark the module as being loaded */
00231         mod->is_currently_loaded=TRUE;
00232 
00233         /* check the module API version */
00234         if(module_version_ptr==NULL || ((*module_version_ptr)!=CURRENT_NEB_API_VERSION)){
00235 
00236                 logit(NSLOG_RUNTIME_ERROR,FALSE,"Error: Module '%s' is using an old or unspecified version of the event broker API.  Module will be unloaded.\n",mod->filename);
00237 
00238                 neb_unload_module(mod,NEBMODULE_FORCE_UNLOAD,NEBMODULE_ERROR_API_VERSION);
00239 
00240                 return ERROR;
00241                 }
00242 
00243         /* locate the initialization function */
00244 #ifdef USE_LTDL
00245         mod->init_func=lt_dlsym(mod->module_handle,"nebmodule_init");
00246 #else
00247         mod->init_func=(void *)dlsym(mod->module_handle,"nebmodule_init");
00248 #endif
00249 
00250         /* if the init function could not be located, unload the module */
00251         if(mod->init_func==NULL){
00252 
00253                 logit(NSLOG_RUNTIME_ERROR,FALSE,"Error: Could not locate nebmodule_init() in module '%s'.  Module will be unloaded.\n",mod->filename);
00254 
00255                 neb_unload_module(mod,NEBMODULE_FORCE_UNLOAD,NEBMODULE_ERROR_NO_INIT);
00256 
00257                 return ERROR;
00258                 }
00259 
00260         /* run the module's init function */
00261         initfunc=mod->init_func;
00262         result=(*initfunc)(NEBMODULE_NORMAL_LOAD,mod->args,mod->module_handle);
00263 
00264         /* if the init function returned an error, unload the module */
00265         if(result!=OK){
00266 
00267                 logit(NSLOG_RUNTIME_ERROR,FALSE,"Error: Function nebmodule_init() in module '%s' returned an error.  Module will be unloaded.\n",mod->filename);
00268 
00269                 neb_unload_module(mod,NEBMODULE_FORCE_UNLOAD,NEBMODULE_ERROR_BAD_INIT);
00270 
00271                 return ERROR;
00272                 }
00273 
00274         logit(NSLOG_INFO_MESSAGE,FALSE,"Event broker module '%s' initialized successfully.\n",mod->filename);
00275 
00276         /* locate the de-initialization function (may or may not be present) */
00277 #ifdef USE_LTDL
00278         mod->deinit_func=lt_dlsym(mod->module_handle,"nebmodule_deinit");
00279 #else
00280         mod->deinit_func=(void *)dlsym(mod->module_handle,"nebmodule_deinit");
00281 #endif
00282 
00283         log_debug_info(DEBUGL_EVENTBROKER,0,"Module '%s' loaded with return code of '%d'\n",mod->filename,result);
00284         if(mod->deinit_func!=NULL)
00285                 log_debug_info(DEBUGL_EVENTBROKER,0,"nebmodule_deinit() found\n");
00286 
00287         return OK;
00288         }
00289 
00290 
00291 /* close (unload) all modules that are currently loaded */
00292 int neb_unload_all_modules(int flags, int reason){
00293         nebmodule *temp_module;
00294 
00295         for(temp_module=neb_module_list;temp_module;temp_module=temp_module->next){
00296 
00297                 /* skip modules that are not loaded */
00298                 if(temp_module->is_currently_loaded==FALSE)
00299                         continue;
00300 
00301                 /* skip modules that do not have a valid handle */
00302                 if(temp_module->module_handle==NULL)
00303                         continue;
00304 
00305                 /* close/unload the module */
00306                 neb_unload_module(temp_module,flags,reason);
00307                 }
00308 
00309         return OK;
00310         }
00311 
00312 
00313 
00314 /* close (unload) a particular module */
00315 int neb_unload_module(nebmodule *mod, int flags, int reason){
00316         int (*deinitfunc)(int,int);
00317         int result=OK;
00318 
00319         if(mod==NULL)
00320                 return ERROR;
00321 
00322         log_debug_info(DEBUGL_EVENTBROKER,0,"Attempting to unload module '%s': flags=%d, reason=%d\n",mod->filename,flags,reason);
00323 
00324         /* call the de-initialization function if available (and the module was initialized) */
00325         if(mod->deinit_func && reason!=NEBMODULE_ERROR_BAD_INIT){
00326 
00327                 deinitfunc=mod->deinit_func;
00328 
00329                 /* module can opt to not be unloaded */
00330                 result=(*deinitfunc)(flags,reason);
00331 
00332                 /* if module doesn't want to be unloaded, exit with error (unless its being forced) */
00333                 if(result!=OK && !(flags & NEBMODULE_FORCE_UNLOAD))
00334                         return ERROR;
00335                 }
00336 
00337         /* deregister all of the module's callbacks */
00338         neb_deregister_module_callbacks(mod);
00339 
00340         /* unload the module */
00341 #ifdef USE_LTDL
00342         result=lt_dlclose(mod->module_handle);
00343 #else
00344         result=dlclose(mod->module_handle);
00345 #endif
00346 
00347         /* mark the module as being unloaded */
00348         mod->is_currently_loaded=FALSE;
00349 
00350         log_debug_info(DEBUGL_EVENTBROKER,0,"Module '%s' unloaded successfully.\n",mod->filename);
00351 
00352         logit(NSLOG_INFO_MESSAGE,FALSE,"Event broker module '%s' deinitialized successfully.\n",mod->filename);
00353 
00354         return OK;
00355         }
00356 
00357 
00358 
00359 
00360 /****************************************************************************/
00361 /****************************************************************************/
00362 /* INFO FUNCTIONS                                                           */
00363 /****************************************************************************/
00364 /****************************************************************************/
00365 
00366 /* sets module information */
00367 int neb_set_module_info(void *handle, int type, char *data){
00368         nebmodule *temp_module=NULL;
00369 
00370         if(handle==NULL)
00371                 return NEBERROR_NOMODULE;
00372 
00373         /* check type */
00374         if(type<0 || type>=NEBMODULE_MODINFO_NUMITEMS)
00375                 return NEBERROR_MODINFOBOUNDS;
00376 
00377         /* find the module */
00378         for(temp_module=neb_module_list;temp_module!=NULL;temp_module=temp_module->next){
00379                 if((void *)temp_module->module_handle==(void *)handle)
00380                         break;
00381                 }
00382         if(temp_module==NULL)
00383                 return NEBERROR_BADMODULEHANDLE;
00384 
00385         /* free any previously allocated memory */
00386         my_free(temp_module->info[type]);
00387 
00388         /* allocate memory for the new data */
00389         if((temp_module->info[type]=(char *)strdup(data))==NULL)
00390                 return NEBERROR_NOMEM;
00391 
00392         return OK;
00393         }
00394 
00395 
00396 
00397 /****************************************************************************/
00398 /****************************************************************************/
00399 /* CALLBACK FUNCTIONS                                                       */
00400 /****************************************************************************/
00401 /****************************************************************************/
00402 
00403 /* allows a module to register a callback function */
00404 int neb_register_callback(int callback_type, void *mod_handle, int priority, int (*callback_func)(int,void *)){
00405         nebmodule *temp_module=NULL;
00406         nebcallback *new_callback=NULL;
00407         nebcallback *temp_callback=NULL;
00408         nebcallback *last_callback=NULL;
00409 
00410         if(callback_func==NULL)
00411                 return NEBERROR_NOCALLBACKFUNC;
00412 
00413         if(neb_callback_list==NULL)
00414                 return NEBERROR_NOCALLBACKLIST;
00415 
00416         if(mod_handle==NULL)
00417                 return NEBERROR_NOMODULEHANDLE;
00418 
00419         /* make sure the callback type is within bounds */
00420         if(callback_type<0 || callback_type>=NEBCALLBACK_NUMITEMS)
00421                 return NEBERROR_CALLBACKBOUNDS;
00422 
00423         /* make sure module handle is valid */
00424         for(temp_module=neb_module_list;temp_module;temp_module=temp_module->next){
00425                 if((void *)temp_module->module_handle==(void *)mod_handle)
00426                         break;
00427                 }
00428         if(temp_module==NULL)
00429                 return NEBERROR_BADMODULEHANDLE;
00430 
00431         /* allocate memory */
00432         new_callback=(nebcallback *)malloc(sizeof(nebcallback));
00433         if(new_callback==NULL)
00434                 return NEBERROR_NOMEM;
00435         
00436         new_callback->priority=priority;
00437         new_callback->module_handle=(void *)mod_handle;
00438         new_callback->callback_func=(void *)callback_func;
00439 
00440         /* add new function to callback list, sorted by priority (first come, first served for same priority) */
00441         new_callback->next=NULL;
00442         if(neb_callback_list[callback_type]==NULL)
00443                 neb_callback_list[callback_type]=new_callback;
00444         else{
00445                 last_callback=NULL;
00446                 for(temp_callback=neb_callback_list[callback_type];temp_callback!=NULL;temp_callback=temp_callback->next){
00447                         if(temp_callback->priority>new_callback->priority)
00448                                 break;
00449                         last_callback=temp_callback;
00450                         }
00451                 if(last_callback==NULL)
00452                         neb_callback_list[callback_type]=new_callback;
00453                 else{
00454                         if(temp_callback==NULL)
00455                                 last_callback->next=new_callback;
00456                         else{
00457                                 new_callback->next=temp_callback;
00458                                 last_callback->next=new_callback;
00459                                 }
00460                         }
00461                 }
00462 
00463         return OK;
00464         }
00465 
00466 
00467 
00468 /* dregisters all callback functions for a given module */
00469 int neb_deregister_module_callbacks(nebmodule *mod){
00470         nebcallback *temp_callback=NULL;
00471         nebcallback *next_callback=NULL;
00472         int callback_type=0;
00473 
00474         if(mod==NULL)
00475                 return NEBERROR_NOMODULE;
00476 
00477         if(neb_callback_list==NULL)
00478                 return OK;
00479 
00480         for(callback_type=0;callback_type<NEBCALLBACK_NUMITEMS;callback_type++){
00481                 for(temp_callback=neb_callback_list[callback_type];temp_callback!=NULL;temp_callback=next_callback){
00482                         next_callback=temp_callback->next;
00483                         if((void *)temp_callback->module_handle==(void *)mod->module_handle)
00484                                 neb_deregister_callback(callback_type,(int(*)(int,void*))temp_callback->callback_func);
00485                         }
00486 
00487                 }
00488 
00489         return OK;
00490         }
00491 
00492 
00493 /* allows a module to deregister a callback function */
00494 int neb_deregister_callback(int callback_type, int (*callback_func)(int,void *)){
00495         nebcallback *temp_callback=NULL;
00496         nebcallback *last_callback=NULL;
00497         nebcallback *next_callback=NULL;
00498 
00499         if(callback_func==NULL)
00500                 return NEBERROR_NOCALLBACKFUNC;
00501 
00502         if(neb_callback_list==NULL)
00503                 return NEBERROR_NOCALLBACKLIST;
00504 
00505         /* make sure the callback type is within bounds */
00506         if(callback_type<0 || callback_type>=NEBCALLBACK_NUMITEMS)
00507                 return NEBERROR_CALLBACKBOUNDS;
00508 
00509         /* find the callback to remove */
00510         for(temp_callback=last_callback=neb_callback_list[callback_type];temp_callback!=NULL;temp_callback=next_callback){
00511                 next_callback=temp_callback->next;
00512 
00513                 /* we found it */
00514                 if(temp_callback->callback_func==(void *)callback_func)
00515                         break;
00516 
00517                 last_callback=temp_callback;
00518                 }
00519 
00520         /* we couldn't find the callback */
00521         if(temp_callback==NULL)
00522                 return NEBERROR_CALLBACKNOTFOUND;
00523 
00524         else{
00525                 /* only one item in the list */
00526                 if (temp_callback!=last_callback->next)
00527                         neb_callback_list[callback_type]=NULL;
00528                 else
00529                         last_callback->next=next_callback;
00530                 my_free(temp_callback);
00531                 }
00532         
00533         return OK;
00534         }
00535 
00536 
00537 
00538 /* make callbacks to modules */
00539 int neb_make_callbacks(int callback_type, void *data){
00540         nebcallback *temp_callback=NULL, *next_callback=NULL;
00541         int (*callbackfunc)(int,void *);
00542         register int cbresult=0;
00543         int total_callbacks=0;
00544 
00545         /* make sure callback list is initialized */
00546         if(neb_callback_list==NULL)
00547                 return ERROR;
00548 
00549         /* make sure the callback type is within bounds */
00550         if(callback_type<0 || callback_type>=NEBCALLBACK_NUMITEMS)
00551                 return ERROR;
00552 
00553         log_debug_info(DEBUGL_EVENTBROKER,1,"Making callbacks (type %d)...\n",callback_type);
00554 
00555         /* make the callbacks... */
00556         for(temp_callback=neb_callback_list[callback_type];temp_callback;temp_callback=next_callback){
00557                 /* Save temp_callback->next because if the callback function de-registers itself temp_callback's */
00558                 /* pointer isn't guaranteed to be usable anymore (neb_deregister_callback will free() it) */
00559                 next_callback=temp_callback->next;
00560                 callbackfunc=temp_callback->callback_func;
00561                 cbresult=callbackfunc(callback_type,data);
00562                 temp_callback = next_callback;
00563 
00564                 total_callbacks++;
00565                 log_debug_info(DEBUGL_EVENTBROKER,2,"Callback #%d (type %d) return code = %d\n",total_callbacks,callback_type,cbresult);
00566 
00567                 /* module wants to cancel callbacks to other modules (and potentially cancel the default Icinga handling of an event) */
00568                 if(cbresult==NEBERROR_CALLBACKCANCEL)
00569                         break;
00570 
00571                 /* module wants to override default Icinga handling of an event */
00572                 /* not sure if we should bail out here just because one module wants to override things - what about other modules? EG 12/11/2006 */
00573                 else if(cbresult==NEBERROR_CALLBACKOVERRIDE)
00574                         break;
00575                 }
00576 
00577         return cbresult;
00578         }
00579 
00580 
00581 
00582 /* initialize callback list */
00583 int neb_init_callback_list(void){
00584         register int x=0;
00585 
00586         /* allocate memory for the callback list */
00587         neb_callback_list=(nebcallback **)malloc(NEBCALLBACK_NUMITEMS*sizeof(nebcallback *));
00588         if(neb_callback_list==NULL)
00589                 return ERROR;
00590 
00591         /* initialize list pointers */
00592         for(x=0;x<NEBCALLBACK_NUMITEMS;x++)
00593                 neb_callback_list[x]=NULL;
00594 
00595         return OK;
00596         }
00597 
00598 
00599 /* free memory allocated to callback list */
00600 int neb_free_callback_list(void){
00601         nebcallback *temp_callback=NULL;
00602         nebcallback *next_callback=NULL;
00603         register int x=0;
00604 
00605         if(neb_callback_list==NULL)
00606                 return OK;
00607 
00608         for(x=0;x<NEBCALLBACK_NUMITEMS;x++){
00609 
00610                 for(temp_callback=neb_callback_list[x];temp_callback!=NULL;temp_callback=next_callback){
00611                         next_callback=temp_callback->next;
00612                         my_free(temp_callback);
00613                         }
00614 
00615                 neb_callback_list[x]=NULL;
00616                 }
00617 
00618         my_free(neb_callback_list);
00619 
00620         return OK;
00621         }
00622 
00623 #endif
 All Data Structures Files Functions Variables Typedefs Defines