Make the config parser thread-safe

fixes #7822
This commit is contained in:
Gunnar Beutner 2014-11-27 08:04:07 +01:00
parent b61f803d60
commit ab63fca3d5
7 changed files with 90 additions and 113 deletions

View File

@ -69,6 +69,8 @@ void ThreadPool::Stop(void)
} }
m_ThreadGroup.join_all(); m_ThreadGroup.join_all();
m_Stopped = false;
} }
/** /**

View File

@ -201,6 +201,8 @@ static void SigHupHandler(int)
static bool Daemonize(void) static bool Daemonize(void)
{ {
#ifndef _WIN32 #ifndef _WIN32
Application::GetTP().Stop();
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
return false; return false;
@ -231,6 +233,8 @@ static bool Daemonize(void)
Application::Exit(0); Application::Exit(0);
} }
Application::GetTP().Start();
#endif /* _WIN32 */ #endif /* _WIN32 */
return true; return true;

View File

@ -60,54 +60,6 @@ do { \
do { \ do { \
result = yyextra->ReadInput(buf, max_size); \ result = yyextra->ReadInput(buf, max_size); \
} while (0) } while (0)
extern int ignore_newlines;
struct lex_buf {
char *buf;
size_t size;
};
static void lb_init(lex_buf *lb)
{
lb->buf = NULL;
lb->size = 0;
}
/*static void lb_cleanup(lex_buf *lb)
{
free(lb->buf);
}*/
static void lb_append_char(lex_buf *lb, char new_char)
{
const size_t block_size = 64;
size_t old_blocks = (lb->size + (block_size - 1)) / block_size;
size_t new_blocks = ((lb->size + 1) + (block_size - 1)) / block_size;
if (old_blocks != new_blocks) {
char *new_buf = (char *)realloc(lb->buf, new_blocks * block_size);
if (new_buf == NULL && new_blocks > 0)
throw std::bad_alloc();
lb->buf = new_buf;
}
lb->size++;
lb->buf[lb->size - 1] = new_char;
}
static char *lb_steal(lex_buf *lb)
{
lb_append_char(lb, '\0');
char *buf = lb->buf;
lb->buf = NULL;
lb->size = 0;
return buf;
}
%} %}
%option reentrant noyywrap yylineno %option reentrant noyywrap yylineno
@ -120,25 +72,19 @@ static char *lb_steal(lex_buf *lb)
%x HEREDOC %x HEREDOC
%% %%
lex_buf string_buf; \" { yyextra->m_LexBuffer.str(""); yyextra->m_LexBuffer.clear(); BEGIN(STRING); }
\" { lb_init(&string_buf); BEGIN(STRING); }
<STRING>\" { <STRING>\" {
BEGIN(INITIAL); BEGIN(INITIAL);
lb_append_char(&string_buf, '\0'); std::string str = yyextra->m_LexBuffer.str();
yylval->text = strdup(str.c_str());
yylval->text = lb_steal(&string_buf);
return T_STRING; return T_STRING;
} }
<STRING>\n { <STRING>\n {
std::ostringstream msgbuf; BOOST_THROW_EXCEPTION(ConfigError("Unterminated string literal") << errinfo_debuginfo(*yylloc));
msgbuf << "Unterminated string found: " << *yylloc;
ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
BEGIN(INITIAL);
} }
<STRING>\\[0-7]{1,3} { <STRING>\\[0-7]{1,3} {
@ -149,50 +95,45 @@ static char *lb_steal(lex_buf *lb)
if (result > 0xff) { if (result > 0xff) {
/* error, constant is out-of-bounds */ /* error, constant is out-of-bounds */
std::ostringstream msgbuf; BOOST_THROW_EXCEPTION(ConfigError("Constant is out of bounds: " + String(yytext)) << errinfo_debuginfo(*yylloc));
msgbuf << "Constant is out-of-bounds: " << yytext << " " << *yylloc;
ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
} }
lb_append_char(&string_buf, result); yyextra->m_LexBuffer << static_cast<char>(result);
} }
<STRING>\\[0-9]+ { <STRING>\\[0-9]+ {
/* generate error - bad escape sequence; something /* generate error - bad escape sequence; something
* like '\48' or '\0777777' * like '\48' or '\0777777'
*/ */
std::ostringstream msgbuf; BOOST_THROW_EXCEPTION(ConfigError("Bad escape sequence found: " + String(yytext)) << errinfo_debuginfo(*yylloc));
msgbuf << "Bad escape sequence found: " << yytext << " " << *yylloc;
ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
} }
<STRING>\\n { lb_append_char(&string_buf, '\n'); } <STRING>\\n { yyextra->m_LexBuffer << "\n"; }
<STRING>\\t { lb_append_char(&string_buf, '\t'); } <STRING>\\t { yyextra->m_LexBuffer << "\t"; }
<STRING>\\r { lb_append_char(&string_buf, '\r'); } <STRING>\\r { yyextra->m_LexBuffer << "\r"; }
<STRING>\\b { lb_append_char(&string_buf, '\b'); } <STRING>\\b { yyextra->m_LexBuffer << "\b"; }
<STRING>\\f { lb_append_char(&string_buf, '\f'); } <STRING>\\f { yyextra->m_LexBuffer << "\f"; }
<STRING>\\(.|\n) { lb_append_char(&string_buf, yytext[1]); } <STRING>\\(.|\n) { yyextra->m_LexBuffer << yytext[1]; }
<STRING>[^\\\n\"]+ { <STRING>[^\\\n\"]+ {
char *yptr = yytext; char *yptr = yytext;
while (*yptr) while (*yptr)
lb_append_char(&string_buf, *yptr++); yyextra->m_LexBuffer << *yptr++;
} }
\{\{\{ { lb_init(&string_buf); BEGIN(HEREDOC); } \{\{\{ { yyextra->m_LexBuffer.str(""); yyextra->m_LexBuffer.clear(); BEGIN(HEREDOC); }
<HEREDOC>\}\}\} { <HEREDOC>\}\}\} {
BEGIN(INITIAL); BEGIN(INITIAL);
lb_append_char(&string_buf, '\0'); std::string str = yyextra->m_LexBuffer.str();
yylval->text = strdup(str.c_str());
yylval->text = lb_steal(&string_buf);
return T_STRING; return T_STRING;
} }
<HEREDOC>(.|\n) { lb_append_char(&string_buf, yytext[0]); } <HEREDOC>(.|\n) { yyextra->m_LexBuffer << yytext[0]; }
<INITIAL>{ <INITIAL>{
"/*" BEGIN(C_COMMENT); "/*" BEGIN(C_COMMENT);
@ -205,10 +146,7 @@ static char *lb_steal(lex_buf *lb)
} }
<C_COMMENT><<EOF>> { <C_COMMENT><<EOF>> {
std::ostringstream msgbuf; BOOST_THROW_EXCEPTION(ConfigError("End-of-file while in comment") << errinfo_debuginfo(*yylloc));
msgbuf << "End-of-file while in comment: " << yytext << " " << *yylloc;
ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
yyterminate();
} }
@ -294,7 +232,7 @@ in return T_IN;
\> return T_GREATER_THAN; \> return T_GREATER_THAN;
} }
[\r\n]+ { yycolumn -= strlen(yytext) - 1; if (!ignore_newlines) return T_NEWLINE; } [\r\n]+ { yycolumn -= strlen(yytext) - 1; if (!yyextra->m_IgnoreNewlines) return T_NEWLINE; }
<<EOF>> { if (!yyextra->m_Eof) { yyextra->m_Eof = true; return T_NEWLINE; } else { yyterminate(); } } <<EOF>> { if (!yyextra->m_Eof) { yyextra->m_Eof = true; return T_NEWLINE; } else { yyterminate(); } }
. return yytext[0]; . return yytext[0];

