MaxScale/server/core/load_utils.cc
Markus Mäkelä c7b9b7ac4a MXS-1220: Unify resource member naming
All resoures now use the `state` member to describe their internal
state. This includes servers, services and monitors. This means that the
`status` keyword can be reserved for something else and it can be removed
until it is needed again.

Changed the module maturity field to `maturity` to better describe its
purpose.
2017-07-10 22:23:45 +03:00

621 lines
18 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: 2020-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 "maxscale/modules.h"
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <maxscale/modinfo.h>
#include <maxscale/log_manager.h>
#include <maxscale/version.h>
#include <maxscale/paths.h>
#include <maxscale/alloc.h>
#include <maxscale/json_api.h>
#include <maxscale/modulecmd.h>
#include "maxscale/modules.h"
#include "maxscale/config.h"
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;
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 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 (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)
{
ss_dassert(module && type);
LOADED_MODULE *mod;
if ((mod = find_module(module)) == NULL)
{
/** 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(), 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)
{
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 (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
* @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)
{
if (modulecmd_requires_output_dcb(cmd))
{
/** Module requires an output DCB, don't print it */
return true;
}
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;
ss_dassert(strcmp(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
ss_dassert(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);
}
/**
* 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 RESULT_ROW *
moduleRowCallback(RESULTSET *set, void *data)
{
int *rowno = (int *)data;
int i = 0;;
char *stat, buf[20];
RESULT_ROW *row;
LOADED_MODULE *ptr;
ptr = registered;
while (i < *rowno && ptr)
{
i++;
ptr = ptr->next;
}
if (ptr == NULL)
{
MXS_FREE(data);
return NULL;
}
(*rowno)++;
row = resultset_make_row(set);
resultset_row_set(row, 0, ptr->module);
resultset_row_set(row, 1, ptr->type);
resultset_row_set(row, 2, ptr->version);
snprintf(buf, 19, "%d.%d.%d", ptr->info->api_version.major,
ptr->info->api_version.minor,
ptr->info->api_version.patch);
buf[19] = '\0';
resultset_row_set(row, 3, buf);
resultset_row_set(row, 4, 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")))));
return row;
}
RESULTSET *moduleGetList()
{
RESULTSET *set;
int *data;
if ((data = (int *)MXS_MALLOC(sizeof(int))) == NULL)
{
return NULL;
}
*data = 0;
if ((set = resultset_create(moduleRowCallback, data)) == NULL)
{
MXS_FREE(data);
return NULL;
}
resultset_add_column(set, "Module Name", 18, COL_TYPE_VARCHAR);
resultset_add_column(set, "Module Type", 12, COL_TYPE_VARCHAR);
resultset_add_column(set, "Version", 10, COL_TYPE_VARCHAR);
resultset_add_column(set, "API Version", 8, COL_TYPE_VARCHAR);
resultset_add_column(set, "Status", 15, COL_TYPE_VARCHAR);
return set;
}
const MXS_MODULE *get_module(const char *name, const char *type)
{
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;
}