734 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			734 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 <maxbase/alloc.h>
 | |
| #include <maxscale/json_api.hh>
 | |
| #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},
 | |
|     {MODULE_AUTHENTICATOR, "mysqlauth",        "mariadbauth",        false},
 | |
|     {MODULE_AUTHENTICATOR, "mysqlbackendauth", "mariadbbackendauth", 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;
 | |
| }
 | 
