0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 16:22:35 +01:00

Introduce modular configuration system.

This commit is contained in:
Jason Volk 2016-09-05 16:10:30 -07:00
parent d2bb8cd8a7
commit 2742547826
8 changed files with 664 additions and 100 deletions

View file

@ -28,32 +28,131 @@ namespace ircd {
namespace conf {
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, already_exists)
IRCD_EXCEPTION(error, bad_topconf)
IRCD_EXCEPTION(error, not_found)
IRCD_EXCEPTION(error, bad_cast)
extern struct log::log log;
struct item
{
uint8_t *ptr;
std::type_index type;
template<class T> item(T *ptr);
};
struct top
:cmd
{
using items = std::initializer_list<std::pair<std::string, item>>;
char letter;
std::string name;
std::unordered_map<std::string, item> map;
// Override to provide custom type handling
virtual void assign(item &item, std::string val) const;
// Override to provide operations on singleton blocks
virtual const uint8_t *get(client::client &, const std::string &key) const;
virtual void set(client::client &, std::string key, std::string val);
virtual void del(client::client &, const std::string &key);
virtual void enu(client::client &, const std::string &key);
// Override to provide operations on named blocks
virtual const uint8_t *get(client::client &, const std::string &label, const std::string &key) const;
virtual void set(client::client &, std::string label, std::string key, std::string val);
virtual void del(client::client &, const std::string &label, const std::string &key);
virtual void enu(client::client &, const std::string &label, const std::string &key);
// Override to handle the raw line
virtual void operator()(client::client &, line) override;
top(const char &letter, const std::string &name, const items & = {});
~top() noexcept;
};
namespace newconf
{
IRCD_EXCEPTION(conf::error, error)
IRCD_EXCEPTION(error, unknown_block)
using letters = std::array<std::string, 256>;
extern topconf current; // The latest newconf parse map after startup or rehash
extern topconf last; // The current is moved to last for differentiation
extern letters registry; // Translates newconf block names into characters
uint8_t find_letter(const std::string &name);
std::forward_list<std::string> translate(const topconf &);
}
extern struct log::log log;
extern std::array<top *, 256> confs;
// Dynamic casting closure
using type_handler = std::function<void (uint8_t *const &ptr, std::string text)>;
extern std::map<std::type_index, type_handler> type_handlers;
template<class T> std::type_index make_index();
template<class T = std::string> const T &get(client::client &, const char &, const std::string &label, const std::string &key);
template<class T = std::string> const T &get(client::client &, const char &, const std::string &key);
void init(const std::string &path);
template<class T>
const T &
get(client::client &client,
const char &letter,
const std::string &key)
{
const uint8_t &idx(letter);
const auto &conf(confs[idx]);
if(unlikely(!conf))
throw not_found("conf[%c] is not registered", letter);
return *reinterpret_cast<const T *>(conf->get(client, key));
}
template<class T>
const T &
get(client::client &client,
const char &letter,
const std::string &label,
const std::string &key)
{
const uint8_t &idx(letter);
const auto &conf(confs[idx]);
if(unlikely(!conf))
throw not_found("conf[%c] is not registered", letter);
return *reinterpret_cast<const T *>(conf->get(client, label, key));
}
template<class T>
std::type_index
make_index()
{
return typeid(typename std::add_pointer<T>::type);
}
template<class T>
item::item(T *ptr):
ptr{reinterpret_cast<uint8_t *>(ptr)},
type{typeid(ptr)}
{
}
} // namespace conf
} // namespace ircd
#endif // __cplusplus
#ifdef __cplusplus
namespace ircd {
namespace conf {
@ -282,27 +381,6 @@ struct config_server_hide
int disable_hidden;
};
struct server_info
{
char *name;
char sid[4];
char *description;
char *network_name;
int hub;
struct rb_sockaddr_storage bind4;
int default_max_clients;
#ifdef RB_IPV6
struct rb_sockaddr_storage bind6;
#endif
char *ssl_private_key;
char *ssl_ca_cert;
char *ssl_cert;
char *ssl_dh_params;
char *ssl_cipher_list;
int ssld_count;
int wsockd_count;
};
struct admin_info
{
char *name;

View file

@ -27,23 +27,37 @@ namespace ircd {
namespace conf {
namespace newconf {
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, unknown_block)
using key = std::string; // before the equals sign in an item
using val = std::vector<std::string>; // Either one or more elems after '='
using item = std::pair<key, val>; // Pairing of key/vals
using block = std::pair<key, std::vector<item>>; // key is optional "label" { items };
using topconf = std::multimap<key, block>; // key is type of block i.e admin { ... };
using topconf = std::list<std::pair<key, block>>; // key is type of block i.e admin { ... };
/* Notes:
* Some topconf entries are not blocks, but just key/values like "loadmodule." For this, the
* topconf multimap contains keys of "loadmodule," and a block entry containing an empty key,
* a vector of one item, with the item key also being "loadmodule" and the value being the
* module to load.
*
* topconf is not an real multimap, but a vector preserving the important order of the config.
*/
// Parse newconf syntax into tree
topconf parse(const std::string &str);
topconf parse(std::ifstream &file);
topconf parse_file(const std::string &path);
// Translates newconf block names into characters
using letters = std::array<std::string, 256>;
extern letters registry;
uint8_t find_letter(const std::string &name);
void translate(const topconf &, const std::function<void (std::string)> &closure);
std::list<std::string> translate(const topconf &);
} // namespace newconf
} // namespace conf
} // namespace ircd

View file

@ -20,44 +20,60 @@
*
*/
#include <boost/lexical_cast.hpp>
namespace ircd {
namespace conf {
newconf::topconf newconf::current;
newconf::topconf newconf::last;
struct log::log log
{
"conf", 'w' // both C's unavailable :/
};
newconf::topconf newconf::current;
newconf::topconf newconf::last;
newconf::letters newconf::registry;
std::array<top *, 256> confs;
std::map<std::type_index, type_handler> type_handlers
{
{
make_index<std::string>(), [](uint8_t *const &ptr, std::string text)
{
*reinterpret_cast<std::string *>(ptr) = std::move(text);
}
}
};
void reg(top *const &top);
void unreg(top *const &top);
bool execute(const std::string &line);
void parse_newconf(const std::string &path);
void bootstrap();
} // namespace conf
} // namespace ircd
using namespace ircd;
void conf::init(const std::string &path)
void
conf::init(const std::string &path)
{
newconf::registry['A'] = "admin";
newconf::registry['B'] = "blacklist";
newconf::registry['C'] = "connect";
newconf::registry['I'] = "auth";
newconf::registry['M'] = "serverinfo";
newconf::registry['O'] = "operator";
newconf::registry['P'] = "listen";
newconf::registry['U'] = "service";
newconf::registry['Y'] = "class";
newconf::registry['a'] = "alias";
newconf::registry['d'] = "exempt";
newconf::registry['g'] = "general";
newconf::registry['l'] = "log";
newconf::registry['m'] = "loadmodule";
bootstrap();
parse_newconf(path);
const auto oldconf(newconf::translate(newconf::current));
// Translate to oldconf and linefeed
newconf::translate(newconf::current, []
(const std::string &line)
{
execute(line);
});
}
void
conf::bootstrap()
{
log.debug("Bootstrapping L-Line module to load more modules...");
mods::load("conf_loadmodule");
}
void
@ -67,54 +83,202 @@ conf::parse_newconf(const std::string &path)
newconf::current = newconf::parse_file(path);
}
std::forward_list<std::string>
conf::newconf::translate(const newconf::topconf &top)
bool
conf::execute(const std::string &line)
try
{
std::forward_list<std::string> ret;
for(const auto &pair : top) try
{
const auto &type(pair.first);
const auto &block(pair.second);
const auto &label(block.first);
const auto &items(block.second);
const auto &letter(find_letter(type));
for(const auto &item : items)
{
const auto &key(item.first);
const auto &vals(item.second);
std::stringstream buf;
buf << letter << " "
<< label << " "
<< key << " :";
for(auto i(0); i < int(vals.size()) - 1; ++i)
buf << vals.at(i) << " ";
if(!vals.empty())
buf << vals.back();
ret.emplace_front(buf.str());
}
}
catch(const error &e)
{
log.warning("%s", e.what());
}
return ret;
log.debug("%s", line.c_str());
ircd::execute(me, line + "\r\n");
return true;
}
catch(const std::exception &e)
{
log.error("%s", e.what());
//return false;
throw;
}
uint8_t
conf::newconf::find_letter(const std::string &name)
conf::top::top(const char &letter,
const std::string &name,
const items &list)
:cmd{std::string{letter}}
,letter{letter}
,name{name}
,map{begin(list), end(list)}
{
const auto &registry(newconf::registry);
const auto it(std::find(begin(registry), end(registry), name));
if(it == end(registry))
throw unknown_block("%s is not registered to a letter", name.c_str());
return std::distance(begin(registry), it);
reg(this);
}
conf::top::~top()
noexcept
{
unreg(this);
}
void
conf::top::operator()(client::client &client,
line line)
try
{
if(line[0] == "*")
set(client, std::move(line[1]), std::move(line[2]));
else
set(client, std::move(line[0]), std::move(line[1]), std::move(line[2]));
}
catch(const std::out_of_range &e)
{
//throw err::NEEDMOREPARAMS(me.id, id(me, client), command(line).c_str());
throw error("NEEDMOREPARAMS");
}
catch(const boost::bad_lexical_cast &e)
{
throw bad_cast("conf[%c]: %s", letter, e.what());
}
__attribute__((noreturn))
void
conf::top::enu(client::client &client,
const std::string &label,
const std::string &key)
{
throw bad_topconf("conf[%c] is a singleton and the label must be '*'", letter);
}
__attribute__((noreturn))
void
conf::top::del(client::client &client,
const std::string &label,
const std::string &key)
{
throw bad_topconf("conf[%c] is a singleton and the label must be '*'", letter);
}
void
conf::top::set(client::client &client,
std::string label,
std::string key,
std::string val)
{
throw bad_topconf("conf[%c] is a singleton and the label must be '*'", letter);
}
__attribute__((noreturn))
const uint8_t *
conf::top::get(client::client &client,
const std::string &label,
const std::string &key)
const
{
throw bad_topconf("conf[%c] is a singleton and the label must be '*'", letter);
}
void
conf::top::enu(client::client &client,
const std::string &key)
{
}
void
conf::top::del(client::client &client,
const std::string &key)
{
if(!map.erase(key))
log.warning("conf[%c] tried to erase non-existent key \"%s\"",
letter,
key.c_str());
}
void
conf::top::set(client::client &client,
std::string key,
std::string val)
try
{
auto &item(map.at(key));
assign(item, std::move(val));
}
catch(const std::out_of_range &e)
{
throw not_found("key \"%s\"", key.c_str());
}
catch(const boost::bad_lexical_cast &e)
{
throw bad_cast("conf[%c]: failed to set key \"%s\": %s",
letter,
key.c_str(),
e.what());
}
const uint8_t *
conf::top::get(client::client &client,
const std::string &key)
const
try
{
const auto &item(map.at(key));
return item.ptr;
}
catch(const std::out_of_range &e)
{
throw not_found("key \"%s\"", key.c_str());
}
void
conf::top::assign(item &item,
std::string val)
const
try
{
const auto &handler(type_handlers.at(item.type));
handler(item.ptr, std::move(val));
}
catch(const std::out_of_range &e)
{
throw bad_cast("No handler to assign to value type \"%s\" @ %p",
item.type.name(),
item.ptr);
}
void
conf::unreg(top *const &top)
{
const auto &name(top->name);
const auto &letter(top->letter);
const uint8_t &idx(letter);
confs[idx] = nullptr;
newconf::registry[idx].clear();
log.info("Unregistered configuration letter '%c' (%s)",
letter,
name.c_str());
}
void
conf::reg(top *const &top)
{
const auto &name(top->name);
const auto &letter(top->letter);
const uint8_t &idx(letter);
if(!newconf::registry[idx].empty())
throw already_exists("Configuration letter '%c' already registered for \"%s\"",
letter,
newconf::registry[idx].c_str());
const auto it(std::find(begin(newconf::registry), end(newconf::registry), name));
if(it != end(newconf::registry))
throw already_exists("Configuration letter '%c' already registered topconf \"%s\"",
char(std::distance(begin(newconf::registry), it)),
it->c_str());
newconf::registry[idx] = name;
confs[idx] = top;
log.info("Registered configuration letter '%c' to conf type '%s'",
letter,
name.c_str());
}
/*
#define CF_TYPE(x) ((x) & CF_MTYPE)

View file

@ -21,8 +21,40 @@
#include <boost/spirit/include/qi.hpp>
namespace ircd {
namespace conf {
namespace newconf {
letters registry;
} // namespace newconf
} // namespace conf
} // namespace ircd
using namespace ircd;
/*
registry['A'] = "admin";
registry['B'] = "blacklist";
registry['C'] = "connect";
registry['I'] = "auth";
registry['O'] = "operator";
registry['P'] = "listen";
registry['U'] = "service";
registry['Y'] = "class";
registry['a'] = "alias";
registry['d'] = "exempt";
registry['g'] = "general";
registry['l'] = "log";
*/
namespace qi = boost::spirit::qi;
using namespace qi;
namespace ascii = qi::ascii;
using qi::lexeme;
using qi::char_;
using qi::lit;
using qi::eol;
using qi::blank;
using str = std::string;
using strvec = std::vector<str>;
@ -31,7 +63,7 @@ using strvecvecvec = std::vector<strvecvec>;
template<class iter>
struct ignores
:grammar<iter>
:qi::grammar<iter>
{
using rule = qi::rule<iter>;
@ -46,7 +78,7 @@ struct ignores
template<class iter,
class ignores>
struct newconf_parser
:grammar<iter, strvecvecvec(), ignores>
:qi::grammar<iter, strvecvecvec(), ignores>
{
template<class ret> using rule = qi::rule<iter, ret, ignores>;
@ -185,8 +217,66 @@ ircd::conf::newconf::parse(const std::string &str)
b.second.emplace_back(i);
}
top.emplace(k, b);
top.emplace_back(k, b);
}
return top;
}
std::list<std::string>
conf::newconf::translate(const topconf &top)
{
std::list<std::string> ret;
translate(top, [&ret](std::string line)
{
ret.emplace_back(std::move(line));
});
return ret;
}
void
conf::newconf::translate(const topconf &top,
const std::function<void (std::string)> &closure)
{
for(const auto &pair : top) try
{
const auto &type(pair.first);
const auto &block(pair.second);
const auto &label(block.first);
const auto &items(block.second);
const auto &letter(find_letter(type));
for(const auto &item : items)
{
const auto &key(item.first);
const auto &vals(item.second);
std::stringstream buf;
buf << letter << " "
<< label << " "
<< key << " :";
for(auto i(0); i < int(vals.size()) - 1; ++i)
buf << vals.at(i) << " ";
if(!vals.empty())
buf << vals.back();
closure(buf.str());
}
}
catch(const error &e)
{
log.warning("%s", e.what());
}
}
uint8_t
conf::newconf::find_letter(const std::string &name)
{
const auto &registry(newconf::registry);
const auto it(std::find(begin(registry), end(registry), name));
if(it == end(registry))
throw unknown_block("%s is not registered to a letter", name.c_str());
return std::distance(begin(registry), it);
}

