390 lines
10 KiB
C
390 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
|
|
*/
|
|
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");
|
|
}
|