MaxScale/server/core/load_utils.c
2014-08-19 13:22:40 +01:00

391 lines
10 KiB
C

/*
* This file is distributed as part of the SkySQL Gateway. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright SkySQL Ab 2013
*/
/**
* @file load_utils.c Utility functions to aid the loading of dynamic
* modules into the gateway
*
* @verbatim
* Revision History
*
* Date Who Description
* 13/06/13 Mark Riddoch Initial implementation
* 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is
* defined in the loaded module.
* Also updated to call fixed GetModuleObject
* 02/06/14 Mark Riddoch Addition of module info
*
* @endverbatim
*/
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <modules.h>
#include <modinfo.h>
#include <skygw_utils.h>
#include <log_manager.h>
extern int lm_enabled_logfiles_bitmask;
static MODULES *registered = NULL;
static MODULES *find_module(const char *module);
static void register_module(const char *module,
const char *type,
void *dlhandle,
char *version,
void *modobj,
MODULE_INFO *info);
static void unregister_module(const char *module);
char* get_maxscale_home(void)
{
char* home = getenv("MAXSCALE_HOME");
if (home == NULL)
{
home = "/usr/local/skysql/MaxScale";
}
return home;
}
/**
* Load the dynamic library related to a gateway module. The routine
* will look for library files in the current directory,
* $MAXSCALE_HOME/modules and /usr/local/skysql/MaxScale/modules.
*
* @param module Name of the module to load
* @param type Type of module, used purely for registration
* @return The module specific entry point structure or NULL
*/
void *
load_module(const char *module, const char *type)
{
char *home, *version;
char fname[MAXPATHLEN];
void *dlhandle, *sym;
char *(*ver)();
void *(*ep)(), *modobj;
MODULES *mod;
MODULE_INFO *mod_info = NULL;
if ((mod = find_module(module)) == NULL)
{
/*<
* The module is not already loaded
*
* Search of the shared object.
*/
sprintf(fname, "./lib%s.so", module);
if (access(fname, F_OK) == -1)
{
home = get_maxscale_home ();
sprintf(fname, "%s/modules/lib%s.so", home, module);
if (access(fname, F_OK) == -1)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unable to find library for "
"module: %s.",
module)));
return NULL;
}
}
if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unable to load library for module: "
"%s\n\n\t\t %s."
"\n\n",
module,
dlerror())));
return NULL;
}
if ((sym = dlsym(dlhandle, "version")) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Version interface not supported by "
"module: %s\n\t\t\t %s.",
module,
dlerror())));
dlclose(dlhandle);
return NULL;
}
ver = sym;
version = ver();
/*
* If the module has a ModuleInit function cal it now.
*/
if ((sym = dlsym(dlhandle, "ModuleInit")) != NULL)
{
void (*ModuleInit)() = sym;
ModuleInit();
}
if ((sym = dlsym(dlhandle, "info")) != NULL)
{
int fatal = 0;
mod_info = sym;
if (strcmp(type, MODULE_PROTOCOL) == 0
&& mod_info->modapi != MODULE_API_PROTOCOL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the protocol API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_ROUTER) == 0
&& mod_info->modapi != MODULE_API_ROUTER)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the router API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_MONITOR) == 0
&& mod_info->modapi != MODULE_API_MONITOR)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the monitor API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_FILTER) == 0
&& mod_info->modapi != MODULE_API_FILTER)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the filter API.\n",
module)));
fatal = 1;
}
if (fatal)
{
dlclose(dlhandle);
return NULL;
}
}
if ((sym = dlsym(dlhandle, "GetModuleObject")) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Expected entry point interface missing "
"from module: %s\n\t\t\t %s.",
module,
dlerror())));
dlclose(dlhandle);
return NULL;
}
ep = sym;
modobj = ep();
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Loaded module %s: %s from %s",
module,
version,
fname)));
register_module(module, type, dlhandle, version, modobj, mod_info);
}
else
{
/*
* The module is already loaded, get the entry points again and
* return a reference to the already loaded module.
*/
modobj = mod->modobj;
}
return modobj;
}
/**
* Unload a module.
*
* No errors are returned since it is not clear that much can be done
* to fix issues relating to unloading modules.
*
* @param module The name of the module
*/
void
unload_module(const char *module)
{
MODULES *mod = find_module(module);
void *handle;
if (!mod)
return;
handle = mod->handle;
unregister_module(module);
dlclose(handle);
}
/**
* Find a module that has been previously loaded and return the handle for that
* library
*
* @param module The name of the module
* @return The module handle or NULL if it was not found
*/
static MODULES *
find_module(const char *module)
{
MODULES *mod = registered;
while (mod)
if (strcmp(mod->module, module) == 0)
return mod;
else
mod = mod->next;
return NULL;
}
/**
* Register a newly loaded module. The registration allows for single copies
* to be loaded and cached entry point information to be return.
*
* @param module The name of the module loaded
* @param type The type of the module loaded
* @param dlhandle The handle returned by dlopen
* @param version The version string returned by the module
* @param modobj The module object
* @param mod_info The module information
*/
static void
register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj, MODULE_INFO *mod_info)
{
MODULES *mod;
if ((mod = malloc(sizeof(MODULES))) == NULL)
return;
mod->module = strdup(module);
mod->type = strdup(type);
mod->handle = dlhandle;
mod->version = strdup(version);
mod->modobj = modobj;
mod->next = registered;
mod->info = mod_info;
registered = mod;
}
/**
* Unregister a module
*
* @param module The name of the module to remove
*/
static void
unregister_module(const char *module)
{
MODULES *mod = find_module(module);
MODULES *ptr;
if (!mod)
return; // Module not found
if (registered == mod)
registered = mod->next;
else
{
ptr = registered;
while (ptr && ptr->next != mod)
ptr = ptr->next;
}
/*<
* The module is now not in the linked list and all
* memory related to it can be freed
*/
free(mod->module);
free(mod->type);
free(mod->version);
free(mod);
}
/**
* Print Modules
*
* Diagnostic routine to display all the loaded modules
*/
void
printModules()
{
MODULES *ptr = registered;
printf("%-15s | %-11s | Version\n", "Module Name", "Module Type");
printf("-----------------------------------------------------\n");
while (ptr)
{
printf("%-15s | %-11s | %s\n", ptr->module, ptr->type, ptr->version);
ptr = ptr->next;
}
}
/**
* Print Modules to a DCB
*
* Diagnostic routine to display all the loaded modules
*/
void
dprintAllModules(DCB *dcb)
{
MODULES *ptr = registered;
dcb_printf(dcb, "Modules.\n");
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n");
dcb_printf(dcb, "%-15s | %-11s | Version | API | Status\n", "Module Name", "Module Type");
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n");
while (ptr)
{
dcb_printf(dcb, "%-15s | %-11s | %-7s ", ptr->module, ptr->type, ptr->version);
if (ptr->info)
dcb_printf(dcb, "| %d.%d.%d | %s",
ptr->info->api_version.major,
ptr->info->api_version.minor,
ptr->info->api_version.patch,
ptr->info->status == MODULE_IN_DEVELOPMENT
? "In Development"
: (ptr->info->status == MODULE_ALPHA_RELEASE
? "Alpha"
: (ptr->info->status == MODULE_BETA_RELEASE
? "Beta"
: (ptr->info->status == MODULE_GA
? "GA"
: (ptr->info->status == MODULE_EXPERIMENTAL
? "Experimental" : "Unknown")))));
dcb_printf(dcb, "\n");
ptr = ptr->next;
}
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n\n");
}