]> granicus.if.org Git - icinga2/commitdiff
Make the config parser thread-safe
authorGunnar Beutner <gunnar@beutner.name>
Thu, 27 Nov 2014 07:04:07 +0000 (08:04 +0100)
committerGunnar Beutner <gunnar@beutner.name>
Fri, 28 Nov 2014 05:48:27 +0000 (06:48 +0100)
fixes #7822

lib/base/threadpool.cpp
lib/cli/daemoncommand.cpp
lib/config/config_lexer.ll
lib/config/config_parser.yy
lib/config/configcompiler.cpp
lib/config/configcompiler.hpp
lib/config/expression.hpp

index 2b26828609bc087cfacd2984d690dc2957685f21..0f6d44e46d0ea4b30660515267ef15bfc1abcaa2 100644 (file)
@@ -69,6 +69,8 @@ void ThreadPool::Stop(void)
        }
 
        m_ThreadGroup.join_all();
+
+       m_Stopped = false;
 }
 
 /**
index c269214e91cfccd6e4ead3e9493fbf50c01119b6..85dbfe31bfc116dedba6ca96506da47932509945 100644 (file)
@@ -201,6 +201,8 @@ static void SigHupHandler(int)
 static bool Daemonize(void)
 {
 #ifndef _WIN32
+       Application::GetTP().Stop();
+
        pid_t pid = fork();
        if (pid == -1) {
                return false;
@@ -231,6 +233,8 @@ static bool Daemonize(void)
 
                Application::Exit(0);
        }
+
+       Application::GetTP().Start();
 #endif /* _WIN32 */
 
        return true;
index 8f17c33b4efb80559609948ca13056d5c2d6d2e5..90b5236548f6e3eb2025581aec02314a465b351a 100644 (file)
@@ -60,54 +60,6 @@ do {                                                 \
 do {                                                   \
        result = yyextra->ReadInput(buf, max_size);     \
 } 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
@@ -120,25 +72,19 @@ static char *lb_steal(lex_buf *lb)
 %x HEREDOC
 
 %%
-       lex_buf string_buf;
-
-\"                             { lb_init(&string_buf); BEGIN(STRING); }
+\"                             { yyextra->m_LexBuffer.str(""); yyextra->m_LexBuffer.clear(); BEGIN(STRING); }
 
 <STRING>\"                     {
        BEGIN(INITIAL);
 
-       lb_append_char(&string_buf, '\0');
-
-       yylval->text = lb_steal(&string_buf);
+       std::string str = yyextra->m_LexBuffer.str();
+       yylval->text = strdup(str.c_str());
 
        return T_STRING;
                                }
 
 <STRING>\n                     {
-       std::ostringstream msgbuf;
-       msgbuf << "Unterminated string found: " << *yylloc;
-       ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
-       BEGIN(INITIAL);
+       BOOST_THROW_EXCEPTION(ConfigError("Unterminated string literal") << errinfo_debuginfo(*yylloc));
                                }
 
 <STRING>\\[0-7]{1,3}           {
@@ -149,50 +95,45 @@ static char *lb_steal(lex_buf *lb)
 
        if (result > 0xff) {
                /* error, constant is out-of-bounds */
-               std::ostringstream msgbuf;
-               msgbuf << "Constant is out-of-bounds: " << yytext << " " << *yylloc;
-               ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
+               BOOST_THROW_EXCEPTION(ConfigError("Constant is out of bounds: " + String(yytext)) << errinfo_debuginfo(*yylloc));
        }
 
-       lb_append_char(&string_buf, result);
+       yyextra->m_LexBuffer << static_cast<char>(result);
                                }
 
 <STRING>\\[0-9]+               {
        /* generate error - bad escape sequence; something
         * like '\48' or '\0777777'
         */
-       std::ostringstream msgbuf;
-       msgbuf << "Bad escape sequence found: " << yytext << " " << *yylloc;
-       ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
+       BOOST_THROW_EXCEPTION(ConfigError("Bad escape sequence found: " + String(yytext)) << errinfo_debuginfo(*yylloc));
                                }
 
-<STRING>\\n                    { lb_append_char(&string_buf, '\n'); }
-<STRING>\\t                    { lb_append_char(&string_buf, '\t'); }
-<STRING>\\r                    { lb_append_char(&string_buf, '\r'); }
-<STRING>\\b                    { lb_append_char(&string_buf, '\b'); }
-<STRING>\\f                    { lb_append_char(&string_buf, '\f'); }
-<STRING>\\(.|\n)               { lb_append_char(&string_buf, yytext[1]); }
+<STRING>\\n                    { yyextra->m_LexBuffer << "\n"; }
+<STRING>\\t                    { yyextra->m_LexBuffer << "\t"; }
+<STRING>\\r                    { yyextra->m_LexBuffer << "\r"; }
+<STRING>\\b                    { yyextra->m_LexBuffer << "\b"; }
+<STRING>\\f                    { yyextra->m_LexBuffer << "\f"; }
+<STRING>\\(.|\n)               { yyextra->m_LexBuffer << yytext[1]; }
 
 <STRING>[^\\\n\"]+             {
        char *yptr = yytext;
 
        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>\}\}\}                        {
        BEGIN(INITIAL);
 
-       lb_append_char(&string_buf, '\0');
-
-       yylval->text = lb_steal(&string_buf);
+       std::string str = yyextra->m_LexBuffer.str();
+       yylval->text = strdup(str.c_str());
 
        return T_STRING;
                                }
 