View file

@ -1,5 +1,3 @@
moduledir=@moduledir@
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
@BOOST_CPPFLAGS@ \
@ -26,6 +24,20 @@ AM_LDFLAGS += \
-lrb \
@BOOST_LIBS@
# This puts the source in conf/ but the installed
# library is conf_X.so in the main modules dir.
conf_moduledir=@moduledir@
conf_conf_loadmodule_la_SOURCES = conf/loadmodule.cc
conf_conf_serverinfo_la_SOURCES = conf/serverinfo.cc
conf_conf_listen_la_SOURCES = conf/listen.cc
conf_module_LTLIBRARIES = \
conf/conf_loadmodule.la \
conf/conf_serverinfo.la \
conf/conf_listen.la
moduledir=@moduledir@
#m_ban_la_SOURCES = m_ban.cc
#m_die_la_SOURCES = m_die.cc
#m_error_la_SOURCES = m_error.cc

83
modules/conf/listen.cc Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/lexical_cast.hpp>
using namespace ircd;
mapi::header IRCD_MODULE
{
"P-Line - configuration directives for listening sockets"
};
struct P
:conf::top
{
void set(client::client &, std::string label, std::string key, std::string val) override;
using conf::top::top;
}
static P
{
'P', "listen",
};
struct block
{
std::string host;
std::vector<uint16_t> port;
};
std::map<std::string, block> blocks;
void
P::set(client::client &,
std::string label,
std::string key,
std::string val)
{
switch(hash(key))
{
case hash("host"):
blocks[label].host = val;
break;
case hash("port"):
if(!val)
{
blocks[label].port.clear();
break;
}
tokens(val, ", ", [&](const std::string &token)
{
const auto &val(boost::lexical_cast<uint16_t>(token));
blocks[label].port.emplace_back(val);
});
break;
default:
conf::log.warning("Unknown P-Line key \"%s\"", key.c_str());
break;
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
mapi::header IRCD_MODULE
{
"L-Line - configuration instruction for loadmodule"
};
struct L
:conf::top
{
void set(client::client &, std::string label, std::string key, std::string val) override;
void del(client::client &, const std::string &label, const std::string &key) override;
using conf::top::top;
}
static L
{
'L', "loadmodule"
};
void
L::set(client::client &client,
std::string label,
std::string key,
std::string val)
try
{
conf::log.debug("Loading \"%s\" via L-Line instruction", label.c_str());
mods::load(label);
}
catch(const std::exception &e)
{
conf::log.error("L-Line \"%s\": %s",
label.c_str(),
e.what());
throw;
}
void
L::del(client::client &client,
const std::string &label,
const std::string &key)
try
{
conf::log.debug("Unloading \"%s\" via L-Line instruction", label.c_str());
mods::unload(label);
}
catch(const std::exception &e)
{
conf::log.error("L-Line \"%s\": %s",
label.c_str(),
e.what());
throw;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
std::string name;
std::string description;
std::string network_name;
std::string sid;
std::string ssl_private_key;
std::string ssl_ca_cert;
std::string ssl_cert;
std::string ssl_dh_params;
std::string ssl_cipher_list;
int ssld_count;
int wsockd_count;
int default_max_clients;
conf::top M
{
'M', "serverinfo"
};
mapi::header IRCD_MODULE
{
"M-Line - configuration directives for serverinfo"
};