0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-24 17:38:54 +02:00

ircd: Improve exception reporting on newconf parsing.

This commit is contained in:
Jason Volk 2016-09-08 13:29:02 -07:00
parent f25cb78588
commit aca6eefcd9
6 changed files with 117 additions and 54 deletions

View file

@ -40,6 +40,14 @@ const char *const fatalerrstr
%s
)"};
const char *const usererrstr
{R"(
***
*** A fatal startup error has occurred. Please fix the problem to continue. ***
***
%s
)"};
bool printversion;
bool testing_conf;
bool cmdline;
@ -73,6 +81,9 @@ try
return 0;
}
const std::string confpath(configfile?: path::get(path::IRCD_CONF));
ircd::init(ios, confpath);
sigs.add(SIGHUP);
sigs.add(SIGINT);
sigs.add(SIGTSTP);
@ -80,14 +91,19 @@ try
sigs.add(SIGTERM);
sigs.async_wait(sigfd_handler);
const std::string confpath(configfile?: path::get(path::IRCD_CONF));
ircd::init(ios, confpath);
if(cmdline)
console_spawn();
ios.run(); // Blocks until a clean exit or an exception comes out of it.
}
catch(const ircd::conf::newconf::syntax_error &e)
{
if(ircd::debugmode)
throw;
fprintf(stderr, usererrstr, e.what());
return EXIT_FAILURE;
}
catch(const std::exception &e)
{
if(ircd::debugmode)
@ -120,14 +136,6 @@ void print_version()
printf("VERSION :%s\n", rb_lib_version());
}
const char *const usererrstr
{R"(
***
*** A fatal startup error has occurred. Please fix the problem to continue. ***
***
%s
)"};
bool startup_checks()
try
{

View file

@ -89,7 +89,8 @@ 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);
void execute();
void parse(const std::string &path);
template<class T>

View file

