749 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			749 lines
		
	
	
		
			19 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: 2024-06-02
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <maxscale/modulecmd.hh>
 | 
						|
 | 
						|
#include <string>
 | 
						|
 | 
						|
#include <maxbase/alloc.h>
 | 
						|
#include <maxscale/config.hh>
 | 
						|
#include <maxscale/pcre2.h>
 | 
						|
 | 
						|
#include "internal/filter.hh"
 | 
						|
#include "internal/modules.hh"
 | 
						|
#include "internal/monitor.hh"
 | 
						|
#include "internal/monitormanager.hh"
 | 
						|
#include "internal/server.hh"
 | 
						|
 | 
						|
/** Size of the error buffer */
 | 
						|
#define MODULECMD_ERRBUF_SIZE 512
 | 
						|
 | 
						|
using maxscale::Monitor;
 | 
						|
 | 
						|
/** Thread local error buffer */
 | 
						|
thread_local char* errbuf = NULL;
 | 
						|
 | 
						|
/** Parameter passed to functions that do not always expect arguments */
 | 
						|
static const MODULECMD_ARG MODULECMD_NO_ARGUMENTS = {0, NULL};
 | 
						|
 | 
						|
/**
 | 
						|
 * A registered domain
 | 
						|
 */
 | 
						|
typedef struct modulecmd_domain
 | 
						|
{
 | 
						|
    char*                    domain;    /**< The domain */
 | 
						|
    MODULECMD*               commands;  /**< List of registered commands */
 | 
						|
    struct modulecmd_domain* next;      /**< Next domain */
 | 
						|
} MODULECMD_DOMAIN;
 | 
						|
 | 
						|
/**
 | 
						|
 * Internal functions
 | 
						|
 */
 | 
						|
 | 
						|
/** The global list of registered domains */
 | 
						|
static MODULECMD_DOMAIN* modulecmd_domains = NULL;
 | 
						|
static std::mutex modulecmd_lock;
 | 
						|
 | 
						|
