Exemplo n.º 1
0
Arquivo: nebmods.c Projeto: atj/nagios
/* load a particular module */
int neb_load_module(nebmodule *mod) {
	int (*initfunc)(int, char *, void *);
	int *module_version_ptr = NULL;
	char output_file[PATH_MAX];
	int dest_fd, result = OK;

	if(mod == NULL || mod->filename == NULL)
		return ERROR;

	/* don't reopen the module */
	if(mod->is_currently_loaded == TRUE)
		return OK;

	/* don't load modules unless they should be loaded */
	if(mod->should_be_loaded == FALSE)
		return ERROR;

	/**********
	   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'),
	   you do not alter the inode of the original file.  Since the original file/module is memory-mapped in some fashion, Nagios will segfault the next
	   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
	   modules while Nagios is running.  A workaround is to (1) 'mv' the original/loaded module file to another name (on the same filesystem)
	   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
	   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().
	   This prevents other processed from unintentially overwriting the original file, which would cause Nagios to crash.  However, if we delete the file
	   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
	   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
	   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.
	   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.
	************/

	/*
	 * open a temp file for copying the module. We use my_fdcopy() so
	 * we re-use the destination file descriptor returned by mkstemp(3),
	 * which we have to close ourselves.
	 */
	snprintf(output_file, sizeof(output_file) - 1, "%s/nebmodXXXXXX", temp_path);
	dest_fd = mkstemp(output_file);
	result = my_fdcopy(mod->filename, output_file, dest_fd);
	close(dest_fd);
	if(result == ERROR) {
		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Failed to safely copy module '%s'. The module will not be loaded\n", mod->filename);
		return ERROR;
		}

	/* load the module (use the temp copy we just made) */
#ifdef USE_LTDL
	mod->module_handle = lt_dlopen(output_file);
#else
	mod->module_handle = (void *)dlopen(output_file, RTLD_NOW | RTLD_GLOBAL);
#endif
	if(mod->module_handle == NULL) {

#ifdef USE_LTDL
		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not load module '%s' -> %s\n", mod->filename, lt_dlerror());
#else
		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not load module '%s' -> %s\n", mod->filename, dlerror());
#endif

		return ERROR;
		}

	/* mark the module as being loaded */
	mod->is_currently_loaded = TRUE;

	/* delete the temp copy of the module we just created and loaded */
	/* this will prevent other processes from overwriting the file (using the same inode), which would cause Nagios to crash */
	/* the kernel will keep the deleted file in memory until we unload it */
	/* NOTE: This *should* be portable to most Unices, but I've only tested it on Linux */
	if(unlink(output_file) == -1) {
		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not delete temporary file '%s' used for module '%s'.  The module will be unloaded: %s\n", output_file, mod->filename, strerror(errno));
		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_API_VERSION);

		return ERROR;
		}

	/* find module API version */
#ifdef USE_LTDL
	module_version_ptr = (int *)lt_dlsym(mod->module_handle, "__neb_api_version");
#else
	module_version_ptr = (int *)dlsym(mod->module_handle, "__neb_api_version");
#endif

	/* check the module API version */
	if(module_version_ptr == NULL || ((*module_version_ptr) != CURRENT_NEB_API_VERSION)) {

		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);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_API_VERSION);

		return ERROR;
		}

	/* locate the initialization function */
#ifdef USE_LTDL
	mod->init_func = lt_dlsym(mod->module_handle, "nebmodule_init");
#else
	mod->init_func = (void *)dlsym(mod->module_handle, "nebmodule_init");
#endif

	/* if the init function could not be located, unload the module */
	if(mod->init_func == NULL) {

		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not locate nebmodule_init() in module '%s'.  Module will be unloaded.\n", mod->filename);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_NO_INIT);

		return ERROR;
		}

	/* run the module's init function */
	initfunc = mod->init_func;
	result = (*initfunc)(NEBMODULE_NORMAL_LOAD, mod->args, mod->module_handle);

	/* if the init function returned an error, unload the module */
	if(result != OK) {

		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Function nebmodule_init() in module '%s' returned an error.  Module will be unloaded.\n", mod->filename);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_BAD_INIT);

		return ERROR;
		}

	logit(NSLOG_INFO_MESSAGE, FALSE, "Event broker module '%s' initialized successfully.\n", mod->filename);

	/* locate the de-initialization function (may or may not be present) */
#ifdef USE_LTDL
	mod->deinit_func = lt_dlsym(mod->module_handle, "nebmodule_deinit");
#else
	mod->deinit_func = (void *)dlsym(mod->module_handle, "nebmodule_deinit");
#endif

	log_debug_info(DEBUGL_EVENTBROKER, 0, "Module '%s' loaded with return code of '%d'\n", mod->filename, result);
	if(mod->deinit_func != NULL)
		log_debug_info(DEBUGL_EVENTBROKER, 0, "nebmodule_deinit() found\n");

	return OK;
	}
