/* * Copyright (c) 2018 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: 2023-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. */ #include #include "internal/config.hh" using namespace std; namespace { using namespace config; bool is_core_param(Specification::Kind kind, const std::string& param) { bool rv = false; const MXS_MODULE_PARAM* pzCore_params = nullptr; switch (kind) { case Specification::FILTER: pzCore_params = config_filter_params; break; case Specification::MONITOR: pzCore_params = config_monitor_params; break; case Specification::ROUTER: pzCore_params = config_service_params; break; default: mxb_assert(!true); } if (pzCore_params) { while (!rv && pzCore_params->name) { const char* zCore_param = pzCore_params->name; rv = (param == zCore_param); ++pzCore_params; } } return rv; } } namespace config { /** * class Specification */ Specification::Specification(const char* zModule, Kind kind) : m_module(zModule) , m_kind(kind) { } Specification::~Specification() { } const string& Specification::module() const { return m_module; } const Param* Specification::find_param(const string& name) const { auto it = m_params.find(name); return it != m_params.end() ? it->second : nullptr; } ostream& Specification::document(ostream& out) const { for (const auto& entry : m_params) { out << entry.second->documentation() << endl; } return out; } bool Specification::validate(const MXS_CONFIG_PARAMETER& params) const { bool valid = true; set provided; for (const auto& param : params) { const auto& name = param.first; const auto& value = param.second; const Param* pParam = find_param(name.c_str()); if (pParam) { bool param_valid = true; string message; if (!pParam->validate(value.c_str(), &message)) { param_valid = false; } if (!message.empty()) { if (param_valid) { MXS_WARNING("%s: %s", name.c_str(), message.c_str()); } else { MXS_ERROR("%s: %s", name.c_str(), message.c_str()); } } provided.insert(name); } else if (!is_core_param(m_kind, name)) { MXS_WARNING("%s: The parameter '%s' is unrecognized.", m_module.c_str(), name.c_str()); valid = false; } } for (const auto& entry : m_params) { const Param* pParam = entry.second; if (pParam->is_mandatory() && (provided.find(pParam->name()) == provided.end())) { MXS_ERROR("%s: The mandatory parameter '%s' is not provided.", m_module.c_str(), pParam->name().c_str()); valid = false; } } return valid; } bool Specification::configure(Configuration& configuration, const MXS_CONFIG_PARAMETER& params) const { mxb_assert(validate(params)); mxb_assert(size() == configuration.size()); bool configured = true; for (const auto& param : params) { const auto& name = param.first; if (!is_core_param(m_kind, name)) { const auto& value = param.second; const Param* pParam = find_param(name.c_str()); config::Type* pValue = configuration.find_value(name.c_str()); mxb_assert(pValue && pParam); // Should have been validated. mxb_assert(&pValue->parameter() == pParam); if (pParam && pValue) { if (!pParam->set(*pValue, value.c_str())) { mxb_assert(!true); configured = false; } } else { MXS_ERROR("%s: The parameter '%s' is unrecognized.", m_module.c_str(), name.c_str()); configured = false; } } } if (configured) { configured = configuration.post_configure(params); } return configured; } void Specification::populate(MXS_MODULE& module) const { MXS_MODULE_PARAM* pModule_param = &module.parameters[0]; for (const auto& entry : m_params) { const Param* pParam = entry.second; pParam->populate(*pModule_param); ++pModule_param; } } size_t Specification::size() const { return m_params.size(); } void Specification::insert(Param* pParam) { mxb_assert(m_params.find(pParam->name()) == m_params.end()); m_params.insert(make_pair(pParam->name(), pParam)); } void Specification::remove(Param* pParam) { auto it = m_params.find(pParam->name()); mxb_assert(it != m_params.end()); m_params.erase(it); } /** * class Param */ Param::Param(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, mxs_module_param_type legacy_type) : m_specification(*pSpecification) , m_name(zName) , m_description(zDescription) , m_kind(kind) , m_legacy_type(legacy_type) { m_specification.insert(this); } Param::~Param() { m_specification.remove(this); } const string& Param::name() const { return m_name; } const string& Param::description() const { return m_description; } std::string Param::documentation() const { std::stringstream ss; ss << m_name << " (" << type() << ", "; if (is_mandatory()) { ss << "mandatory"; } else { ss << "optional, default: " << default_to_string(); } ss << "): " << m_description; return ss.str(); } Param::Kind Param::kind() const { return m_kind; } bool Param::is_mandatory() const { return m_kind == MANDATORY; } bool Param::is_optional() const { return m_kind == OPTIONAL; } bool Param::has_default_value() const { return is_optional(); } void Param::populate(MXS_MODULE_PARAM& param) const { param.type = m_legacy_type; param.name = MXS_STRDUP_A(name().c_str()); if (has_default_value()) { string s = default_to_string().c_str(); if ((s.length() >= 2) && (s.at(0) == '"') && (s.at(s.length() - 1) == '"')) { s = s.substr(1, s.length() - 2); } param.default_value = MXS_STRDUP_A(s.c_str()); } if (is_mandatory()) { param.options |= MXS_MODULE_OPT_REQUIRED; } } /** * class Configuration */ Configuration::Configuration(const std::string& name, const config::Specification* pSpecification) : m_name(name) , m_specification(*pSpecification) { } const std::string& Configuration::name() const { return m_name; } const config::Specification& Configuration::specification() const { return m_specification; } Type* Configuration::find_value(const string& name) { auto it = m_values.find(name); return it != m_values.end() ? it->second : nullptr; } const Type* Configuration::find_value(const string& name) const { return const_cast(this)->find_value(name); } ostream& Configuration::persist(ostream& out) const { for (const auto& entry : m_values) { Type* pValue = entry.second; pValue->persist(out) << "\n"; } return out; } void Configuration::insert(Type* pValue) { mxb_assert(m_values.find(pValue->parameter().name()) == m_values.end()); m_values.insert(make_pair(pValue->parameter().name(), pValue)); } void Configuration::remove(Type* pValue) { auto it = m_values.find(pValue->parameter().name()); mxb_assert(it != m_values.end()); m_values.erase(it); } bool Configuration::post_configure(const MXS_CONFIG_PARAMETER& params) { return true; } size_t Configuration::size() const { return m_values.size(); } /** * class Type */ Type::Type(Configuration* pConfiguration, const config::Param* pParam) : m_configuration(*pConfiguration) , m_param(*pParam) { m_configuration.insert(this); } Type::~Type() { m_configuration.remove(this); } const config::Param& Type::parameter() const { return m_param; } ostream& Type::persist(ostream& out) const { out << m_param.name() << "=" << to_string(); return out; } bool Type::set(const string& value_as_string) { return m_param.set(*this, value_as_string); } /** * ParamBool */ std::string ParamBool::type() const { return "boolean"; } std::string ParamBool::default_to_string() const { return to_string(m_default_value); } bool ParamBool::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamBool::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Bool& bool_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { bool_value.set(x); } return valid; } bool ParamBool::from_string(const string& value_as_string, value_type* pValue, string* pMessage) const { int rv = config_truth_value(value_as_string.c_str()); if (rv == 1) { *pValue = true; } else if (rv == 0) { *pValue = false; } else if (pMessage) { mxb_assert(rv == -1); *pMessage = "Invalid boolean: "; *pMessage += value_as_string; } return rv != -1; } string ParamBool::to_string(value_type value) const { return value ? "true" : "false"; } /** * ParamNumber */ std::string ParamNumber::default_to_string() const { return to_string(m_default_value); } bool ParamNumber::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamNumber::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Number& number_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { number_value.set(x); } return valid; } bool ParamNumber::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { const char* zValue = value_as_string.c_str(); char* zEnd; long l = strtol(zValue, &zEnd, 10); bool valid = (l >= m_min_value && l <= m_max_value && zEnd != zValue && *zEnd == 0); if (valid) { *pValue = l; } else if (pMessage) { if (!(zEnd != zValue && *zEnd == 0)) { *pMessage = "Invalid "; } else if (!(l >= m_min_value)) { *pMessage = "Too small a "; } else { mxb_assert(!(l <= m_max_value)); *pMessage = "Too large a "; } *pMessage += type(); *pMessage += ": "; *pMessage += value_as_string; } return valid; } std::string ParamNumber::to_string(value_type value) const { return std::to_string(value); } /** * ParamCount */ std::string ParamCount::type() const { return "count"; } /** * ParamInteger */ std::string ParamInteger::type() const { return "integer"; } /** * ParamPath */ std::string ParamPath::type() const { return "path"; } std::string ParamPath::default_to_string() const { return to_string(m_default_value); } bool ParamPath::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamPath::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Path& path_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { path_value.set(x); } return valid; } bool ParamPath::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { MXS_MODULE_PARAM param {}; param.options = m_options; bool valid = check_path_parameter(¶m, value_as_string.c_str()); if (valid) { *pValue = value_as_string; } else if (pMessage) { *pMessage = "Invalid path (does not exist, required permissions are not granted, "; *pMessage += "or cannot be created): "; *pMessage += value_as_string; } return valid; } std::string ParamPath::to_string(const value_type& value) const { return value; } void ParamPath::populate(MXS_MODULE_PARAM& param) const { Param::populate(param); param.options |= m_options; } /** * ParamServer */ std::string ParamServer::type() const { return "server"; } std::string ParamServer::default_to_string() const { return ""; } bool ParamServer::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamServer::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Server& server_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { server_value.set(x); } return valid; } bool ParamServer::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { *pValue = SERVER::find_by_unique_name(value_as_string); if (!*pValue && pMessage) { *pMessage = "Unknown server: "; *pMessage += value_as_string; } return *pValue; } std::string ParamServer::to_string(value_type value) const { return value->name(); } /** * ParamSize */ std::string ParamSize::type() const { return "size"; } std::string ParamSize::default_to_string() const { return to_string(m_default_value); } bool ParamSize::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamSize::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Size& size_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { size_value.set(x); } return valid; } bool ParamSize::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { bool valid = get_suffixed_size(value_as_string.c_str(), pValue); if (!valid && pMessage) { *pMessage = "Invalid size: "; *pMessage += value_as_string; } return valid; } std::string ParamSize::to_string(value_type value) const { // TODO: Use largest possible unit. return std::to_string(value); } /** * ParamString */ std::string ParamString::type() const { return "string"; } std::string ParamString::default_to_string() const { return to_string(m_default_value); } bool ParamString::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } bool ParamString::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); String& string_value = static_cast(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { string_value.set(x); } return valid; } bool ParamString::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { bool valid = true; char b = value_as_string.empty() ? 0 : value_as_string.front(); char e = value_as_string.empty() ? 0 : value_as_string.back(); if (b != '"' && b != '\'') { if (pMessage) { *pMessage = "A string value should be enclosed in quotes: "; *pMessage += value_as_string; } } string s = value_as_string; if (b == '"' || b == '\'') { valid = (b == e); if (valid) { s = s.substr(1, s.length() - 2); } else if (pMessage) { *pMessage = "A quoted string must end with the same quote: "; *pMessage += value_as_string; } } if (valid) { *pValue = s; } return valid; } std::string ParamString::to_string(value_type value) const { stringstream ss; ss << "\"" << value << "\""; return ss.str(); } }