
The header was not merged with queryclassifier.hh since the latter does not include the former.
732 lines
20 KiB
C++
732 lines
20 KiB
C++
/*
|
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
*
|
|
* Change Date: 2022-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
|
|
/**
|
|
* @file load_utils.c Utility functions for loading of modules
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <dlfcn.h>
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
#include <maxscale/modinfo.h>
|
|
#include <maxscale/version.h>
|
|
#include <maxscale/paths.h>
|
|
#include <maxscale/alloc.h>
|
|
#include <maxscale/json_api.h>
|
|
#include <maxscale/modulecmd.hh>
|
|
#include <maxscale/protocol.hh>
|
|
#include <maxscale/router.hh>
|
|
#include <maxscale/filter.hh>
|
|
#include <maxscale/authenticator.hh>
|
|
#include <maxscale/monitor.hh>
|
|
#include <maxscale/query_classifier.hh>
|
|
|
|
#include "internal/modules.hh"
|
|
#include "internal/config.hh"
|
|
|
|
namespace
|
|
{
|
|
|
|
typedef struct loaded_module
|
|
{
|
|
char* module; /**< The name of the module */
|
|
char* type; /**< The module type */
|
|
char* version; /**< Module version */
|
|
void* handle; /**< The handle returned by dlopen */
|
|
void* modobj; /**< The module "object" this is the set of entry points */
|
|
MXS_MODULE* info; /**< The module information */
|
|
struct loaded_module* next; /**< Next module in the linked list */
|
|
} LOADED_MODULE;
|
|
|
|
struct NAME_MAPPING
|
|
{
|
|
const char* type; // The type of the module.
|
|
const char* from; // Old module name.
|
|
const char* to; // What should be loaded instead.
|
|
bool warned; // Whether a warning has been logged.
|
|
};
|
|
}
|
|
|
|
static NAME_MAPPING name_mappings[] =
|
|
{
|
|
{MODULE_MONITOR, "mysqlmon", "mariadbmon", false},
|
|
{MODULE_PROTOCOL, "mysqlclient", "mariadbclient", false},
|
|
{MODULE_PROTOCOL, "mysqlbackend", "mariadbbackend", false}
|
|
};
|
|
|
|
static const size_t N_NAME_MAPPINGS = sizeof(name_mappings) / sizeof(name_mappings[0]);
|
|
|
|
static LOADED_MODULE* registered = NULL;
|
|
|
|
static LOADED_MODULE* find_module(const char* module);
|
|
static LOADED_MODULE* register_module(const char* module,
|
|
const char* type,
|
|
void* dlhandle,
|
|
MXS_MODULE* mod_info);
|
|
static void unregister_module(const char* module);
|
|
|
|
static bool api_version_mismatch(const MXS_MODULE* mod_info, const char* module)
|
|
{
|
|
bool rval = false;
|
|
MXS_MODULE_VERSION api = {};
|
|
|
|
switch (mod_info->modapi)
|
|
{
|
|
case MXS_MODULE_API_PROTOCOL:
|
|
api = MXS_PROTOCOL_VERSION;
|
|
break;
|
|
|
|
case MXS_MODULE_API_AUTHENTICATOR:
|
|
api = MXS_AUTHENTICATOR_VERSION;
|
|
break;
|
|
|
|
case MXS_MODULE_API_ROUTER:
|
|
api = MXS_ROUTER_VERSION;
|
|
break;
|
|
|
|
case MXS_MODULE_API_MONITOR:
|
|
api = MXS_MONITOR_VERSION;
|
|
break;
|
|
|
|
case MXS_MODULE_API_FILTER:
|
|
api = MXS_FILTER_VERSION;
|
|
break;
|
|
|
|
case MXS_MODULE_API_QUERY_CLASSIFIER:
|
|
api = MXS_QUERY_CLASSIFIER_VERSION;
|
|
break;
|
|
|
|
default:
|
|
MXS_ERROR("Unknown module type: 0x%02hhx", mod_info->modapi);
|
|
mxb_assert(!true);
|
|
break;
|
|
}
|
|
|
|
if (api.major != mod_info->api_version.major
|
|
|| api.minor != mod_info->api_version.minor
|
|
|| api.patch != mod_info->api_version.patch)
|
|
{
|
|
MXS_ERROR("API version mismatch for '%s': Need version %d.%d.%d, have %d.%d.%d",
|
|
module,
|
|
api.major,
|
|
api.minor,
|
|
api.patch,
|
|
mod_info->api_version.major,
|
|
mod_info->api_version.minor,
|
|
mod_info->api_version.patch);
|
|
rval = true;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static bool check_module(const MXS_MODULE* mod_info, const char* type, const char* module)
|
|
{
|
|
bool success = true;
|
|
|
|
if (strcmp(type, MODULE_PROTOCOL) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_PROTOCOL)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the protocol API.", module);
|
|
success = false;
|
|
}
|
|
if (strcmp(type, MODULE_AUTHENTICATOR) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_AUTHENTICATOR)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the authenticator API.", module);
|
|
success = false;
|
|
}
|
|
if (strcmp(type, MODULE_ROUTER) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_ROUTER)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the router API.", module);
|
|
success = false;
|
|
}
|
|
if (strcmp(type, MODULE_MONITOR) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_MONITOR)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the monitor API.", module);
|
|
success = false;
|
|
}
|
|
if (strcmp(type, MODULE_FILTER) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_FILTER)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the filter API.", module);
|
|
success = false;
|
|
}
|
|
if (strcmp(type, MODULE_QUERY_CLASSIFIER) == 0
|
|
&& mod_info->modapi != MXS_MODULE_API_QUERY_CLASSIFIER)
|
|
{
|
|
MXS_ERROR("Module '%s' does not implement the query classifier API.", module);
|
|
success = false;
|
|
}
|
|
|
|
if (api_version_mismatch(mod_info, module))
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
if (mod_info->version == NULL)
|
|
{
|
|
MXS_ERROR("Module '%s' does not define a version string", module);
|
|
success = false;
|
|
}
|
|
|
|
if (mod_info->module_object == NULL)
|
|
{
|
|
MXS_ERROR("Module '%s' does not define a module object", module);
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void* load_module(const char* module, const char* type)
|
|
{
|
|
mxb_assert(module && type);
|
|
LOADED_MODULE* mod;
|
|
|
|
module = mxs_module_get_effective_name(module);
|
|
|
|
if ((mod = find_module(module)) == NULL)
|
|
{
|
|
size_t len = strlen(module);
|
|
char lc_module[len + 1];
|
|
lc_module[len] = 0;
|
|
std::transform(module, module + len, lc_module, tolower);
|
|
|
|
/** The module is not already loaded, search for the shared object */
|
|
char fname[MAXPATHLEN + 1];
|
|
snprintf(fname, MAXPATHLEN + 1, "%s/lib%s.so", get_libdir(), lc_module);
|
|
|
|
if (access(fname, F_OK) == -1)
|
|
{
|
|
MXS_ERROR("Unable to find library for "
|
|
"module: %s. Module dir: %s",
|
|
module,
|
|
get_libdir());
|
|
return NULL;
|
|
}
|
|
|
|
void* dlhandle = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
|
|
|
|
if (dlhandle == NULL)
|
|
{
|
|
MXS_ERROR("Unable to load library for module: "
|
|
"%s\n\n\t\t %s."
|
|
"\n\n",
|
|
module,
|
|
dlerror());
|
|
return NULL;
|
|
}
|
|
|
|
void* sym = dlsym(dlhandle, MXS_MODULE_SYMBOL_NAME);
|
|
|
|
if (sym == NULL)
|
|
{
|
|
MXS_ERROR("Expected entry point interface missing "
|
|
"from module: %s\n\t\t\t %s.",
|
|
module,
|
|
dlerror());
|
|
dlclose(dlhandle);
|
|
return NULL;
|
|
}
|
|
|
|
void* (* entry_point)() = (void*(*)())sym;
|
|
MXS_MODULE* mod_info = (MXS_MODULE*)entry_point();
|
|
|
|
if (!check_module(mod_info, type, module)
|
|
|| (mod = register_module(module, type, dlhandle, mod_info)) == NULL)
|
|
{
|
|
dlclose(dlhandle);
|
|
return NULL;
|
|
}
|
|
|
|
MXS_NOTICE("Loaded module %s: %s from %s", module, mod_info->version, fname);
|
|
}
|
|
|
|
return mod->modobj;
|
|
}
|
|
|
|
void unload_module(const char* module)
|
|
{
|
|
module = mxs_module_get_effective_name(module);
|
|
|
|
LOADED_MODULE* mod = find_module(module);
|
|
|
|
if (mod)
|
|
{
|
|
void* 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 LOADED_MODULE* find_module(const char* module)
|
|
{
|
|
LOADED_MODULE* mod = registered;
|
|
|
|
if (module)
|
|
{
|
|
while (mod)
|
|
{
|
|
if (strcasecmp(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
|
|
* @return The new registered module or NULL on memory allocation failure
|
|
*/
|
|
static LOADED_MODULE* register_module(const char* module,
|
|
const char* type,
|
|
void* dlhandle,
|
|
MXS_MODULE* mod_info)
|
|
{
|
|
module = MXS_STRDUP(module);
|
|
type = MXS_STRDUP(type);
|
|
char* version = MXS_STRDUP(mod_info->version);
|
|
|
|
LOADED_MODULE* mod = (LOADED_MODULE*)MXS_MALLOC(sizeof(LOADED_MODULE));
|
|
|
|
if (!module || !type || !version || !mod)
|
|
{
|
|
MXS_FREE((void*)module);
|
|
MXS_FREE((void*)type);
|
|
MXS_FREE(version);
|
|
MXS_FREE(mod);
|
|
return NULL;
|
|
}
|
|
|
|
mod->module = (char*)module;
|
|
mod->type = (char*)type;
|
|
mod->handle = dlhandle;
|
|
mod->version = version;
|
|
mod->modobj = mod_info->module_object;
|
|
mod->next = registered;
|
|
mod->info = mod_info;
|
|
registered = mod;
|
|
return mod;
|
|
}
|
|
|
|
/**
|
|
* Unregister a module
|
|
*
|
|
* @param module The name of the module to remove
|
|
*/
|
|
static void unregister_module(const char* module)
|
|
{
|
|
LOADED_MODULE* mod = find_module(module);
|
|
LOADED_MODULE* ptr;
|
|
|
|
if (!mod)
|
|
{
|
|
return; // Module not found
|
|
}
|
|
if (registered == mod)
|
|
{
|
|
registered = mod->next;
|
|
}
|
|
else
|
|
{
|
|
ptr = registered;
|
|
while (ptr && ptr->next != mod)
|
|
{
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
/*<
|
|
* Remove the module to be be freed from the list.
|
|
*/
|
|
if (ptr && (ptr->next == mod))
|
|
{
|
|
ptr->next = ptr->next->next;
|
|
}
|
|
}
|
|
|
|
/*<
|
|
* The module is now not in the linked list and all
|
|
* memory related to it can be freed
|
|
*/
|
|
dlclose(mod->handle);
|
|
MXS_FREE(mod->module);
|
|
MXS_FREE(mod->type);
|
|
MXS_FREE(mod->version);
|
|
MXS_FREE(mod);
|
|
}
|
|
|
|
void unload_all_modules()
|
|
{
|
|
while (registered)
|
|
{
|
|
unregister_module(registered->module);
|
|
}
|
|
}
|
|
|
|
void printModules()
|
|
{
|
|
LOADED_MODULE* 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;
|
|
}
|
|
}
|
|
|
|
void dprintAllModules(DCB* dcb)
|
|
{
|
|
LOADED_MODULE* ptr = registered;
|
|
|
|
dcb_printf(dcb, "Modules.\n");
|
|
dcb_printf(dcb, "----------------+-----------------+---------+-------+-------------------------\n");
|
|
dcb_printf(dcb, "%-15s | %-15s | Version | API | Status\n", "Module Name", "Module Type");
|
|
dcb_printf(dcb, "----------------+-----------------+---------+-------+-------------------------\n");
|
|
while (ptr)
|
|
{
|
|
dcb_printf(dcb, "%-15s | %-15s | %-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 == MXS_MODULE_IN_DEVELOPMENT ?
|
|
"In Development" :
|
|
(ptr->info->status == MXS_MODULE_ALPHA_RELEASE ?
|
|
"Alpha" :
|
|
(ptr->info->status == MXS_MODULE_BETA_RELEASE ?
|
|
"Beta" :
|
|
(ptr->info->status == MXS_MODULE_GA ?
|
|
"GA" :
|
|
(ptr->info->status == MXS_MODULE_EXPERIMENTAL ?
|
|
"Experimental" : "Unknown")))));
|
|
}
|
|
dcb_printf(dcb, "\n");
|
|
ptr = ptr->next;
|
|
}
|
|
dcb_printf(dcb, "----------------+-----------------+---------+-------+-------------------------\n\n");
|
|
}
|
|
|
|
struct cb_param
|
|
{
|
|
json_t* commands;
|
|
const char* domain;
|
|
const char* host;
|
|
};
|
|
|
|
bool modulecmd_cb(const MODULECMD* cmd, void* data)
|
|
{
|
|
cb_param* d = static_cast<cb_param*>(data);
|
|
|
|
json_t* obj = json_object();
|
|
json_object_set_new(obj, CN_ID, json_string(cmd->identifier));
|
|
json_object_set_new(obj, CN_TYPE, json_string(CN_MODULE_COMMAND));
|
|
|
|
json_t* attr = json_object();
|
|
const char* method = MODULECMD_MODIFIES_DATA(cmd) ? "POST" : "GET";
|
|
json_object_set_new(attr, CN_METHOD, json_string(method));
|
|
json_object_set_new(attr, CN_ARG_MIN, json_integer(cmd->arg_count_min));
|
|
json_object_set_new(attr, CN_ARG_MAX, json_integer(cmd->arg_count_max));
|
|
json_object_set_new(attr, CN_DESCRIPTION, json_string(cmd->description));
|
|
|
|
json_t* param = json_array();
|
|
|
|
for (int i = 0; i < cmd->arg_count_max; i++)
|
|
{
|
|
json_t* p = json_object();
|
|
json_object_set_new(p, CN_DESCRIPTION, json_string(cmd->arg_types[i].description));
|
|
json_object_set_new(p, CN_TYPE, json_string(modulecmd_argtype_to_str(&cmd->arg_types[i])));
|
|
json_object_set_new(p, CN_REQUIRED, json_boolean(MODULECMD_ARG_IS_REQUIRED(&cmd->arg_types[i])));
|
|
json_array_append_new(param, p);
|
|
}
|
|
|
|
std::string s = d->domain;
|
|
s += "/";
|
|
s += cmd->identifier;
|
|
mxb_assert(strcasecmp(d->domain, cmd->domain) == 0);
|
|
|
|
json_object_set_new(obj, CN_LINKS, mxs_json_self_link(d->host, CN_MODULES, s.c_str()));
|
|
json_object_set_new(attr, CN_PARAMETERS, param);
|
|
json_object_set_new(obj, CN_ATTRIBUTES, attr);
|
|
|
|
json_array_append_new(d->commands, obj);
|
|
|
|
return true;
|
|
}
|
|
|
|
static json_t* module_json_data(const LOADED_MODULE* mod, const char* host)
|
|
{
|
|
json_t* obj = json_object();
|
|
|
|
json_object_set_new(obj, CN_ID, json_string(mod->module));
|
|
json_object_set_new(obj, CN_TYPE, json_string(CN_MODULE));
|
|
|
|
json_t* attr = json_object();
|
|
json_object_set_new(attr, "module_type", json_string(mod->type));
|
|
json_object_set_new(attr, "version", json_string(mod->info->version));
|
|
json_object_set_new(attr, CN_DESCRIPTION, json_string(mod->info->description));
|
|
json_object_set_new(attr, "api", json_string(mxs_module_api_to_string(mod->info->modapi)));
|
|
json_object_set_new(attr, "maturity", json_string(mxs_module_status_to_string(mod->info->status)));
|
|
|
|
json_t* commands = json_array();
|
|
cb_param p = {commands, mod->module, host};
|
|
modulecmd_foreach(mod->module, NULL, modulecmd_cb, &p);
|
|
|
|
json_t* params = json_array();
|
|
|
|
for (int i = 0; mod->info->parameters[i].name; i++)
|
|
{
|
|
json_t* p = json_object();
|
|
|
|
json_object_set_new(p, CN_NAME, json_string(mod->info->parameters[i].name));
|
|
json_object_set_new(p,
|
|
CN_TYPE,
|
|
json_string(mxs_module_param_type_to_string(mod->info->parameters[i].type)));
|
|
|
|
if (mod->info->parameters[i].default_value)
|
|
{
|
|
json_object_set(p, "default_value", json_string(mod->info->parameters[i].default_value));
|
|
}
|
|
|
|
if (mod->info->parameters[i].type == MXS_MODULE_PARAM_ENUM
|
|
&& mod->info->parameters[i].accepted_values)
|
|
{
|
|
json_t* arr = json_array();
|
|
|
|
for (int x = 0; mod->info->parameters[i].accepted_values[x].name; x++)
|
|
{
|
|
json_array_append_new(arr, json_string(mod->info->parameters[i].accepted_values[x].name));
|
|
}
|
|
|
|
json_object_set_new(p, "enum_values", arr);
|
|
}
|
|
|
|
json_array_append_new(params, p);
|
|
}
|
|
|
|
json_object_set_new(attr, "commands", commands);
|
|
json_object_set_new(attr, CN_PARAMETERS, params);
|
|
json_object_set_new(obj, CN_ATTRIBUTES, attr);
|
|
json_object_set_new(obj, CN_LINKS, mxs_json_self_link(host, CN_MODULES, mod->module));
|
|
|
|
return obj;
|
|
}
|
|
|
|
json_t* module_to_json(const MXS_MODULE* module, const char* host)
|
|
{
|
|
json_t* data = NULL;
|
|
|
|
for (LOADED_MODULE* ptr = registered; ptr; ptr = ptr->next)
|
|
{
|
|
if (ptr->info == module)
|
|
{
|
|
data = module_json_data(ptr, host);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This should always be non-NULL
|
|
mxb_assert(data);
|
|
|
|
return mxs_json_resource(host, MXS_JSON_API_MODULES, data);
|
|
}
|
|
|
|
json_t* module_list_to_json(const char* host)
|
|
{
|
|
json_t* arr = json_array();
|
|
|
|
for (LOADED_MODULE* ptr = registered; ptr; ptr = ptr->next)
|
|
{
|
|
json_array_append_new(arr, module_json_data(ptr, host));
|
|
}
|
|
|
|
return mxs_json_resource(host, MXS_JSON_API_MODULES, arr);
|
|
}
|
|
|
|
static const char* module_status_to_string(LOADED_MODULE* ptr)
|
|
{
|
|
switch (ptr->info->status)
|
|
{
|
|
case MXS_MODULE_IN_DEVELOPMENT:
|
|
return "In Development";
|
|
|
|
case MXS_MODULE_ALPHA_RELEASE:
|
|
return "Alpha";
|
|
|
|
case MXS_MODULE_BETA_RELEASE:
|
|
return "Beta";
|
|
|
|
case MXS_MODULE_GA:
|
|
return "GA";
|
|
|
|
case MXS_MODULE_EXPERIMENTAL:
|
|
return "Experimental";
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
/**
|
|
* Provide a row to the result set that defines the set of modules
|
|
*
|
|
* @param set The result set
|
|
* @param data The index of the row to send
|
|
* @return The next row or NULL
|
|
*/
|
|
static void moduleRowCallback(std::unique_ptr<ResultSet>& set)
|
|
{
|
|
|
|
for (LOADED_MODULE* ptr = registered; ptr; ptr = ptr->next)
|
|
{
|
|
char buf[40];
|
|
snprintf(buf,
|
|
sizeof(buf),
|
|
"%d.%d.%d",
|
|
ptr->info->api_version.major,
|
|
ptr->info->api_version.minor,
|
|
ptr->info->api_version.patch);
|
|
set->add_row({ptr->module, ptr->type, ptr->version, buf, module_status_to_string(ptr)});
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<ResultSet> moduleGetList()
|
|
{
|
|
std::unique_ptr<ResultSet> set = ResultSet::create({"Module Name", "Module Type", "Version",
|
|
"API Version", "Status"});
|
|
moduleRowCallback(set);
|
|
return set;
|
|
}
|
|
|
|
const MXS_MODULE* get_module(const char* name, const char* type)
|
|
{
|
|
name = mxs_module_get_effective_name(name);
|
|
|
|
LOADED_MODULE* mod = find_module(name);
|
|
|
|
if (mod == NULL && type && load_module(name, type))
|
|
{
|
|
mod = find_module(name);
|
|
}
|
|
|
|
return mod ? mod->info : NULL;
|
|
}
|
|
|
|
MXS_MODULE_ITERATOR mxs_module_iterator_get(const char* type)
|
|
{
|
|
LOADED_MODULE* module = registered;
|
|
|
|
while (module && type && (strcmp(module->type, type) != 0))
|
|
{
|
|
module = module->next;
|
|
}
|
|
|
|
MXS_MODULE_ITERATOR iterator;
|
|
iterator.type = type;
|
|
iterator.position = module;
|
|
|
|
return iterator;
|
|
}
|
|
|
|
bool mxs_module_iterator_has_next(const MXS_MODULE_ITERATOR* iterator)
|
|
{
|
|
return iterator->position != NULL;
|
|
}
|
|
|
|
MXS_MODULE* mxs_module_iterator_get_next(MXS_MODULE_ITERATOR* iterator)
|
|
{
|
|
MXS_MODULE* module = NULL;
|
|
LOADED_MODULE* loaded_module = (LOADED_MODULE*)iterator->position;
|
|
|
|
if (loaded_module)
|
|
{
|
|
module = loaded_module->info;
|
|
|
|
do
|
|
{
|
|
loaded_module = loaded_module->next;
|
|
}
|
|
while (loaded_module && iterator->type && (strcmp(loaded_module->type, iterator->type) != 0));
|
|
|
|
iterator->position = loaded_module;
|
|
}
|
|
|
|
return module;
|
|
}
|
|
|
|
const char* mxs_module_get_effective_name(const char* name)
|
|
{
|
|
const char* effective_name = NULL;
|
|
size_t i = 0;
|
|
|
|
while (!effective_name && (i < N_NAME_MAPPINGS))
|
|
{
|
|
NAME_MAPPING& nm = name_mappings[i];
|
|
|
|
if (strcasecmp(name, nm.from) == 0)
|
|
{
|
|
if (!nm.warned)
|
|
{
|
|
MXS_WARNING("%s module '%s' has been deprecated, use '%s' instead.",
|
|
nm.type,
|
|
nm.from,
|
|
nm.to);
|
|
nm.warned = true;
|
|
}
|
|
effective_name = nm.to;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
if (!effective_name)
|
|
{
|
|
effective_name = name;
|
|
}
|
|
|
|
return effective_name;
|
|
}
|