Exemplo n.º 2
0
/* load a particular module */
int neb_load_module(nebmodule *mod) {
	int (*initfunc)(int, char *, void *);
	int *module_version_ptr = NULL;
	int dest_fd, result = OK;
	char output_file[PATH_MAX];

	if (mod == NULL || mod->filename == NULL)
		return ERROR;

	/* don't reopen the module */
	if (mod->is_currently_loaded == TRUE)
		return OK;

	/* don't load modules unless they should be loaded */
	if (mod->should_be_loaded == FALSE)
		return ERROR;

	/**********
	   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'),
	   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
	   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
	   modules while Icinga is running.  A workaround is to (1) 'mv' the original/loaded module file to another name (on the same filesystem)
	   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
	   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().
	   This prevents other processed from unintentially overwriting the original file, which would cause Icinga to crash.  However, if we delete the file
	   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
	   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
	   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.
	   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.
	************/

	/*
	 * create a temp copy with a different name,
	 * not sharing the global symbol space with
	 * other neb modules of the same binary.
	 * check https://dev.icinga.org/issues/4199
	 */
	snprintf(output_file, sizeof(output_file)-1, "%s/icinganebmodXXXXXX", temp_path);
	dest_fd = mkstemp(output_file);
	result = my_fdcopy(mod->filename, output_file, dest_fd);
	close(dest_fd);

	if (result == ERROR) {
		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Failed to safely copy module '%s' to '%s'. The module will not be loaded\n", mod->filename, output_file);
		return ERROR;
	}

	/* load the module (use the temp copy we just made) */
	mod->module_handle = dlopen(output_file, RTLD_NOW | RTLD_GLOBAL);

	if (mod->module_handle == NULL) {

		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not load module '%s' -> %s\n", mod->filename, dlerror());

		return ERROR;
	}

	/* mark the module as being loaded */
	mod->is_currently_loaded = TRUE;

	/* delete the temp copy of the module just loaded */
	if (unlink(output_file) == -1) {
		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not delete temporary file '%s' used for module '%s'. The module will be unloaded: %s", output_file, mod->filename, strerror(errno));
		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_API_VERSION);
	}

	/* if we're debugging, create a copy for debuggers finding the correct symbols */
	if (daemon_dumps_core == TRUE) {
		dest_fd = open(output_file, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH);
		result = my_fdcopy(mod->filename, output_file, dest_fd);
		mod->dl_file = strdup(output_file);
	}

	/* find module API version */
	module_version_ptr = (int *)dlsym(mod->module_handle, "__neb_api_version");

	/* check the module API version */
	if (module_version_ptr == NULL || ((*module_version_ptr) != CURRENT_NEB_API_VERSION)) {

		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);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_API_VERSION);

		return ERROR;
	}

	/* locate the initialization function */
	mod->init_func = (void *)dlsym(mod->module_handle, "nebmodule_init");

	/* if the init function could not be located, unload the module */
	if (mod->init_func == NULL) {

		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Could not locate nebmodule_init() in module '%s'.  Module will be unloaded.\n", mod->filename);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_NO_INIT);

		return ERROR;
	}

	/* run the module's init function */
	initfunc = mod->init_func;
	result = (*initfunc)(NEBMODULE_NORMAL_LOAD, mod->args, mod->module_handle);

	/* if the init function returned an error, unload the module */
	if (result != OK) {

		logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Function nebmodule_init() in module '%s' returned an error.  Module will be unloaded.\n", mod->filename);

		neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_BAD_INIT);

		return ERROR;
	}

	/* locate the specific modules we know about (idomod, ...) and require minimum version, after calling nebmodule_init, this should be set */
	/* check if module exports its title? */
	if(mod->info[NEBMODULE_MODINFO_TITLE] != NULL) {
		/* check if we are going to load idomod, which we know about */
		if(strstr(mod->info[NEBMODULE_MODINFO_TITLE], "IDOMOD") != NULL) {
			/* check if the version complies with the core's version, as they are tied together */
			if(strcmp(mod->info[NEBMODULE_MODINFO_VERSION], PROGRAM_VERSION) != 0) {
				logit(NSLOG_RUNTIME_ERROR, FALSE, "Error: Module '%s' exports version '%s' different to core version '%s'.  Module will be unloaded.\n", mod->filename, mod->info[NEBMODULE_MODINFO_VERSION], PROGRAM_VERSION);
				neb_unload_module(mod, NEBMODULE_FORCE_UNLOAD, NEBMODULE_ERROR_IDO_VERSION);
				return ERROR;
			}
			logit(NSLOG_INFO_MESSAGE, FALSE, "Event broker module '%s' version '%s' from '%s' initialized successfully.\n", mod->info[NEBMODULE_MODINFO_TITLE], mod->info[NEBMODULE_MODINFO_VERSION], mod->filename);
		} else {
			logit(NSLOG_INFO_MESSAGE, FALSE, "Event broker module '%s' from '%s' initialized successfully.\n", mod->info[NEBMODULE_MODINFO_TITLE], mod->filename);
		}
	} else {
		logit(NSLOG_INFO_MESSAGE, FALSE, "Event broker module '%s' initialized successfully.\n", mod->filename);
	}

	/* locate the de-initialization function (may or may not be present) */
	mod->deinit_func = dlsym(mod->module_handle, "nebmodule_deinit");

	log_debug_info(DEBUGL_EVENTBROKER, 0, "Module '%s' loaded with return code of '%d'\n", mod->filename, result);
	if (mod->deinit_func != NULL)
		log_debug_info(DEBUGL_EVENTBROKER, 0, "nebmodule_deinit() found\n");

	return OK;
}