View File

@ -69,8 +69,6 @@ do { \
using namespace icinga; using namespace icinga;
int ignore_newlines = 0;
template<typename T> template<typename T>
static void MakeRBinaryOp(Expression** result, Expression *left, Expression *right, DebugInfo& diLeft, DebugInfo& diRight) static void MakeRBinaryOp(Expression** result, Expression *left, Expression *right, DebugInfo& diLeft, DebugInfo& diRight)
{ {
@ -230,32 +228,21 @@ int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
void yyerror(YYLTYPE *locp, std::vector<Expression *> *, ConfigCompiler *, const char *err) void yyerror(YYLTYPE *locp, std::vector<Expression *> *, ConfigCompiler *, const char *err)
{ {
std::ostringstream message; BOOST_THROW_EXCEPTION(ConfigError(err) << errinfo_debuginfo(*locp));
message << *locp << ": " << err;
ConfigCompilerContext::GetInstance()->AddMessage(true, message.str(), *locp);
} }
int yyparse(std::vector<Expression *> *elist, ConfigCompiler *context); int yyparse(std::vector<Expression *> *elist, ConfigCompiler *context);
Expression *ConfigCompiler::Compile(void) Expression *ConfigCompiler::Compile(void)
{ {
try { std::vector<Expression *> elist;
std::vector<Expression *> elist;
if (yyparse(&elist, this) != 0) if (yyparse(&elist, this) != 0)
return NULL; return NULL;
DictExpression *expr = new DictExpression(elist); DictExpression *expr = new DictExpression(elist);
expr->MakeInline(); expr->MakeInline();
return expr; return expr;
} catch (const ConfigError& ex) {
const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
} catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
return NULL;
} }
#define scanner (context->GetScanner()) #define scanner (context->GetScanner())
@ -788,11 +775,11 @@ rterm_without_indexer: T_STRING
} }
| '(' | '('
{ {
ignore_newlines++; context->m_IgnoreNewlines++;
} }
rterm ')' rterm ')'
{ {
ignore_newlines--; context->m_IgnoreNewlines--;
$$ = $3; $$ = $3;
} }
| rterm T_LOGICAL_OR rterm { MakeRBinaryOp<LogicalOrExpression>(&$$, $1, $3, @1, @3); } | rterm T_LOGICAL_OR rterm { MakeRBinaryOp<LogicalOrExpression>(&$$, $1, $3, @1, @3); }

View File

@ -41,7 +41,7 @@ std::vector<String> ConfigCompiler::m_IncludeSearchDirs;
* @param zone The zone. * @param zone The zone.
*/ */
ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const String& zone) ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const String& zone)
: m_Path(path), m_Input(input), m_Zone(zone), m_Eof(false) : m_Path(path), m_Input(input), m_Zone(zone), m_Eof(false), m_IgnoreNewlines(0)
{ {
InitializeScanner(); InitializeScanner();
} }
@ -52,6 +52,7 @@ ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const St
ConfigCompiler::~ConfigCompiler(void) ConfigCompiler::~ConfigCompiler(void)
{ {
DestroyScanner(); DestroyScanner();
delete m_Input;
} }
/** /**
@ -173,6 +174,17 @@ void ConfigCompiler::HandleLibrary(const String& library)
(void) Utility::LoadExtensionLibrary(library); (void) Utility::LoadExtensionLibrary(library);
} }
void ConfigCompiler::CompileHelper(void)
{
try {
m_Promise.set_value(boost::shared_ptr<Expression>(Compile()));
} catch (...) {
m_Promise.set_exception(boost::current_exception());
}
delete this;
}
/** /**
* Compiles a stream. * Compiles a stream.
* *
@ -186,8 +198,12 @@ Expression *ConfigCompiler::CompileStream(const String& path, std::istream *stre
stream->exceptions(std::istream::badbit); stream->exceptions(std::istream::badbit);
ConfigCompiler ctx(path, stream, zone); ConfigCompiler* ctx = new ConfigCompiler(path, stream, zone);
return ctx.Compile();
boost::shared_future<boost::shared_ptr<Expression> > ftr = boost::shared_future<boost::shared_ptr<Expression> >(ctx->m_Promise.get_future());
Utility::QueueAsyncCallback(boost::bind(&ConfigCompiler::CompileHelper, ctx));
return new FutureExpression(ftr);
} }
/** /**
@ -200,10 +216,10 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone)
{ {
CONTEXT("Compiling configuration file '" + path + "'"); CONTEXT("Compiling configuration file '" + path + "'");
std::ifstream stream; std::ifstream *stream = new std::ifstream();
stream.open(path.CStr(), std::ifstream::in); stream->open(path.CStr(), std::ifstream::in);
if (!stream) if (!*stream)
BOOST_THROW_EXCEPTION(posix_error() BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("std::ifstream::open") << boost::errinfo_api_function("std::ifstream::open")
<< boost::errinfo_errno(errno) << boost::errinfo_errno(errno)
@ -212,7 +228,7 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone)
Log(LogInformation, "ConfigCompiler") Log(LogInformation, "ConfigCompiler")
<< "Compiling config file: " << path; << "Compiling config file: " << path;
return CompileStream(path, &stream, zone); return CompileStream(path, stream, zone);
} }
/** /**
@ -224,8 +240,8 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone)
*/ */
Expression *ConfigCompiler::CompileText(const String& path, const String& text, const String& zone) Expression *ConfigCompiler::CompileText(const String& path, const String& text, const String& zone)
{ {
std::stringstream stream(text); std::stringstream *stream = new std::stringstream(text);
return CompileStream(path, &stream, zone); return CompileStream(path, stream, zone);
} }
/** /**

View File

@ -81,6 +81,8 @@ public:
void *GetScanner(void) const; void *GetScanner(void) const;
private: private:
boost::promise<boost::shared_ptr<Expression> > m_Promise;
String m_Path; String m_Path;
std::istream *m_Input; std::istream *m_Input;
String m_Zone; String m_Zone;
@ -88,6 +90,9 @@ private:
void *m_Scanner; void *m_Scanner;
bool m_Eof; bool m_Eof;
int m_IgnoreNewlines;
std::ostringstream m_LexBuffer;
std::stack<TypeRuleList::Ptr> m_RuleLists; std::stack<TypeRuleList::Ptr> m_RuleLists;
ConfigType::Ptr m_Type; ConfigType::Ptr m_Type;
@ -105,6 +110,8 @@ private:
void InitializeScanner(void); void InitializeScanner(void);
void DestroyScanner(void); void DestroyScanner(void);
void CompileHelper(void);
friend int ::yylex(YYSTYPE *context, icinga::DebugInfo *di, yyscan_t scanner); friend int ::yylex(YYSTYPE *context, icinga::DebugInfo *di, yyscan_t scanner);
friend int ::yyparse(std::vector<icinga::Expression *> *elist, ConfigCompiler *context); friend int ::yyparse(std::vector<icinga::Expression *> *elist, ConfigCompiler *context);
}; };

View File

@ -29,6 +29,7 @@
#include "base/configerror.hpp" #include "base/configerror.hpp"
#include "base/convert.hpp" #include "base/convert.hpp"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/thread/future.hpp>
#include <map> #include <map>
namespace icinga namespace icinga
@ -164,6 +165,28 @@ private:
boost::shared_ptr<Expression> m_Expression; boost::shared_ptr<Expression> m_Expression;
}; };
class I2_CONFIG_API FutureExpression : public Expression
{
public:
FutureExpression(const boost::shared_future<boost::shared_ptr<Expression> >& future)
: m_Future(future)
{ }
protected:
virtual Value DoEvaluate(VMFrame& frame, DebugHint *dhint) const
{
return m_Future.get()->DoEvaluate(frame, dhint);
}
virtual const DebugInfo& GetDebugInfo(void) const
{
return m_Future.get()->GetDebugInfo();
}
private:
mutable boost::shared_future<boost::shared_ptr<Expression> > m_Future;
};
class I2_CONFIG_API LiteralExpression : public Expression class I2_CONFIG_API LiteralExpression : public Expression
{ {
public: public: