From: Gunnar Beutner Date: Tue, 5 Feb 2013 12:06:42 +0000 (+0100) Subject: Validate config objects before trying to activate them. X-Git-Tag: v0.0.2~554 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7a150d2c291b53a339e2af1aed1139e4aea4c356;p=icinga2 Validate config objects before trying to activate them. Fixes #3619 --- diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index e7f3104cd..3547b54ee 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -38,28 +38,35 @@ static bool g_ReloadConfig = false; static Timer::Ptr g_ReloadConfigTimer; #endif /* _WIN32 */ -static bool LoadConfigFiles(void) +static bool LoadConfigFiles(bool validateOnly) { - set allItems; + ConfigCompilerContext context; - try { - BOOST_FOREACH(const String& configPath, g_AppParams["config"].as >()) { - vector items; - vector types; + ConfigCompilerContext::SetContext(&context); + + BOOST_FOREACH(const String& configPath, g_AppParams["config"].as >()) { + ConfigCompiler::CompileFile(configPath); + } - ConfigCompiler::CompileFile(configPath, &items, &types); + ConfigCompilerContext::SetContext(NULL); - Logger::Write(LogInformation, "icinga-app", "Registering config types..."); - BOOST_FOREACH(const ConfigType::Ptr& type, types) { - type->Commit(); - } + context.Validate(); + + bool hasError = false; + + BOOST_FOREACH(const ConfigCompilerError& error, context.GetErrors()) { + if (error.Warning) { + Logger::Write(LogWarning, "icinga-app", "Config warning: " + error.Message); + } else { + hasError = true; + Logger::Write(LogCritical, "icinga-app", "Config error: " + error.Message); + } + } - Logger::Write(LogInformation, "icinga-app", "Executing config items..."); - BOOST_FOREACH(const ConfigItem::Ptr& item, items) { - item->Commit(); - } + if (hasError) + return false; - Logger::Write(LogInformation, "icinga-app", "Validating config items..."); +/* Logger::Write(LogInformation, "icinga-app", "Validating config items..."); DynamicType::Ptr type; BOOST_FOREACH(tie(tuples::ignore, type), DynamicType::GetTypes()) { ConfigType::Ptr ctype = ConfigType::GetByName(type->GetName()); @@ -74,32 +81,26 @@ static bool LoadConfigFiles(void) BOOST_FOREACH(tie(tuples::ignore, object), type->GetObjects()) { ctype->ValidateObject(object); } - } + }*/ - std::copy(items.begin(), items.end(), std::inserter(allItems, allItems.begin())); - } + context.ActivateItems(); - BOOST_FOREACH(const ConfigItem::WeakPtr& witem, g_ConfigItems) { - ConfigItem::Ptr item = witem.lock(); + BOOST_FOREACH(const ConfigItem::WeakPtr& witem, g_ConfigItems) { + ConfigItem::Ptr item = witem.lock(); - /* Ignore this item if it's not active anymore */ - if (!item || ConfigItem::GetObject(item->GetType(), item->GetName()) != item) - continue; + /* Ignore this item if it's not active anymore */ + if (!item || ConfigItem::GetObject(item->GetType(), item->GetName()) != item) + continue; - /* Remove the object if it's not in the list of current items */ - if (allItems.find(item) == allItems.end()) - item->Unregister(); - } + item->Unregister(); + } - g_ConfigItems.clear(); - std::copy(allItems.begin(), allItems.end(), std::back_inserter(g_ConfigItems)); + g_ConfigItems.clear(); - return true; - } catch (const exception& ex) { - Logger::Write(LogCritical, "icinga-app", "Configuration error: " + String(ex.what())); - return false; - } + vector items = context.GetItems(); + std::copy(items.begin(), items.end(), std::back_inserter(g_ConfigItems)); + return true; } #ifndef _WIN32 @@ -107,7 +108,7 @@ static void ReloadConfigTimerHandler(void) { if (g_ReloadConfig) { Logger::Write(LogInformation, "icinga-app", "Received SIGHUP. Reloading config files."); - LoadConfigFiles(); + LoadConfigFiles(false); g_ReloadConfig = false; } } @@ -241,21 +242,23 @@ int main(int argc, char **argv) DynamicObject::BeginTx(); - if (!LoadConfigFiles()) + bool validateOnly = g_AppParams.count("validate"); + + if (!LoadConfigFiles(validateOnly)) return EXIT_FAILURE; DynamicObject::FinishTx(); + if (validateOnly) { + Logger::Write(LogInformation, "icinga-app", "Terminating as requested by --validate."); + return EXIT_SUCCESS; + } + Application::Ptr app = Application::GetInstance(); if (!app) throw_exception(runtime_error("Configuration must create an Application object.")); - if (g_AppParams.count("validate")) { - Logger::Write(LogInformation, "icinga-app", "Terminating as requested by --validate."); - return EXIT_SUCCESS; - } - if (g_AppParams.count("daemonize")) { Logger::Write(LogInformation, "icinga", "Daemonizing."); Utility::Daemonize(); diff --git a/lib/base/value.cpp b/lib/base/value.cpp index f74bb7f3a..a83a75739 100644 --- a/lib/base/value.cpp +++ b/lib/base/value.cpp @@ -88,7 +88,7 @@ String Value::Serialize(void) const char *jsonString; - if (Application::GetInstance()->IsDebugging()) + if (Application::IsDebugging()) jsonString = cJSON_Print(json); else jsonString = cJSON_PrintUnformatted(json); diff --git a/lib/config/Makefile.am b/lib/config/Makefile.am index 5a1ce8cd6..8737d31bd 100644 --- a/lib/config/Makefile.am +++ b/lib/config/Makefile.am @@ -11,6 +11,8 @@ AM_YFLAGS = -d libconfig_la_SOURCES = \ configcompiler.cpp \ configcompiler.h \ + configcompilercontext.cpp \ + configcompilercontext.h \ config_lexer.ll \ config_parser.yy \ i2-config.h \ diff --git a/lib/config/config.vcxproj b/lib/config/config.vcxproj index ded9a97b5..b111cf53d 100644 --- a/lib/config/config.vcxproj +++ b/lib/config/config.vcxproj @@ -20,6 +20,7 @@ + @@ -33,6 +34,7 @@ + diff --git a/lib/config/config_parser.cc b/lib/config/config_parser.cc index 8dbf738bf..461744fdd 100644 --- a/lib/config/config_parser.cc +++ b/lib/config/config_parser.cc @@ -246,7 +246,13 @@ static ConfigType::Ptr m_Type; void ConfigCompiler::Compile(void) { - yyparse(this); + assert(ConfigCompilerContext::GetContext() != NULL); + + try { + yyparse(this); + } catch (const exception& ex) { + ConfigCompilerContext::GetContext()->AddError(false, ex.what()); + } } #define scanner (context->GetScanner()) @@ -254,7 +260,7 @@ void ConfigCompiler::Compile(void) /* Line 343 of yacc.c */ -#line 258 "config_parser.cc" +#line 264 "config_parser.cc" #ifdef short # undef short @@ -565,13 +571,13 @@ static const yytype_int8 yyrhs[] = /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 110, 110, 111, 114, 114, 114, 114, 117, 121, - 126, 131, 132, 139, 138, 160, 163, 170, 169, 181, - 182, 184, 185, 186, 189, 194, 202, 203, 209, 210, - 211, 212, 213, 220, 225, 220, 244, 245, 248, 252, - 258, 259, 262, 269, 270, 274, 273, 285, 286, 288, - 289, 290, 293, 301, 315, 324, 325, 326, 327, 328, - 334, 339, 343, 349, 350 + 0, 116, 116, 117, 120, 120, 120, 120, 123, 127, + 132, 137, 138, 145, 144, 166, 169, 176, 175, 187, + 188, 190, 191, 192, 195, 200, 208, 209, 215, 216, + 217, 218, 219, 226, 231, 226, 250, 251, 254, 258, + 264, 265, 268, 275, 276, 280, 279, 291, 292, 294, + 295, 296, 299, 307, 321, 330, 331, 332, 333, 334, + 340, 345, 349, 355, 356 }; #endif @@ -1616,7 +1622,7 @@ yyreduce: case 8: /* Line 1806 of yacc.c */ -#line 118 "config_parser.yy" +#line 124 "config_parser.yy" { context->HandleInclude((yyvsp[(2) - (2)].text), false, yylloc); } @@ -1625,7 +1631,7 @@ yyreduce: case 9: /* Line 1806 of yacc.c */ -#line 122 "config_parser.yy" +#line 128 "config_parser.yy" { context->HandleInclude((yyvsp[(2) - (2)].text), true, yylloc); } @@ -1634,7 +1640,7 @@ yyreduce: case 10: /* Line 1806 of yacc.c */ -#line 127 "config_parser.yy" +#line 133 "config_parser.yy" { context->HandleLibrary((yyvsp[(2) - (2)].text)); } @@ -1643,7 +1649,7 @@ yyreduce: case 12: /* Line 1806 of yacc.c */ -#line 133 "config_parser.yy" +#line 139 "config_parser.yy" { (yyval.text) = (yyvsp[(1) - (1)].text); } @@ -1652,17 +1658,17 @@ yyreduce: case 13: /* Line 1806 of yacc.c */ -#line 139 "config_parser.yy" +#line 145 "config_parser.yy" { String name = String((yyvsp[(3) - (3)].text)); - m_Type = context->GetTypeByName(name); + m_Type = ConfigCompilerContext::GetContext()->GetType(name); if (!m_Type) { if ((yyvsp[(1) - (3)].num)) throw_exception(invalid_argument("partial type definition for unknown type '" + name + "'")); m_Type = boost::make_shared(name, yylloc); - context->AddType(m_Type); + ConfigCompilerContext::GetContext()->AddType(m_Type); } } break; @@ -1670,7 +1676,7 @@ yyreduce: case 14: /* Line 1806 of yacc.c */ -#line 152 "config_parser.yy" +#line 158 "config_parser.yy" { TypeRuleList::Ptr ruleList = *(yyvsp[(6) - (6)].variant); m_Type->GetRuleList()->AddRules(ruleList); @@ -1681,7 +1687,7 @@ yyreduce: case 15: /* Line 1806 of yacc.c */ -#line 160 "config_parser.yy" +#line 166 "config_parser.yy" { (yyval.num) = 0; } @@ -1690,7 +1696,7 @@ yyreduce: case 16: /* Line 1806 of yacc.c */ -#line 164 "config_parser.yy" +#line 170 "config_parser.yy" { (yyval.num) = 1; } @@ -1699,7 +1705,7 @@ yyreduce: case 17: /* Line 1806 of yacc.c */ -#line 170 "config_parser.yy" +#line 176 "config_parser.yy" { m_RuleLists.push(boost::make_shared()); } @@ -1708,7 +1714,7 @@ yyreduce: case 18: /* Line 1806 of yacc.c */ -#line 175 "config_parser.yy" +#line 181 "config_parser.yy" { (yyval.variant) = new Value(m_RuleLists.top()); m_RuleLists.pop(); @@ -1718,7 +1724,7 @@ yyreduce: case 24: /* Line 1806 of yacc.c */ -#line 190 "config_parser.yy" +#line 196 "config_parser.yy" { TypeRule rule((yyvsp[(1) - (2)].type), (yyvsp[(2) - (2)].text), TypeRuleList::Ptr(), yylloc); m_RuleLists.top()->AddRule(rule); @@ -1728,7 +1734,7 @@ yyreduce: case 25: /* Line 1806 of yacc.c */ -#line 195 "config_parser.yy" +#line 201 "config_parser.yy" { TypeRule rule((yyvsp[(1) - (3)].type), (yyvsp[(2) - (3)].text), *(yyvsp[(3) - (3)].variant), yylloc); delete (yyvsp[(3) - (3)].variant); @@ -1739,7 +1745,7 @@ yyreduce: case 27: /* Line 1806 of yacc.c */ -#line 204 "config_parser.yy" +#line 210 "config_parser.yy" { m_Type->SetParent((yyvsp[(2) - (2)].text)); } @@ -1748,7 +1754,7 @@ yyreduce: case 32: /* Line 1806 of yacc.c */ -#line 214 "config_parser.yy" +#line 220 "config_parser.yy" { (yyval.type) = (yyvsp[(1) - (1)].type); } @@ -1757,7 +1763,7 @@ yyreduce: case 33: /* Line 1806 of yacc.c */ -#line 220 "config_parser.yy" +#line 226 "config_parser.yy" { m_Abstract = false; m_Local = false; @@ -1767,7 +1773,7 @@ yyreduce: case 34: /* Line 1806 of yacc.c */ -#line 225 "config_parser.yy" +#line 231 "config_parser.yy" { m_Item = boost::make_shared(yylloc); m_Item->SetType((yyvsp[(4) - (5)].text)); @@ -1778,7 +1784,7 @@ yyreduce: case 35: /* Line 1806 of yacc.c */ -#line 231 "config_parser.yy" +#line 237 "config_parser.yy" { ExpressionList::Ptr exprl = *(yyvsp[(8) - (8)].variant); delete (yyvsp[(8) - (8)].variant); @@ -1787,7 +1793,7 @@ yyreduce: m_Item->SetLocal(m_Local); m_Item->SetAbstract(m_Abstract); - context->AddObject(m_Item->Compile()); + ConfigCompilerContext::GetContext()->AddItem(m_Item->Compile()); m_Item.reset(); } break; @@ -1795,7 +1801,7 @@ yyreduce: case 38: /* Line 1806 of yacc.c */ -#line 249 "config_parser.yy" +#line 255 "config_parser.yy" { m_Abstract = true; } @@ -1804,7 +1810,7 @@ yyreduce: case 39: /* Line 1806 of yacc.c */ -#line 253 "config_parser.yy" +#line 259 "config_parser.yy" { m_Local = true; } @@ -1813,7 +1819,7 @@ yyreduce: case 42: /* Line 1806 of yacc.c */ -#line 263 "config_parser.yy" +#line 269 "config_parser.yy" { m_Item->AddParent((yyvsp[(1) - (1)].text)); free((yyvsp[(1) - (1)].text)); @@ -1823,7 +1829,7 @@ yyreduce: case 45: /* Line 1806 of yacc.c */ -#line 274 "config_parser.yy" +#line 280 "config_parser.yy" { m_ExpressionLists.push(boost::make_shared()); } @@ -1832,7 +1838,7 @@ yyreduce: case 46: /* Line 1806 of yacc.c */ -#line 279 "config_parser.yy" +#line 285 "config_parser.yy" { (yyval.variant) = new Value(m_ExpressionLists.top()); m_ExpressionLists.pop(); @@ -1842,7 +1848,7 @@ yyreduce: case 52: /* Line 1806 of yacc.c */ -#line 294 "config_parser.yy" +#line 300 "config_parser.yy" { Expression expr((yyvsp[(1) - (3)].text), (yyvsp[(2) - (3)].op), *(yyvsp[(3) - (3)].variant), yylloc); free((yyvsp[(1) - (3)].text)); @@ -1855,7 +1861,7 @@ yyreduce: case 53: /* Line 1806 of yacc.c */ -#line 302 "config_parser.yy" +#line 308 "config_parser.yy" { Expression subexpr((yyvsp[(3) - (6)].text), (yyvsp[(5) - (6)].op), *(yyvsp[(6) - (6)].variant), yylloc); free((yyvsp[(3) - (6)].text)); @@ -1874,7 +1880,7 @@ yyreduce: case 54: /* Line 1806 of yacc.c */ -#line 316 "config_parser.yy" +#line 322 "config_parser.yy" { Expression expr((yyvsp[(1) - (1)].text), OperatorSet, (yyvsp[(1) - (1)].text), yylloc); free((yyvsp[(1) - (1)].text)); @@ -1886,7 +1892,7 @@ yyreduce: case 59: /* Line 1806 of yacc.c */ -#line 329 "config_parser.yy" +#line 335 "config_parser.yy" { (yyval.op) = (yyvsp[(1) - (1)].op); } @@ -1895,7 +1901,7 @@ yyreduce: case 60: /* Line 1806 of yacc.c */ -#line 335 "config_parser.yy" +#line 341 "config_parser.yy" { (yyval.variant) = new Value((yyvsp[(1) - (1)].text)); free((yyvsp[(1) - (1)].text)); @@ -1905,7 +1911,7 @@ yyreduce: case 61: /* Line 1806 of yacc.c */ -#line 340 "config_parser.yy" +#line 346 "config_parser.yy" { (yyval.variant) = new Value((yyvsp[(1) - (1)].num)); } @@ -1914,7 +1920,7 @@ yyreduce: case 62: /* Line 1806 of yacc.c */ -#line 344 "config_parser.yy" +#line 350 "config_parser.yy" { (yyval.variant) = new Value(); } @@ -1923,7 +1929,7 @@ yyreduce: case 64: /* Line 1806 of yacc.c */ -#line 351 "config_parser.yy" +#line 357 "config_parser.yy" { (yyval.variant) = (yyvsp[(1) - (1)].variant); } @@ -1932,7 +1938,7 @@ yyreduce: /* Line 1806 of yacc.c */ -#line 1936 "config_parser.cc" +#line 1942 "config_parser.cc" default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -2170,6 +2176,6 @@ yyreturn: /* Line 2067 of yacc.c */ -#line 355 "config_parser.yy" +#line 361 "config_parser.yy" diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 5c9a3a7f3..b19d99a28 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -99,7 +99,13 @@ static ConfigType::Ptr m_Type; void ConfigCompiler::Compile(void) { - yyparse(this); + assert(ConfigCompilerContext::GetContext() != NULL); + + try { + yyparse(this); + } catch (const exception& ex) { + ConfigCompilerContext::GetContext()->AddError(false, ex.what()); + } } #define scanner (context->GetScanner()) @@ -138,14 +144,14 @@ identifier: T_IDENTIFIER type: partial_specifier T_TYPE identifier { String name = String($3); - m_Type = context->GetTypeByName(name); + m_Type = ConfigCompilerContext::GetContext()->GetType(name); if (!m_Type) { if ($1) throw_exception(invalid_argument("partial type definition for unknown type '" + name + "'")); m_Type = boost::make_shared(name, yylloc); - context->AddType(m_Type); + ConfigCompilerContext::GetContext()->AddType(m_Type); } } type_inherits_specifier typerulelist @@ -236,7 +242,7 @@ object_inherits_specifier expressionlist m_Item->SetLocal(m_Local); m_Item->SetAbstract(m_Abstract); - context->AddObject(m_Item->Compile()); + ConfigCompilerContext::GetContext()->AddItem(m_Item->Compile()); m_Item.reset(); } ; diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index adb0ef08f..a7f178c56 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -71,33 +71,6 @@ void *ConfigCompiler::GetScanner(void) const return m_Scanner; } -/** - * Retrieves the result from the compiler. - * - * @returns A list of configuration items. - */ -vector ConfigCompiler::GetResultObjects(void) const -{ - return m_ResultObjects; -} - -/** - * Retrieves the resulting type objects from the compiler. - * - * @returns A list of type objects. - */ -vector ConfigCompiler::GetResultTypes(void) const -{ - vector types; - - ConfigType::Ptr type; - BOOST_FOREACH(tie(tuples::ignore, type), m_ResultTypes) { - types.push_back(type); - } - - return types; -} - /** * Retrieves the path for the input file. * @@ -125,12 +98,7 @@ void ConfigCompiler::HandleInclude(const String& include, bool search, const Deb else path = Utility::DirName(GetPath()) + "/" + include; - vector types; - m_HandleInclude(path, search, &m_ResultObjects, &types, debuginfo); - - BOOST_FOREACH(const ConfigType::Ptr& type, types) { - AddType(type); - } + m_HandleInclude(path, search, debuginfo); } /** @@ -150,23 +118,12 @@ void ConfigCompiler::HandleLibrary(const String& library) * @param stream The input stream. * @returns Configuration items. */ -void ConfigCompiler::CompileStream(const String& path, - istream *stream, vector *resultItems, vector *resultTypes) +void ConfigCompiler::CompileStream(const String& path, istream *stream) { stream->exceptions(istream::badbit); ConfigCompiler ctx(path, stream); ctx.Compile(); - - if (resultItems) { - vector items = ctx.GetResultObjects(); - std::copy(items.begin(), items.end(), std::back_inserter(*resultItems)); - } - - if (resultTypes) { - vector types = ctx.GetResultTypes(); - std::copy(types.begin(), types.end(), std::back_inserter(*resultTypes)); - } } /** @@ -175,8 +132,7 @@ void ConfigCompiler::CompileStream(const String& path, * @param path The path. * @returns Configuration items. */ -void ConfigCompiler::CompileFile(const String& path, - vector *resultItems, vector *resultTypes) +void ConfigCompiler::CompileFile(const String& path) { ifstream stream; stream.open(path.CStr(), ifstream::in); @@ -186,7 +142,7 @@ void ConfigCompiler::CompileFile(const String& path, Logger::Write(LogInformation, "config", "Compiling config file: " + path); - return CompileStream(path, &stream, resultItems, resultTypes); + return CompileStream(path, &stream); } /** @@ -196,11 +152,10 @@ void ConfigCompiler::CompileFile(const String& path, * @param text The text. * @returns Configuration items. */ -void ConfigCompiler::CompileText(const String& path, const String& text, - vector *resultItems, vector *resultTypes) +void ConfigCompiler::CompileText(const String& path, const String& text) { stringstream stream(text); - return CompileStream(path, &stream, resultItems, resultTypes); + return CompileStream(path, &stream); } /** @@ -209,12 +164,10 @@ void ConfigCompiler::CompileText(const String& path, const String& text, * * @param include The path from the include directive. * @param search Whether to search include dirs. - * @param resultItems The resulting items. - * @param resultTypes The resulting types. * @param debuginfo Debug information. */ void ConfigCompiler::HandleFileInclude(const String& include, bool search, - vector *resultItems, vector *resultTypes, const DebugInfo& debuginfo) + const DebugInfo& debuginfo) { String includePath = include; @@ -239,23 +192,13 @@ void ConfigCompiler::HandleFileInclude(const String& include, bool search, vector items; - if (!Utility::Glob(includePath, boost::bind(&ConfigCompiler::CompileFile, _1, resultItems, resultTypes))) { + if (!Utility::Glob(includePath, boost::bind(&ConfigCompiler::CompileFile, _1))) { stringstream msgbuf; msgbuf << "Include file '" + include + "' does not exist (or no files found for pattern): " << debuginfo; throw_exception(invalid_argument(msgbuf.str())); } } -/** - * Adds an object to the result. - * - * @param object The configuration item. - */ -void ConfigCompiler::AddObject(const ConfigItem::Ptr& object) -{ - m_ResultObjects.push_back(object); -} - /** * Adds a directory to the list of include search dirs. * @@ -268,19 +211,3 @@ void ConfigCompiler::AddIncludeSearchDir(const String& dir) m_IncludeSearchDirs.push_back(dir); } -void ConfigCompiler::AddType(const ConfigType::Ptr& type) -{ - m_ResultTypes[type->GetName()] = type; -} - -ConfigType::Ptr ConfigCompiler::GetTypeByName(const String& name) const -{ - map::const_iterator it; - - it = m_ResultTypes.find(name); - - if (it == m_ResultTypes.end()) - return ConfigType::Ptr(); - - return it->second; -} diff --git a/lib/config/configcompiler.h b/lib/config/configcompiler.h index 006e54432..036b3e8b3 100644 --- a/lib/config/configcompiler.h +++ b/lib/config/configcompiler.h @@ -32,8 +32,7 @@ namespace icinga class I2_CONFIG_API ConfigCompiler { public: - typedef function *, - vector *, const DebugInfo&)> HandleIncludeFunc; + typedef function HandleIncludeFunc; ConfigCompiler(const String& path, istream *input = &cin, HandleIncludeFunc includeHandler = &ConfigCompiler::HandleFileInclude); @@ -41,32 +40,21 @@ public: void Compile(void); - static void CompileStream(const String& path, - istream *stream, vector *resultItems, vector *resultTypes); - static void CompileFile(const String& path, vector *resultItems, vector *resultTypes); - static void CompileText(const String& path, - const String& text, vector *resultItems, vector *resultTypes); + static void CompileStream(const String& path, istream *stream); + static void CompileFile(const String& path); + static void CompileText(const String& path, const String& text); static void AddIncludeSearchDir(const String& dir); - vector GetResultObjects(void) const; - vector GetResultTypes(void) const; - String GetPath(void) const; static void HandleFileInclude(const String& include, bool search, - vector *resultItems, vector *resultTypes, const DebugInfo& debuginfo); /* internally used methods */ void HandleInclude(const String& include, bool search, const DebugInfo& debuginfo); void HandleLibrary(const String& library); - void AddObject(const ConfigItem::Ptr& object); - void AddType(const ConfigType::Ptr& type); - - ConfigType::Ptr GetTypeByName(const String& name) const; - size_t ReadInput(char *buffer, size_t max_bytes); void *GetScanner(void) const; @@ -77,8 +65,6 @@ private: HandleIncludeFunc m_HandleInclude; void *m_Scanner; - vector m_ResultObjects; - map m_ResultTypes; static vector m_IncludeSearchDirs; diff --git a/lib/config/configcompilercontext.cpp b/lib/config/configcompilercontext.cpp new file mode 100644 index 000000000..f973426a5 --- /dev/null +++ b/lib/config/configcompilercontext.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "i2-config.h" + +using std::ifstream; + +using namespace icinga; + +ConfigCompilerContext *ConfigCompilerContext::m_Context = NULL; + +ConfigCompilerContext::ConfigCompilerContext(void) + : m_Flags(0) +{ } + +void ConfigCompilerContext::AddItem(const ConfigItem::Ptr& item) +{ + m_Items.push_back(item); + m_ItemsMap[make_pair(item->GetType(), item->GetName())] = item; +} + +ConfigItem::Ptr ConfigCompilerContext::GetItem(const String& type, const String& name) const +{ + map, ConfigItem::Ptr>::const_iterator it; + + it = m_ItemsMap.find(make_pair(type, name)); + + if (it == m_ItemsMap.end()) + return ConfigItem::Ptr(); + + return it->second; +} + +vector ConfigCompilerContext::GetItems(void) const +{ + return m_Items; +} + +void ConfigCompilerContext::AddType(const ConfigType::Ptr& type) +{ + m_Types[type->GetName()] = type; +} + +ConfigType::Ptr ConfigCompilerContext::GetType(const String& name) const +{ + map::const_iterator it; + + it = m_Types.find(name); + + if (it == m_Types.end()) + return ConfigType::Ptr(); + + return it->second; +} + +void ConfigCompilerContext::AddError(bool warning, const String& message) +{ + m_Errors.push_back(ConfigCompilerError(warning, message)); +} + +vector ConfigCompilerContext::GetErrors(void) const +{ + return m_Errors; +} + +void ConfigCompilerContext::SetFlags(int flags) +{ + m_Flags = flags; +} + +int ConfigCompilerContext::GetFlags(void) const +{ + return m_Flags; +} + +void ConfigCompilerContext::SetContext(ConfigCompilerContext *context) +{ + assert(m_Context == NULL || context == NULL); + + m_Context = context; +} + +ConfigCompilerContext *ConfigCompilerContext::GetContext(void) +{ + return m_Context; +} + +void ConfigCompilerContext::Validate(void) +{ + SetContext(this); + + BOOST_FOREACH(const ConfigItem::Ptr& item, m_Items) { + ConfigType::Ptr ctype = GetType(item->GetType()); + + if (!ctype) + continue; + + ctype->ValidateItem(item); + } + + SetContext(NULL); +} + +void ConfigCompilerContext::ActivateItems(void) +{ + assert(m_Context == NULL); + + Logger::Write(LogInformation, "config", "Executing config items..."); + BOOST_FOREACH(const ConfigItem::Ptr& item, m_Items) { + item->Commit(); + } +} diff --git a/lib/config/configcompilercontext.h b/lib/config/configcompilercontext.h new file mode 100644 index 000000000..de2e80a76 --- /dev/null +++ b/lib/config/configcompilercontext.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef CONFIGCOMPILERCONTEXT_H +#define CONFIGCOMPILERCONTEXT_H + +namespace icinga +{ + +/** + * @ingroup config + */ +enum ConfigCompilerFlag +{ + CompilerStrict = 1, /**< Treat warnings as errors. */ + CompilerLinkExisting = 2 /**< Link objects to existing config items. */ +}; + +struct ConfigCompilerError +{ + bool Warning; + String Message; + + ConfigCompilerError(bool warning, const String& message) + : Warning(warning), Message(message) + { } +}; + +/* + * @ingroup config + */ +class ConfigCompilerContext +{ +public: + ConfigCompilerContext(void); + + void AddItem(const ConfigItem::Ptr& item); + ConfigItem::Ptr GetItem(const String& type, const String& name) const; + vector GetItems(void) const; + + void AddType(const ConfigType::Ptr& type); + ConfigType::Ptr GetType(const String& name) const; + + void AddError(bool warning, const String& message); + vector GetErrors(void) const; + + void SetFlags(int flags); + int GetFlags(void) const; + + void Validate(void); + void ActivateItems(void); + + static void SetContext(ConfigCompilerContext *context); + static ConfigCompilerContext *GetContext(void); + +private: + int m_Flags; + + vector > m_Items; + map, shared_ptr > m_ItemsMap; + + map > m_Types; + + vector m_Errors; + + static ConfigCompilerContext *m_Context; +}; + +} + +#endif /* CONFIGCOMPILERCONTEXT_H */ diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index dd5251523..77628afd0 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -92,6 +92,13 @@ vector ConfigItem::GetParents(void) const return m_Parents; } +Dictionary::Ptr ConfigItem::Link(void) const +{ + Dictionary::Ptr attrs = boost::make_shared(); + InternalLink(attrs); + return attrs; +} + /** * Calculates the object's properties based on parent objects and the object's * expression list. @@ -99,7 +106,7 @@ vector ConfigItem::GetParents(void) const * @param dictionary The dictionary that should be used to store the * properties. */ -void ConfigItem::CalculateProperties(const Dictionary::Ptr& dictionary) const +void ConfigItem::InternalLink(const Dictionary::Ptr& dictionary) const { BOOST_FOREACH(const String& name, m_Parents) { ConfigItem::Ptr parent = ConfigItem::GetObject(GetType(), name); @@ -111,7 +118,7 @@ void ConfigItem::CalculateProperties(const Dictionary::Ptr& dictionary) const throw_exception(domain_error(message.str())); } - parent->CalculateProperties(dictionary); + parent->InternalLink(dictionary); } m_ExpressionList->Execute(dictionary); @@ -129,8 +136,7 @@ DynamicObject::Ptr ConfigItem::Commit(void) DynamicObject::Ptr dobj = m_DynamicObject.lock(); - Dictionary::Ptr properties = boost::make_shared(); - CalculateProperties(properties); + Dictionary::Ptr properties = Link(); /* Create a fake update in the format that * DynamicObject::ApplyUpdate expects. */ @@ -279,12 +285,27 @@ DynamicObject::Ptr ConfigItem::GetDynamicObject(void) const ConfigItem::Ptr ConfigItem::GetObject(const String& type, const String& name) { ConfigItem::ItemMap::iterator it; + + ConfigCompilerContext *context = ConfigCompilerContext::GetContext(); + + if (context) { + ConfigItem::Ptr item = context->GetItem(type, name); + + if (item) + return item; + + /* ignore already active objects while we're in the compiler + * context and linking to existing items is disabled. */ + if ((context->GetFlags() & CompilerLinkExisting) == 0) + return ConfigItem::Ptr(); + } + it = m_Items.find(make_pair(type, name)); - if (it == m_Items.end()) - return ConfigItem::Ptr(); + if (it != m_Items.end()) + return it->second; - return it->second; + return ConfigItem::Ptr(); } void ConfigItem::Dump(ostream& fp) const diff --git a/lib/config/configitem.h b/lib/config/configitem.h index 2cc73c919..1bc3d8892 100644 --- a/lib/config/configitem.h +++ b/lib/config/configitem.h @@ -54,6 +54,8 @@ public: DebugInfo GetDebugInfo(void) const; + Dictionary::Ptr Link(void) const; + static ConfigItem::Ptr GetObject(const String& type, const String& name); @@ -61,7 +63,7 @@ public: static boost::signal OnRemoved; private: - void CalculateProperties(const Dictionary::Ptr& dictionary) const; + void InternalLink(const Dictionary::Ptr& dictionary) const; void RegisterChild(const ConfigItem::Ptr& child); void UnregisterChild(const ConfigItem::Ptr& child); diff --git a/lib/config/configtype.cpp b/lib/config/configtype.cpp index c4ff22b15..90b5eebfb 100644 --- a/lib/config/configtype.cpp +++ b/lib/config/configtype.cpp @@ -21,8 +21,6 @@ using namespace icinga; -ConfigType::TypeMap ConfigType::m_Types; - ConfigType::ConfigType(const String& name, const DebugInfo& debuginfo) : m_Name(name), m_RuleList(boost::make_shared()), m_DebugInfo(debuginfo) { } @@ -52,68 +50,77 @@ DebugInfo ConfigType::GetDebugInfo(void) const return m_DebugInfo; } -void ConfigType::ValidateObject(const DynamicObject::Ptr& object) const +void ConfigType::ValidateItem(const ConfigItem::Ptr& object) const { - DynamicObject::AttributeConstIterator it; - const DynamicObject::AttributeMap& attributes = object->GetAttributes(); - - for (it = attributes.begin(); it != attributes.end(); it++) { - if ((it->second.Type & Attribute_Config) == 0) - continue; - - if (!ValidateAttribute(it->first, it->second.Data)) - Logger::Write(LogWarning, "config", "Configuration attribute '" + it->first + - "' on object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "' is unknown or contains an invalid type."); - } -} + Dictionary::Ptr attrs = object->Link(); -void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, const TypeRuleList::Ptr& ruleList) -{ - String key; - Value value; - BOOST_FOREACH(tie(key, value), dictionary) { - // TODO: implement (#3619) - } -} + vector locations; + locations.push_back("Object '" + object->GetName() + "' (Type: '" + object->GetType() + "')"); -bool ConfigType::ValidateAttribute(const String& name, const Value& value) const -{ ConfigType::Ptr parent; - if (m_Parent.IsEmpty()) { if (GetName() != "DynamicObject") - parent = ConfigType::GetByName("DynamicObject"); + parent = ConfigCompilerContext::GetContext()->GetType("DynamicObject"); } else { - parent = ConfigType::GetByName(m_Parent); + parent = ConfigCompilerContext::GetContext()->GetType(m_Parent); } - - if (parent && parent->ValidateAttribute(name, value)) - return true; - - TypeRuleList::Ptr subRules; - - if (!m_RuleList->FindMatch(name, value, &subRules)) - return false; - - if (subRules && value.IsObjectType()) - ValidateDictionary(value, subRules); - - return true; -} -void ConfigType::Commit(void) -{ - m_Types[GetName()] = GetSelf(); + vector ruleLists; + if (parent) + ruleLists.push_back(parent->m_RuleList); + + ruleLists.push_back(m_RuleList); + + ValidateDictionary(attrs, ruleLists, locations); } -ConfigType::Ptr ConfigType::GetByName(const String& name) +void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, + const vector& ruleLists, vector& locations) { - ConfigType::TypeMap::iterator it; - - it = m_Types.find(name); - - if (it == m_Types.end()) - return ConfigType::Ptr(); - - return it->second; + String key; + Value value; + BOOST_FOREACH(tie(key, value), dictionary) { + TypeValidationResult overallResult = ValidationUnknownField; + vector subRuleLists; + + locations.push_back("Attribute '" + key + "'"); + + BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { + TypeRuleList::Ptr subRuleList; + TypeValidationResult result = ruleList->Validate(key, value, &subRuleList); + + if (subRuleList) + subRuleLists.push_back(subRuleList); + + if (result == ValidationOK) { + overallResult = result; + break; + } + + if (result == ValidationInvalidType) + overallResult = result; + } + + bool first = true; + String stack; + BOOST_FOREACH(const String& location, locations) { + if (!first) + stack += " -> "; + else + first = false; + + stack += location; + } + + if (overallResult == ValidationUnknownField) + ConfigCompilerContext::GetContext()->AddError(true, "Unknown attribute: " + stack); + else if (overallResult == ValidationInvalidType) + ConfigCompilerContext::GetContext()->AddError(false, "Invalid type for attribute: " + stack); + + if (subRuleLists.size() > 0 && value.IsObjectType()) + ValidateDictionary(value, subRuleLists, locations); + + locations.pop_back(); + } } + diff --git a/lib/config/configtype.h b/lib/config/configtype.h index 08d2d9347..f66c31c34 100644 --- a/lib/config/configtype.h +++ b/lib/config/configtype.h @@ -23,6 +23,8 @@ namespace icinga { +struct ConfigCompilerContext; + /** * A configuration type. Used to validate config objects. * @@ -40,16 +42,12 @@ public: String GetParent(void) const; void SetParent(const String& parent); - void Commit(void); - TypeRuleList::Ptr GetRuleList(void) const; DebugInfo GetDebugInfo(void) const; - void ValidateObject(const DynamicObject::Ptr& object) const; + void ValidateItem(const ConfigItem::Ptr& object) const; - static ConfigType::Ptr GetByName(const String& name); - private: String m_Name; /**< The type name. */ String m_Parent; /**< The parent type. */ @@ -57,11 +55,8 @@ private: TypeRuleList::Ptr m_RuleList; DebugInfo m_DebugInfo; /**< Debug information. */ - typedef map TypeMap; - static TypeMap m_Types; /**< All registered configuration types. */ - - bool ValidateAttribute(const String& name, const Value& value) const; - static void ValidateDictionary(const Dictionary::Ptr& dictionary, const TypeRuleList::Ptr& ruleList); + static void ValidateDictionary(const Dictionary::Ptr& dictionary, + const vector& ruleLists, vector& locations); }; diff --git a/lib/config/i2-config.h b/lib/config/i2-config.h index 468f1a92e..0a3eb71c3 100644 --- a/lib/config/i2-config.h +++ b/lib/config/i2-config.h @@ -48,11 +48,12 @@ using std::endl; #include "debuginfo.h" #include "typerulelist.h" #include "typerule.h" -#include "configtype.h" #include "expression.h" #include "expressionlist.h" #include "configitem.h" +#include "configtype.h" #include "configitembuilder.h" #include "configcompiler.h" +#include "configcompilercontext.h" #endif /* I2CONFIG_H */ diff --git a/lib/config/typerule.cpp b/lib/config/typerule.cpp index 59a9ad8ab..156c21737 100644 --- a/lib/config/typerule.cpp +++ b/lib/config/typerule.cpp @@ -31,11 +31,13 @@ TypeRuleList::Ptr TypeRule::GetSubRules(void) const return m_SubRules; } -bool TypeRule::Matches(const String& name, const Value& value) const +bool TypeRule::MatchName(const String& name) const { - if (!Utility::Match(m_NamePattern, name)) - return false; + return (Utility::Match(m_NamePattern, name)); +} +bool TypeRule::MatchValue(const Value& value) const +{ if (value.IsEmpty()) return true; @@ -64,3 +66,4 @@ bool TypeRule::Matches(const String& name, const Value& value) const assert(!"Type rule has invalid type specifier."); } } + diff --git a/lib/config/typerule.h b/lib/config/typerule.h index e95974f49..384bae68d 100644 --- a/lib/config/typerule.h +++ b/lib/config/typerule.h @@ -50,7 +50,8 @@ public: TypeRuleList::Ptr GetSubRules(void) const; - bool Matches(const String& name, const Value& value) const; + bool MatchName(const String& name) const; + bool MatchValue(const Value& value) const; private: TypeSpecifier m_Type; diff --git a/lib/config/typerulelist.cpp b/lib/config/typerulelist.cpp index d9d156dbd..7df540d9e 100644 --- a/lib/config/typerulelist.cpp +++ b/lib/config/typerulelist.cpp @@ -54,20 +54,31 @@ size_t TypeRuleList::GetLength(void) const } /** - * Finds a matching rule. + * Validates a field. * * @param name The name of the attribute. * @param value The value of the attribute. - * *@param[out] subRules The list of sub-rules for the matching rule. + * @param[out] subRules The list of sub-rules for the matching rule. + * @returns The validation result. */ -bool TypeRuleList::FindMatch(const String& name, const Value& value, TypeRuleList::Ptr *subRules) +TypeValidationResult TypeRuleList::Validate(const String& name, const Value& value, TypeRuleList::Ptr *subRules) const { + bool foundField = false; BOOST_FOREACH(const TypeRule& rule, m_Rules) { - if (rule.Matches(name, value)) { + if (!rule.MatchName(name)) + continue; + + foundField = true; + + if (rule.MatchValue(value)) { *subRules = rule.GetSubRules(); - return true; + return ValidationOK; } } - - return false; + + if (foundField) + return ValidationInvalidType; + else + return ValidationUnknownField; } + diff --git a/lib/config/typerulelist.h b/lib/config/typerulelist.h index 4ec128ed6..b12d3af1a 100644 --- a/lib/config/typerulelist.h +++ b/lib/config/typerulelist.h @@ -25,6 +25,16 @@ namespace icinga struct TypeRule; +/** + * @ingroup config + */ +enum TypeValidationResult +{ + ValidationOK, + ValidationInvalidType, + ValidationUnknownField +}; + /** * A list of configuration type rules. * @@ -39,7 +49,7 @@ public: void AddRule(const TypeRule& rule); void AddRules(const TypeRuleList::Ptr& ruleList); - bool FindMatch(const String& name, const Value& value, TypeRuleList::Ptr *subRules); + TypeValidationResult Validate(const String& name, const Value& value, TypeRuleList::Ptr *subRules) const; size_t GetLength(void) const;