@ -28,6 +28,7 @@ namespace conf {
namespace newconf {
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, syntax_error)
IRCD_EXCEPTION(error, unknown_block)
using key = std::string; // before the equals sign in an item

View file

@ -56,18 +56,37 @@ namespace conf {
using namespace ircd;
void
conf::init(const std::string &path)
conf::parse(const std::string &path)
try
{
bootstrap();
parse_newconf(path);
}
catch(const std::exception &e)
{
log.error("Failed to parse configuration: %s", e.what());
throw;
}
void
conf::execute()
try
{
if(newconf::current.empty())
throw error("No configuration supplied to parse and execute.");
// Translate to oldconf and linefeed
bootstrap();
newconf::translate(newconf::current, []
(const std::string &line)
{
execute(line);
});
}
catch(const std::exception &e)
{
log.error("Configuration init failed: %s", e.what());
throw;
}
void
conf::bootstrap()

View file

@ -38,7 +38,7 @@ namespace ircd
void handle_sigusr1();
void handle_sigterm();
void handle_sigquit();
void main(const std::string confpath);
void main() noexcept;
}
/*
@ -57,9 +57,13 @@ ircd::init(boost::asio::io_service &io_service,
log::init();
log::mark("log started");
log::info("parsing your configuration");
conf::parse(configfile);
// The master of ceremonies runs the show after this function returns and ios.run()
// It cannot spawn when no ios is running so it is deferred just in case.
context mc(8_MiB, std::bind(&main, configfile), ctx::DEFER_POST);
log::debug("spawning main context");
context mc(8_MiB, ircd::main, ctx::DEFER_POST);
// The context will not be joined and block this function when no parent context
// is currently running, but it should still be detached here. It can then delete
@ -70,15 +74,15 @@ ircd::init(boost::asio::io_service &io_service,
}
void
ircd::main(const std::string configfile)
try
ircd::main()
noexcept try
{
// Ownership is taken of the main context to delete it at function end
const custom_ptr<ctx::ctx> mc(ircd::mc, ctx::free);
log::debug("IRCd entered main context.");
log::debug("setting up configuration");
conf::init(configfile);
log::info("executing configuration");
conf::execute();
log::debug("setting up signals");
boost::asio::signal_set sigfd(*ios);
@ -100,7 +104,7 @@ try
catch(const std::exception &e)
{
log::error("main context: %s", e.what());
throw;
return;
}
void

View file

@ -21,18 +21,17 @@
#include <boost/spirit/include/qi.hpp>
namespace ircd {
namespace conf {
namespace newconf {
letters registry;
} // namespace newconf
} // namespace conf
} // namespace ircd
using namespace ircd;
namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;
using qi::lexeme;
using qi::char_;
using qi::lit;
using qi::eol;
using qi::blank;
using qi::eps;
/*
registry['A'] = "admin";
registry['B'] = "blacklist";
@ -48,13 +47,9 @@ using namespace ircd;
registry['l'] = "log";
*/
namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;
using qi::lexeme;
using qi::char_;
using qi::lit;
using qi::eol;
using qi::blank;
namespace ircd {
namespace conf {
namespace newconf {
using str = std::string;
using strvec = std::vector<str>;
@ -77,7 +72,7 @@ struct ignores
template<class iter,
class ignores>
struct newconf_parser
struct parser
:qi::grammar<iter, strvecvecvec(), ignores>
{
template<class ret> using rule = qi::rule<iter, ret, ignores>;
@ -90,49 +85,62 @@ struct newconf_parser
rule<strvecvec()> block;
rule<strvecvecvec()> conf;
newconf_parser();
parser();
};
letters registry;
} // namespace newconf
} // namespace conf
} // namespace ircd
template<class iter,
class ignores>
newconf_parser<iter, ignores>::newconf_parser()
:newconf_parser::base_type // pass reference the topmost level to begin parsing on
conf::newconf::parser<iter, ignores>::parser()
:parser::base_type // pass reference the topmost level to begin parsing on
{
conf
}
,unquoted // config values without double-quotes, cannot have ';' because that's the ending
{
lexeme[+(char_ - ';')]
,"unquoted"
}
,quoted // config values surrounded by double quotes, which cannot have double quotes in them
{
lexeme['"' >> +(char_ - '"') >> '"']
,"quoted"
}
,key // configuration key, must be simple alnum's with underscores
{
+char_("[a-zA-Z0-9_]")
,"key"
}
,item // a key-value pair
{
+(key) >> '=' >> +(quoted | unquoted) >> ';'
,"item"
}
,topitem // a key-value pair, but at the topconf no '=' is used
{
+(key) >> +(quoted) >> ';'
,"topitem"
}
,block // a bracketed conf block, the type-label is like a key, and it can have a unique quoted label
{
*(key >> *(quoted)) >> '{' >> *(item) >> '}' >> ';'
,"block"
}
,conf // newconf is comprised of either conf blocks or key-value's at the top level
{
*(block | topitem)
,"conf"
}
{
}
template<class iter>
ignores<iter>::ignores()
conf::newconf::ignores<iter>::ignores()
:ignores::base_type // pass reference to the topmost skip-parser
{
skip
@ -156,34 +164,35 @@ ignores<iter>::ignores()
{
}
ircd::conf::newconf::topconf
ircd::conf::newconf::parse_file(const std::string &path)
conf::newconf::topconf
conf::newconf::parse_file(const std::string &path)
{
std::ifstream file(path);
return parse(file);
}
ircd::conf::newconf::topconf
ircd::conf::newconf::parse(std::ifstream &file)
conf::newconf::topconf
conf::newconf::parse(std::ifstream &file)
{
const std::istreambuf_iterator<char> bit(file), eit;
return parse(std::string(bit, eit));
}
ircd::conf::newconf::topconf
ircd::conf::newconf::parse(const std::string &str)
conf::newconf::topconf
conf::newconf::parse(const std::string &str)
try
{
using iter = std::string::const_iterator;
strvecvecvec vec;
const ignores<iter> ign;
const newconf_parser<iter, ignores<iter>> p;
const newconf::parser<iter, ignores<iter>> p;
if(!phrase_parse(begin(str), end(str), p, ign, vec))
throw ircd::error("Failed to parse");
throw syntax_error("newconf parse failed for unknown reason");
// The parser doesn't feed exactly into the ideal topconf format returned to the user.
// It's a simple position-semantic vector of strings at the same level.
// Translation for the user is here:
// This was my first qi grammar and there's a lot wrong with it.
// Once it's revisited it should parse directly into the ideal structures
// returned to the user. Until then it's translated here.
topconf top;
for(const auto &v : vec)
{
@ -222,6 +231,27 @@ ircd::conf::newconf::parse(const std::string &str)
return top;
}
catch(const boost::spirit::qi::expectation_failure<std::string::const_iterator> &e)
{
const size_t character(e.last - e.first);
const auto line_num(std::count(begin(str), begin(str)+character, '\n'));
size_t char_num(0);
size_t line_start(character);
while(line_start && str[--line_start] != '\n')
char_num++;
throw syntax_error("@line %zu +%zu: expecting :%s",
line_num,
char_num,
string(e.what_).c_str());
}
catch(const std::exception &e)
{
ircd::log::error("Unexpected newconf error during parsing: %s", e.what());
throw;
}
std::list<std::string>
conf::newconf::translate(const topconf &top)