static inline void prepare_error()
 | 
						|
{
 | 
						|
    if (errbuf == NULL)
 | 
						|
    {
 | 
						|
        errbuf = (char*)MXS_MALLOC(MODULECMD_ERRBUF_SIZE);
 | 
						|
        MXS_ABORT_IF_NULL(errbuf);
 | 
						|
        errbuf[0] = '\0';
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Reset error message
 | 
						|
 *
 | 
						|
 * This should be the first function called in every API function that can
 | 
						|
 * generate errors.
 | 
						|
 */
 | 
						|
static void reset_error()
 | 
						|
{
 | 
						|
    prepare_error();
 | 
						|
    errbuf[0] = '\0';
 | 
						|
}
 | 
						|
 | 
						|
static void report_argc_mismatch(const MODULECMD* cmd, int argc)
 | 
						|
{
 | 
						|
    if (cmd->arg_count_min == cmd->arg_count_max)
 | 
						|
    {
 | 
						|
        modulecmd_set_error("Expected %d arguments, got %d.", cmd->arg_count_min, argc);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        modulecmd_set_error("Expected between %d and %d arguments, got %d.",
 | 
						|
                            cmd->arg_count_min,
 | 
						|
                            cmd->arg_count_max,
 | 
						|
                            argc);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static MODULECMD_DOMAIN* domain_create(const char* domain)
 | 
						|
{
 | 
						|
    MODULECMD_DOMAIN* rval = (MODULECMD_DOMAIN*)MXS_MALLOC(sizeof(*rval));
 | 
						|
    char* dm = MXS_STRDUP(domain);
 | 
						|
 | 
						|
    if (rval && dm)
 | 
						|
    {
 | 
						|
        rval->domain = dm;
 | 
						|
        rval->commands = NULL;
 | 
						|
        rval->next = NULL;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        MXS_FREE(rval);
 | 
						|
        MXS_FREE(dm);
 | 
						|
        rval = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
static void domain_free(MODULECMD_DOMAIN* dm)
 | 
						|
{
 | 
						|
    if (dm)
 | 
						|
    {
 | 
						|
        MXS_FREE(dm->domain);
 | 
						|
        MXS_FREE(dm);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static MODULECMD_DOMAIN* get_or_create_domain(const char* domain)
 | 
						|
{
 | 
						|
 | 
						|
    MODULECMD_DOMAIN* dm;
 | 
						|
 | 
						|
    for (dm = modulecmd_domains; dm; dm = dm->next)
 | 
						|
    {
 | 
						|
        if (strcasecmp(dm->domain, domain) == 0)
 | 
						|
        {
 | 
						|
            return dm;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((dm = domain_create(domain)))
 | 
						|
    {
 | 
						|
        dm->next = modulecmd_domains;
 | 
						|
        modulecmd_domains = dm;
 | 
						|
    }
 | 
						|
 | 
						|
    return dm;
 | 
						|
}
 | 
						|
 | 
						|
static MODULECMD* command_create(const char* identifier,
 | 
						|
                                 const char* domain,
 | 
						|
                                 enum modulecmd_type type,
 | 
						|
                                 MODULECMDFN entry_point,
 | 
						|
                                 int argc,
 | 
						|
                                 modulecmd_arg_type_t* argv,
 | 
						|
                                 const char* description)
 | 
						|
{
 | 
						|
    mxb_assert((argc && argv) || (argc == 0 && argv == NULL));
 | 
						|
    mxb_assert(description);
 | 
						|
    MODULECMD* rval = (MODULECMD*)MXS_MALLOC(sizeof(*rval));
 | 
						|
    char* id = MXS_STRDUP(identifier);
 | 
						|
    char* dm = MXS_STRDUP(domain);
 | 
						|
    char* desc = MXS_STRDUP(description);
 | 
						|
    modulecmd_arg_type_t* types = (modulecmd_arg_type_t*)MXS_MALLOC(sizeof(*types) * (argc ? argc : 1));
 | 
						|
 | 
						|
    if (rval && id && dm && types && desc)
 | 
						|
    {
 | 
						|
        int argc_min = 0;
 | 
						|
 | 
						|
        for (int i = 0; i < argc; i++)
 | 
						|
        {
 | 
						|
            if (MODULECMD_ARG_IS_REQUIRED(&argv[i]))
 | 
						|
            {
 | 
						|
                argc_min++;
 | 
						|
            }
 | 
						|
            types[i] = argv[i];
 | 
						|
        }
 | 
						|
 | 
						|
        if (argc == 0)
 | 
						|
        {
 | 
						|
            /** The command requires no arguments */
 | 
						|
            types[0].type = MODULECMD_ARG_NONE;
 | 
						|
            types[0].description = "";
 | 
						|
        }
 | 
						|
 | 
						|
        rval->type = type;
 | 
						|
        rval->func = entry_point;
 | 
						|
        rval->identifier = id;
 | 
						|
        rval->domain = dm;
 | 
						|
        rval->description = desc;
 | 
						|
        rval->arg_types = types;
 | 
						|
        rval->arg_count_min = argc_min;
 | 
						|
        rval->arg_count_max = argc;
 | 
						|
        rval->next = NULL;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        MXS_FREE(rval);
 | 
						|
        MXS_FREE(id);
 | 
						|
        MXS_FREE(dm);
 | 
						|
        MXS_FREE(types);
 | 
						|
        MXS_FREE(desc);
 | 
						|
        rval = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
static void command_free(MODULECMD* cmd)
 | 
						|
{
 | 
						|
    if (cmd)
 | 
						|
    {
 | 
						|
        MXS_FREE(cmd->identifier);
 | 
						|
        MXS_FREE(cmd->domain);
 | 
						|
        MXS_FREE(cmd->arg_types);
 | 
						|
        MXS_FREE(cmd);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static bool domain_has_command(MODULECMD_DOMAIN* dm, const char* id)
 | 
						|
{
 | 
						|
    for (MODULECMD* cmd = dm->commands; cmd; cmd = cmd->next)
 | 
						|
    {
 | 
						|
        if (strcasecmp(cmd->identifier, id) == 0)
 | 
						|
        {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool process_argument(const MODULECMD* cmd,
 | 
						|
                             modulecmd_arg_type_t* type,
 | 
						|
                             const void* value,
 | 
						|
                             struct arg_node* arg,
 | 
						|
                             const char** err)
 | 
						|
{
 | 
						|
    bool rval = false;
 | 
						|
 | 
						|
    if (!MODULECMD_ARG_IS_REQUIRED(type) && value == NULL)
 | 
						|
    {
 | 
						|
        arg->type.type = MODULECMD_ARG_NONE;
 | 
						|
        rval = true;
 | 
						|
    }
 | 
						|
    else if (value)
 | 
						|
    {
 | 
						|
        switch (MODULECMD_GET_TYPE(type))
 | 
						|
        {
 | 
						|
        case MODULECMD_ARG_NONE:
 | 
						|
            arg->type.type = MODULECMD_ARG_NONE;
 | 
						|
            rval = true;
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_STRING:
 | 
						|
            if ((arg->value.string = MXS_STRDUP((char*)value)))
 | 
						|
            {
 | 
						|
                arg->type.type = MODULECMD_ARG_STRING;
 | 
						|
                rval = true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                *err = "memory allocation failed";
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_BOOLEAN:
 | 
						|
            {
 | 
						|
                int truthval = config_truth_value((char*)value);
 | 
						|
                if (truthval != -1)
 | 
						|
                {
 | 
						|
                    arg->value.boolean = truthval;
 | 
						|
                    arg->type.type = MODULECMD_ARG_BOOLEAN;
 | 
						|
                    rval = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    *err = "not a boolean value";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_SERVICE:
 | 
						|
            if ((arg->value.service = service_find((char*)value)))
 | 
						|
            {
 | 
						|
                if (MODULECMD_ALLOW_NAME_MISMATCH(type)
 | 
						|
                    || strcmp(cmd->domain, arg->value.service->router_name()) == 0)
 | 
						|
                {
 | 
						|
                    arg->type.type = MODULECMD_ARG_SERVICE;
 | 
						|
                    rval = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    *err = "router and domain names don't match";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                *err = "service not found";
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_SERVER:
 | 
						|
            if ((arg->value.server = Server::find_by_unique_name((char*)value)))
 | 
						|
            {
 | 
						|
                if (MODULECMD_ALLOW_NAME_MISMATCH(type)
 | 
						|
                    || (arg->value.server->protocol() == cmd->domain))
 | 
						|
                {
 | 
						|
                    arg->type.type = MODULECMD_ARG_SERVER;
 | 
						|
                    rval = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    *err = "server and domain names don't match";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                *err = "server not found";
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_SESSION:
 | 
						|
            if ((arg->value.session = session_get_by_id(strtoul((const char*)value, NULL, 0))))
 | 
						|
            {
 | 
						|
                arg->type.type = MODULECMD_ARG_SESSION;
 | 
						|
            }
 | 
						|
            rval = true;
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_DCB:
 | 
						|
            arg->type.type = MODULECMD_ARG_DCB;
 | 
						|
            arg->value.dcb = (DCB*)value;
 | 
						|
            rval = true;
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_MONITOR:
 | 
						|
            if ((arg->value.monitor = MonitorManager::find_monitor((char*) value)))
 | 
						|
            {
 | 
						|
                const char* eff_name = mxs_module_get_effective_name(arg->value.monitor->m_module.c_str());
 | 
						|
                if (MODULECMD_ALLOW_NAME_MISMATCH(type) || strcasecmp(cmd->domain, eff_name) == 0)
 | 
						|
                {
 | 
						|
                    arg->type.type = MODULECMD_ARG_MONITOR;
 | 
						|
                    rval = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    *err = "monitor and domain names don't match";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                *err = "monitor not found";
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case MODULECMD_ARG_FILTER:
 | 
						|
            if (auto f = filter_find((char*)value))
 | 
						|
            {
 | 
						|
                arg->value.filter = f.get();
 | 
						|
                const char* orig_name = filter_def_get_module_name(arg->value.filter);
 | 
						|
                const char* eff_name = mxs_module_get_effective_name(orig_name);
 | 
						|
                if (MODULECMD_ALLOW_NAME_MISMATCH(type) || strcasecmp(cmd->domain, eff_name) == 0)
 | 
						|
                {
 | 
						|
                    arg->type.type = MODULECMD_ARG_FILTER;
 | 
						|
                    rval = true;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    *err = "filter and domain names don't match";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                *err = "filter not found";
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            mxb_assert(false);
 | 
						|
            MXS_ERROR("Undefined argument type: %0lx", type->type);
 | 
						|
            *err = "internal error";
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        *err = "required argument";
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
static MODULECMD_ARG* modulecmd_arg_create(int argc)
 | 
						|
{
 | 
						|
    MODULECMD_ARG* arg = (MODULECMD_ARG*)MXS_MALLOC(sizeof(*arg));
 | 
						|
    struct arg_node* argv = (struct arg_node*)MXS_CALLOC(argc, sizeof(*argv));
 | 
						|
 | 
						|
    if (arg && argv)
 | 
						|
    {
 | 
						|
        arg->argc = argc;
 | 
						|
        arg->argv = argv;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        MXS_FREE(argv);
 | 
						|
        MXS_FREE(arg);
 | 
						|
        arg = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return arg;
 | 
						|
}
 | 
						|
 | 
						|
static void free_argument(struct arg_node* arg)
 | 
						|
{
 | 
						|
    switch (arg->type.type)
 | 
						|
    {
 | 
						|
    case MODULECMD_ARG_STRING:
 | 
						|
        MXS_FREE(arg->value.string);
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_SESSION:
 | 
						|
        session_put_ref(arg->value.session);
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Public functions declared in modulecmd.h
 | 
						|
 */
 | 
						|
 | 
						|
bool modulecmd_register_command(const char* domain,
 | 
						|
                                const char* identifier,
 | 
						|
                                enum modulecmd_type type,
 | 
						|
                                MODULECMDFN entry_point,
 | 
						|
                                int argc,
 | 
						|
                                modulecmd_arg_type_t* argv,
 | 
						|
                                const char* description)
 | 
						|
{
 | 
						|
    reset_error();
 | 
						|
    bool rval = false;
 | 
						|
    std::lock_guard<std::mutex> guard(modulecmd_lock);
 | 
						|
 | 
						|
    MODULECMD_DOMAIN* dm = get_or_create_domain(domain);
 | 
						|
 | 
						|
    if (dm)
 | 
						|
    {
 | 
						|
        if (domain_has_command(dm, identifier))
 | 
						|
        {
 | 
						|
            modulecmd_set_error("Command registered more than once: %s::%s", domain, identifier);
 | 
						|
            MXS_ERROR("Command registered more than once: %s::%s", domain, identifier);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            MODULECMD* cmd = command_create(identifier,
 | 
						|
                                            domain,
 | 
						|
                                            type,
 | 
						|
                                            entry_point,
 | 
						|
                                            argc,
 | 
						|
                                            argv,
 | 
						|
                                            description);
 | 
						|
 | 
						|
            if (cmd)
 | 
						|
            {
 | 
						|
                cmd->next = dm->commands;
 | 
						|
                dm->commands = cmd;
 | 
						|
                rval = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
const MODULECMD* modulecmd_find_command(const char* domain, const char* identifier)
 | 
						|
{
 | 
						|
    reset_error();
 | 
						|
 | 
						|
    const char* effective_domain = mxs_module_get_effective_name(domain);
 | 
						|
 | 
						|
    MODULECMD* rval = NULL;
 | 
						|
    std::lock_guard<std::mutex> guard(modulecmd_lock);
 | 
						|
 | 
						|
    for (MODULECMD_DOMAIN* dm = modulecmd_domains; dm; dm = dm->next)
 | 
						|
    {
 | 
						|
        if (strcasecmp(effective_domain, dm->domain) == 0)
 | 
						|
        {
 | 
						|
            for (MODULECMD* cmd = dm->commands; cmd; cmd = cmd->next)
 | 
						|
            {
 | 
						|
                if (strcasecmp(cmd->identifier, identifier) == 0)
 | 
						|
                {
 | 
						|
                    rval = cmd;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (rval == NULL)
 | 
						|
    {
 | 
						|
        modulecmd_set_error("Command not found: %s::%s", domain, identifier);
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
MODULECMD_ARG* modulecmd_arg_parse(const MODULECMD* cmd, int argc, const void** argv)
 | 
						|
{
 | 
						|
    reset_error();
 | 
						|
 | 
						|
    MODULECMD_ARG* arg = NULL;
 | 
						|
 | 
						|
    if (argc >= cmd->arg_count_min && argc <= cmd->arg_count_max)
 | 
						|
    {
 | 
						|
        arg = modulecmd_arg_create(cmd->arg_count_max);
 | 
						|
        bool error = false;
 | 
						|
 | 
						|
        if (arg)
 | 
						|
        {
 | 
						|
            for (int i = 0; i < cmd->arg_count_max && i < argc; i++)
 | 
						|
            {
 | 
						|
                const char* err = "";
 | 
						|
 | 
						|
                if (!process_argument(cmd, &cmd->arg_types[i], argv[i], &arg->argv[i], &err))
 | 
						|
                {
 | 
						|
                    error = true;
 | 
						|
                    modulecmd_set_error("Argument %d, %s: %s",
 | 
						|
                                        i + 1,
 | 
						|
                                        err,
 | 
						|
                                        argv[i] ? (char*)argv[i] : "No argument given");
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (error)
 | 
						|
            {
 | 
						|
                modulecmd_arg_free(arg);
 | 
						|
                arg = NULL;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                arg->argc = argc;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        report_argc_mismatch(cmd, argc);
 | 
						|
    }
 | 
						|
 | 
						|
    return arg;
 | 
						|
}
 | 
						|
 | 
						|
void modulecmd_arg_free(MODULECMD_ARG* arg)
 | 
						|
{
 | 
						|
    if (arg)
 | 
						|
    {
 | 
						|
        for (int i = 0; i < arg->argc; i++)
 | 
						|
        {
 | 
						|
            free_argument(&arg->argv[i]);
 | 
						|
        }
 | 
						|
 | 
						|
        MXS_FREE(arg->argv);
 | 
						|
        MXS_FREE(arg);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void modulecmd_clear_error()
 | 
						|
{
 | 
						|
    prepare_error();
 | 
						|
    errbuf[0] = '\0';
 | 
						|
}
 | 
						|
 | 
						|
bool modulecmd_call_command(const MODULECMD* cmd, const MODULECMD_ARG* args, json_t** output)
 | 
						|
{
 | 
						|
    bool rval = false;
 | 
						|
    reset_error();
 | 
						|
 | 
						|
    if (cmd->arg_count_min > 0 && args == NULL)
 | 
						|
    {
 | 
						|
        report_argc_mismatch(cmd, 0);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (args == NULL)
 | 
						|
        {
 | 
						|
            args = &MODULECMD_NO_ARGUMENTS;
 | 
						|
        }
 | 
						|
 | 
						|
        modulecmd_clear_error();
 | 
						|
 | 
						|
        json_t* discard = NULL;
 | 
						|
        rval = cmd->func(args, output ? output : &discard);
 | 
						|
        json_decref(discard);
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
void modulecmd_set_error(const char* format, ...)
 | 
						|
{
 | 
						|
    prepare_error();
 | 
						|
 | 
						|
    va_list list;
 | 
						|
    va_start(list, format);
 | 
						|
    vsnprintf(errbuf, MODULECMD_ERRBUF_SIZE, format, list);
 | 
						|
    va_end(list);
 | 
						|
}
 | 
						|
 | 
						|
const char* modulecmd_get_error()
 | 
						|
{
 | 
						|
    prepare_error();
 | 
						|
    return errbuf;
 | 
						|
}
 | 
						|
 | 
						|
json_t* modulecmd_get_json_error()
 | 
						|
{
 | 
						|
    json_t* obj = NULL;
 | 
						|
    std::string errmsg = modulecmd_get_error();
 | 
						|
    modulecmd_clear_error();
 | 
						|
 | 
						|
    if (errmsg.length())
 | 
						|
    {
 | 
						|
        json_t* err = json_object();
 | 
						|
        json_object_set_new(err, "detail", json_string(errmsg.c_str()));
 | 
						|
 | 
						|
        json_t* arr = json_array();
 | 
						|
        json_array_append_new(arr, err);
 | 
						|
 | 
						|
        obj = json_object();
 | 
						|
        json_object_set_new(obj, "errors", arr);
 | 
						|
    }
 | 
						|
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
bool modulecmd_foreach(const char* domain_re,
 | 
						|
                       const char* ident_re,
 | 
						|
                       bool (* fn)(const MODULECMD* cmd, void* data),
 | 
						|
                       void* data)
 | 
						|
{
 | 
						|
    bool rval = true;
 | 
						|
    bool stop = false;
 | 
						|
    std::lock_guard<std::mutex> guard(modulecmd_lock);
 | 
						|
 | 
						|
    for (MODULECMD_DOMAIN* domain = modulecmd_domains; domain && rval && !stop; domain = domain->next)
 | 
						|
    {
 | 
						|
        int err;
 | 
						|
        mxs_pcre2_result_t d_res = domain_re ?
 | 
						|
            mxs_pcre2_simple_match(domain_re, domain->domain, PCRE2_CASELESS, &err) :
 | 
						|
            MXS_PCRE2_MATCH;
 | 
						|
 | 
						|
        if (d_res == MXS_PCRE2_MATCH)
 | 
						|
        {
 | 
						|
            for (MODULECMD* cmd = domain->commands; cmd && rval; cmd = cmd->next)
 | 
						|
            {
 | 
						|
                mxs_pcre2_result_t i_res = ident_re ?
 | 
						|
                    mxs_pcre2_simple_match(ident_re, cmd->identifier, PCRE2_CASELESS, &err) :
 | 
						|
                    MXS_PCRE2_MATCH;
 | 
						|
 | 
						|
                if (i_res == MXS_PCRE2_MATCH)
 | 
						|
                {
 | 
						|
                    if (!fn(cmd, data))
 | 
						|
                    {
 | 
						|
                        stop = true;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else if (i_res == MXS_PCRE2_ERROR)
 | 
						|
                {
 | 
						|
                    PCRE2_UCHAR errbuf[MXS_STRERROR_BUFLEN];
 | 
						|
                    pcre2_get_error_message(err, errbuf, sizeof(errbuf));
 | 
						|
                    MXS_ERROR("Failed to match command identifier with '%s': %s", ident_re, errbuf);
 | 
						|
                    modulecmd_set_error("Failed to match command identifier with '%s': %s", ident_re, errbuf);
 | 
						|
                    rval = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (d_res == MXS_PCRE2_ERROR)
 | 
						|
        {
 | 
						|
            PCRE2_UCHAR errbuf[MXS_STRERROR_BUFLEN];
 | 
						|
            pcre2_get_error_message(err, errbuf, sizeof(errbuf));
 | 
						|
            MXS_ERROR("Failed to match command domain with '%s': %s", domain_re, errbuf);
 | 
						|
            modulecmd_set_error("Failed to match command domain with '%s': %s", domain_re, errbuf);
 | 
						|
            rval = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
#define format_type(a, b) (MODULECMD_ARG_IS_REQUIRED(a) ? b : "[" b "]")
 | 
						|
 | 
						|
const char* modulecmd_argtype_to_str(modulecmd_arg_type_t* type)
 | 
						|
{
 | 
						|
    const char* rval = "UNKNOWN";
 | 
						|
 | 
						|
    switch (MODULECMD_GET_TYPE(type))
 | 
						|
    {
 | 
						|
    case MODULECMD_ARG_NONE:
 | 
						|
        rval = format_type(type, "NONE");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_STRING:
 | 
						|
        rval = format_type(type, "STRING");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_BOOLEAN:
 | 
						|
        rval = format_type(type, "BOOLEAN");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_SERVICE:
 | 
						|
        rval = format_type(type, "SERVICE");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_SERVER:
 | 
						|
        rval = format_type(type, "SERVER");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_SESSION:
 | 
						|
        rval = format_type(type, "SESSION");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_DCB:
 | 
						|
        rval = format_type(type, "DCB");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_MONITOR:
 | 
						|
        rval = format_type(type, "MONITOR");
 | 
						|
        break;
 | 
						|
 | 
						|
    case MODULECMD_ARG_FILTER:
 | 
						|
        rval = format_type(type, "FILTER");
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        mxb_assert(false);
 | 
						|
        MXS_ERROR("Unknown type");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
bool modulecmd_arg_is_present(const MODULECMD_ARG* arg, int idx)
 | 
						|
{
 | 
						|
    return arg->argc > idx
 | 
						|
           && MODULECMD_GET_TYPE(&arg->argv[idx].type) != MODULECMD_ARG_NONE;
 | 
						|
}
 |