Validate config objects before trying to activate them.

Fixes #3619
This commit is contained in:
Gunnar Beutner 2013-02-05 13:06:42 +01:00
parent 312756a886
commit 7a150d2c29
19 changed files with 475 additions and 277 deletions

View File

@ -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<ConfigItem::Ptr> allItems;
ConfigCompilerContext context;
try {
BOOST_FOREACH(const String& configPath, g_AppParams["config"].as<vector<String> >()) {
vector<ConfigItem::Ptr> items;
vector<ConfigType::Ptr> types;
ConfigCompilerContext::SetContext(&context);
ConfigCompiler::CompileFile(configPath, &items, &types);
BOOST_FOREACH(const String& configPath, g_AppParams["config"].as<vector<String> >()) {
ConfigCompiler::CompileFile(configPath);
}
Logger::Write(LogInformation, "icinga-app", "Registering config types...");
BOOST_FOREACH(const ConfigType::Ptr& type, types) {
type->Commit();
}
ConfigCompilerContext::SetContext(NULL);
Logger::Write(LogInformation, "icinga-app", "Executing config items...");
BOOST_FOREACH(const ConfigItem::Ptr& item, items) {
item->Commit();
}
context.Validate();
Logger::Write(LogInformation, "icinga-app", "Validating config items...");
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);
}
}
if (hasError)
return false;
/* 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();
}
g_ConfigItems.clear();
std::copy(allItems.begin(), allItems.end(), std::back_inserter(g_ConfigItems));
return true;
} catch (const exception& ex) {
Logger::Write(LogCritical, "icinga-app", "Configuration error: " + String(ex.what()));
return false;
item->Unregister();
}
g_ConfigItems.clear();
vector<ConfigItem::Ptr> 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();

View File

@ -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);

View File

@ -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 \

View File

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="configcompiler.h" />
<ClInclude Include="configcompilercontext.h" />
<ClInclude Include="configitem.h" />
<ClInclude Include="configitembuilder.h" />
<ClInclude Include="configtype.h" />
@ -33,6 +34,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="configcompiler.cpp" />
<ClCompile Include="configcompilercontext.cpp" />
<ClCompile Include="configitem.cpp" />
<ClCompile Include="configitembuilder.cpp" />
<ClCompile Include="configtype.cpp" />

View File

@ -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<ConfigType>(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<TypeRuleList>());
}
@ -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<ConfigItemBuilder>(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<ExpressionList>());
}
@ -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"

View File

@ -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<ConfigType>(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();
}
;

View File

@ -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<ConfigItem::Ptr> ConfigCompiler::GetResultObjects(void) const
{
return m_ResultObjects;
}
/**
* Retrieves the resulting type objects from the compiler.
*
* @returns A list of type objects.
*/
vector<ConfigType::Ptr> ConfigCompiler::GetResultTypes(void) const
{
vector<ConfigType::Ptr> 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<ConfigType::Ptr> 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<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *resultTypes)
void ConfigCompiler::CompileStream(const String& path, istream *stream)
{
stream->exceptions(istream::badbit);
ConfigCompiler ctx(path, stream);
ctx.Compile();
if (resultItems) {
vector<ConfigItem::Ptr> items = ctx.GetResultObjects();
std::copy(items.begin(), items.end(), std::back_inserter(*resultItems));
}
if (resultTypes) {
vector<ConfigType::Ptr> 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<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *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<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *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<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *resultTypes, const DebugInfo& debuginfo)
const DebugInfo& debuginfo)
{
String includePath = include;
@ -239,23 +192,13 @@ void ConfigCompiler::HandleFileInclude(const String& include, bool search,
vector<ConfigItem::Ptr> 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<String, ConfigType::Ptr>::const_iterator it;
it = m_ResultTypes.find(name);
if (it == m_ResultTypes.end())
return ConfigType::Ptr();
return it->second;
}

View File

@ -32,8 +32,7 @@ namespace icinga
class I2_CONFIG_API ConfigCompiler
{
public:
typedef function<void (const String&, bool, vector<ConfigItem::Ptr> *,
vector<ConfigType::Ptr> *, const DebugInfo&)> HandleIncludeFunc;
typedef function<void (const String&, bool, const DebugInfo&)> 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<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *resultTypes);
static void CompileFile(const String& path, vector<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *resultTypes);
static void CompileText(const String& path,
const String& text, vector<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *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<ConfigItem::Ptr> GetResultObjects(void) const;
vector<ConfigType::Ptr> GetResultTypes(void) const;
String GetPath(void) const;
static void HandleFileInclude(const String& include, bool search,
vector<ConfigItem::Ptr> *resultItems, vector<ConfigType::Ptr> *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<ConfigItem::Ptr> m_ResultObjects;
map<String, ConfigType::Ptr> m_ResultTypes;
static vector<String> m_IncludeSearchDirs;

View File

@ -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<pair<String, String>, 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<ConfigItem::Ptr> 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<String, ConfigType::Ptr>::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<ConfigCompilerError> 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();
}
}

View File

@ -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<ConfigItem::Ptr> GetItems(void) const;
void AddType(const ConfigType::Ptr& type);
ConfigType::Ptr GetType(const String& name) const;
void AddError(bool warning, const String& message);
vector<ConfigCompilerError> 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<shared_ptr<ConfigItem> > m_Items;
map<pair<String, String>, shared_ptr<ConfigItem> > m_ItemsMap;
map<String, shared_ptr<ConfigType> > m_Types;
vector<ConfigCompilerError> m_Errors;
static ConfigCompilerContext *m_Context;
};
}
#endif /* CONFIGCOMPILERCONTEXT_H */

View File

@ -92,6 +92,13 @@ vector<String> ConfigItem::GetParents(void) const
return m_Parents;
}
Dictionary::Ptr ConfigItem::Link(void) const
{
Dictionary::Ptr attrs = boost::make_shared<Dictionary>();
InternalLink(attrs);
return attrs;
}
/**
* Calculates the object's properties based on parent objects and the object's
* expression list.
@ -99,7 +106,7 @@ vector<String> 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<Dictionary>();
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

View File

@ -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<void (const ConfigItem::Ptr&)> 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);

View File

@ -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<TypeRuleList>()), 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;
Dictionary::Ptr attrs = object->Link();
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.");
vector<String> locations;
locations.push_back("Object '" + object->GetName() + "' (Type: '" + object->GetType() + "')");
ConfigType::Ptr parent;
if (m_Parent.IsEmpty()) {
if (GetName() != "DynamicObject")
parent = ConfigCompilerContext::GetContext()->GetType("DynamicObject");
} else {
parent = ConfigCompilerContext::GetContext()->GetType(m_Parent);
}
vector<TypeRuleList::Ptr> ruleLists;
if (parent)
ruleLists.push_back(parent->m_RuleList);
ruleLists.push_back(m_RuleList);
ValidateDictionary(attrs, ruleLists, locations);
}
void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, const TypeRuleList::Ptr& ruleList)
void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary,
const vector<TypeRuleList::Ptr>& ruleLists, vector<String>& locations)
{
String key;
Value value;
BOOST_FOREACH(tie(key, value), dictionary) {
// TODO: implement (#3619)
TypeValidationResult overallResult = ValidationUnknownField;
vector<TypeRuleList::Ptr> 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<Dictionary>())
ValidateDictionary(value, subRuleLists, locations);
locations.pop_back();
}
}
bool ConfigType::ValidateAttribute(const String& name, const Value& value) const
{
ConfigType::Ptr parent;
if (m_Parent.IsEmpty()) {
if (GetName() != "DynamicObject")
parent = ConfigType::GetByName("DynamicObject");
} else {
parent = ConfigType::GetByName(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<Dictionary>())
ValidateDictionary(value, subRules);
return true;
}
void ConfigType::Commit(void)
{
m_Types[GetName()] = GetSelf();
}
ConfigType::Ptr ConfigType::GetByName(const String& name)
{
ConfigType::TypeMap::iterator it;
it = m_Types.find(name);
if (it == m_Types.end())
return ConfigType::Ptr();
return it->second;
}

View File

@ -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<String, ConfigType::Ptr> 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<TypeRuleList::Ptr>& ruleLists, vector<String>& locations);
};

View File

@ -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 */

View File

@ -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.");
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;