-<HEREDOC>(.|\n)                        { lb_append_char(&string_buf, yytext[0]); }
+<HEREDOC>(.|\n)                        { yyextra->m_LexBuffer << yytext[0]; }
 
 <INITIAL>{
 "/*"                           BEGIN(C_COMMENT);
@@ -205,10 +146,7 @@ static char *lb_steal(lex_buf *lb)
 }
 
 <C_COMMENT><<EOF>>              {
-               std::ostringstream msgbuf;
-               msgbuf << "End-of-file while in comment: " << yytext << " " << *yylloc;
-               ConfigCompilerContext::GetInstance()->AddMessage(true, msgbuf.str());
-               yyterminate();
+               BOOST_THROW_EXCEPTION(ConfigError("End-of-file while in comment") << errinfo_debuginfo(*yylloc));
                                }
 
 
@@ -294,7 +232,7 @@ in                          return T_IN;
 \>                             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(); } }
 .                              return yytext[0];
 
index 1d6679c66366680e937edce14a969a928d3b0b86..32b26dbd26ca2e3dff196c0b764d26de1d21eec8 100644 (file)
@@ -69,8 +69,6 @@ do {                                                  \
 
 using namespace icinga;
 
-int ignore_newlines = 0;
-
 template<typename T>
 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)
 {
-       std::ostringstream message;
-       message << *locp << ": " << err;
-       ConfigCompilerContext::GetInstance()->AddMessage(true, message.str(), *locp);
+       BOOST_THROW_EXCEPTION(ConfigError(err) << errinfo_debuginfo(*locp));
 }
 
 int yyparse(std::vector<Expression *> *elist, ConfigCompiler *context);
 
 Expression *ConfigCompiler::Compile(void)
 {
-       try {
-               std::vector<Expression *> elist;
-
-               if (yyparse(&elist, this) != 0)
-                       return NULL;
+       std::vector<Expression *> elist;
 
-               DictExpression *expr = new DictExpression(elist);
-               expr->MakeInline();
-               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));
-       }
+       if (yyparse(&elist, this) != 0)
+               return NULL;
 
-       return NULL;
+       DictExpression *expr = new DictExpression(elist);
+       expr->MakeInline();
+       return expr;
 }
 
 #define scanner (context->GetScanner())
@@ -788,11 +775,11 @@ rterm_without_indexer: T_STRING
        }
        | '('
        {
-               ignore_newlines++;
+               context->m_IgnoreNewlines++;
        }
        rterm ')'
        {
-               ignore_newlines--;
+               context->m_IgnoreNewlines--;
                $$ = $3;
        }
        | rterm T_LOGICAL_OR rterm { MakeRBinaryOp<LogicalOrExpression>(&$$, $1, $3, @1, @3); }
index 925e51fb91636ae42b8e3cdc947451e82d822ccf..1e4f47bd85a83548d205d9eaab4af8fca8e865be 100644 (file)
@@ -41,7 +41,7 @@ std::vector<String> ConfigCompiler::m_IncludeSearchDirs;
  * @param zone The 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();
 }
@@ -52,6 +52,7 @@ ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const St
 ConfigCompiler::~ConfigCompiler(void)
 {
        DestroyScanner();
+       delete m_Input;
 }
 
 /**
@@ -173,6 +174,17 @@ void ConfigCompiler::HandleLibrary(const String& 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.
  *
@@ -186,8 +198,12 @@ Expression *ConfigCompiler::CompileStream(const String& path, std::istream *stre
 
        stream->exceptions(std::istream::badbit);
 
-       ConfigCompiler ctx(path, stream, zone);
-       return ctx.Compile();
+       ConfigCompiler* ctx = new ConfigCompiler(path, stream, zone);
+
+       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 + "'");
 
-       std::ifstream stream;
-       stream.open(path.CStr(), std::ifstream::in);
+       std::ifstream *stream = new std::ifstream();
+       stream->open(path.CStr(), std::ifstream::in);
 
-       if (!stream)
+       if (!*stream)
                BOOST_THROW_EXCEPTION(posix_error()
                        << boost::errinfo_api_function("std::ifstream::open")
                        << boost::errinfo_errno(errno)
@@ -212,7 +228,7 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone)
        Log(LogInformation, "ConfigCompiler")
            << "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)
 {
-       std::stringstream stream(text);
-       return CompileStream(path, &stream, zone);
+       std::stringstream *stream = new std::stringstream(text);
+       return CompileStream(path, stream, zone);
 }
 
 /**
index 31bf77d20a4d7d9752d61581d6bb9d5400737c5b..d41cdbfb6d45100a43fe0ebccceb751e9a775930 100644 (file)
@@ -81,6 +81,8 @@ public:
        void *GetScanner(void) const;
 
 private:
+       boost::promise<boost::shared_ptr<Expression> > m_Promise;
+
        String m_Path;
        std::istream *m_Input;
        String m_Zone;
@@ -88,6 +90,9 @@ private:
        void *m_Scanner;
        bool m_Eof;
 
+       int m_IgnoreNewlines;
+       std::ostringstream m_LexBuffer;
+
        std::stack<TypeRuleList::Ptr> m_RuleLists;
        ConfigType::Ptr m_Type;
 
@@ -105,6 +110,8 @@ private:
        void InitializeScanner(void);
        void DestroyScanner(void);
 
+       void CompileHelper(void);
+
        friend int ::yylex(YYSTYPE *context, icinga::DebugInfo *di, yyscan_t scanner);
        friend int ::yyparse(std::vector<icinga::Expression *> *elist, ConfigCompiler *context);
 };
index e98c69745fd7ba10d87279eb37084f714021ace9..d89a5f3e51605aafa9b3b816b4cffa57441704cc 100644 (file)
@@ -29,6 +29,7 @@
 #include "base/configerror.hpp"
 #include "base/convert.hpp"
 #include <boost/foreach.hpp>
+#include <boost/thread/future.hpp>
 #include <map>
 
 namespace icinga
@@ -164,6 +165,28 @@ private:
        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
 {
 public: