From: Michael Friedrich Date: Mon, 3 Aug 2015 12:15:05 +0000 (+0200) Subject: Implement support for writing configuration files X-Git-Tag: v2.4.0~453 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=973db46d40314d1f817f49fc9acedaf62e9da0e9;p=icinga2 Implement support for writing configuration files fixes #9099 --- diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 102f6e6d1..f60b1b852 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -37,6 +37,7 @@ set(config_SOURCES applyrule.cpp configcompilercontext.cpp configcompiler.cpp configitembuilder.cpp configitem.cpp ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS} + configwriter.cpp expression.cpp objectrule.cpp ) diff --git a/lib/config/configwriter.cpp b/lib/config/configwriter.cpp new file mode 100644 index 000000000..278aae122 --- /dev/null +++ b/lib/config/configwriter.cpp @@ -0,0 +1,218 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 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 "config/configwriter.hpp" +#include "base/exception.hpp" +#include +#include +#include + +using namespace icinga; + +ConfigWriter::ConfigWriter(const String& fileName) + : m_FP(fileName.CStr(), std::ofstream::out | std::ostream::trunc) +{ } + +void ConfigWriter::EmitBoolean(bool val) +{ + m_FP << (val ? "true" : "false"); +} + +void ConfigWriter::EmitNumber(double val) +{ + m_FP << val; +} + +void ConfigWriter::EmitString(const String& val) +{ + m_FP << "\"" << EscapeIcingaString(val) << "\""; +} + +void ConfigWriter::EmitEmpty(void) +{ + m_FP << "null"; +} + +void ConfigWriter::EmitArray(const Array::Ptr& val) +{ + m_FP << "[ "; + EmitArrayItems(val); + m_FP << " ]"; +} + +void ConfigWriter::EmitArrayItems(const Array::Ptr& val) +{ + bool first = true; + + ObjectLock olock(val); + BOOST_FOREACH(const Value& item, val) { + if (first) + first = false; + else + m_FP << ", "; + + EmitValue(0, item); + } +} + +void ConfigWriter::EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports) +{ + m_FP << "{"; + + if (imports && imports->GetLength() > 0) { + ObjectLock xlock(imports); + BOOST_FOREACH(const Value& import, imports) { + m_FP << "\n"; + EmitIndent(indentLevel); + m_FP << "import \"" << import << "\""; + } + + m_FP << "\n"; + } + + ObjectLock olock(val); + BOOST_FOREACH(const Dictionary::Pair& kv, val) { + m_FP << "\n"; + EmitIndent(indentLevel); + EmitIdentifier(kv.first, true); + m_FP << " = "; + EmitValue(indentLevel + 1, kv.second); + } + + m_FP << "\n"; + EmitIndent(indentLevel - 1); + m_FP << "}"; +} + +void ConfigWriter::EmitValue(int indentLevel, const Value& val) +{ + if (val.IsObjectType()) + EmitArray(val); + else if (val.IsObjectType()) + EmitScope(indentLevel, val); + else if (val.IsString()) + EmitString(val); + else if (val.IsNumber()) + EmitNumber(val); + else if (val.IsBoolean()) + EmitBoolean(val); + else if (val.IsEmpty()) + EmitEmpty(); +} + +void ConfigWriter::EmitRaw(const String& val) +{ + m_FP << val; +} + +void ConfigWriter::EmitIndent(int indentLevel) +{ + for (int i = 0; i < indentLevel; i++) + m_FP << "\t"; +} + +void ConfigWriter::EmitIdentifier(const String& identifier, bool inAssignment) +{ + static std::set keywords; + if (keywords.empty()) { + keywords.insert("object"); + keywords.insert("template"); + keywords.insert("include"); + keywords.insert("include_recursive"); + keywords.insert("library"); + keywords.insert("null"); + keywords.insert("true"); + keywords.insert("false"); + keywords.insert("const"); + keywords.insert("var"); + keywords.insert("this"); + keywords.insert("globals"); + keywords.insert("locals"); + keywords.insert("use"); + keywords.insert("apply"); + keywords.insert("to"); + keywords.insert("where"); + keywords.insert("import"); + keywords.insert("assign"); + keywords.insert("ignore"); + keywords.insert("function"); + keywords.insert("return"); + keywords.insert("break"); + keywords.insert("continue"); + keywords.insert("for"); + keywords.insert("if"); + keywords.insert("else"); + keywords.insert("while"); + } + + if (keywords.find(identifier) != keywords.end()) { + m_FP << "@" << identifier; + return; + } + + boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$"); + boost::smatch what; + if (boost::regex_search(identifier.GetData(), what, expr)) + m_FP << identifier; + else if (inAssignment) + EmitString(identifier); + else + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier")); +} + +void ConfigWriter::EmitConfigItem(const String& type, const String& name, bool isTemplate, + const Array::Ptr& imports, const Dictionary::Ptr& attrs) +{ + if (isTemplate) + m_FP << "template "; + else + m_FP << "object "; + + EmitIdentifier(type, false); + m_FP << " "; + EmitString(name); + m_FP << " "; + EmitScope(1, attrs, imports); +} + +void ConfigWriter::EmitComment(const String& text) +{ + m_FP << "/* " << text << " */\n"; +} + +void ConfigWriter::EmitFunctionCall(const String& name, const Array::Ptr& arguments) +{ + EmitIdentifier(name, false); + m_FP << "("; + EmitArrayItems(arguments); + m_FP << ")"; +} + +String ConfigWriter::EscapeIcingaString(const String& str) +{ + String result = str; + boost::algorithm::replace_all(result, "\\", "\\\\"); + boost::algorithm::replace_all(result, "\n", "\\n"); + boost::algorithm::replace_all(result, "\t", "\\t"); + boost::algorithm::replace_all(result, "\r", "\\r"); + boost::algorithm::replace_all(result, "\b", "\\b"); + boost::algorithm::replace_all(result, "\f", "\\f"); + boost::algorithm::replace_all(result, "\"", "\\\""); + return result; +} diff --git a/lib/config/configwriter.hpp b/lib/config/configwriter.hpp new file mode 100644 index 000000000..e27af011c --- /dev/null +++ b/lib/config/configwriter.hpp @@ -0,0 +1,70 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2015 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 CONFIGWRITER_H +#define CONFIGWRITER_H + +#include "config/i2-config.hpp" +#include "base/object.hpp" +#include "base/array.hpp" +#include "base/dictionary.hpp" +#include + +namespace icinga +{ + +/** + * A configuration writer. + * + * @ingroup config + */ +class I2_CONFIG_API ConfigWriter : public Object { +public: + DECLARE_PTR_TYPEDEFS(ConfigWriter); + + ConfigWriter(const String& fileName); + + void EmitBoolean(bool val); + void EmitNumber(double val); + void EmitString(const String& val); + void EmitEmpty(void); + void EmitArray(const Array::Ptr& val); + void EmitArrayItems(const Array::Ptr& val); + void EmitDictionary(const Dictionary::Ptr& val); + void EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports = Array::Ptr()); + void EmitValue(int indentLevel, const Value& val); + void EmitRaw(const String& val); + void EmitIndent(int indentLevel); + + void EmitIdentifier(const String& identifier, bool inAssignment); + void EmitConfigItem(const String& type, const String& name, bool isTemplate, + const Array::Ptr& imports, const Dictionary::Ptr& attrs); + + void EmitComment(const String& text); + void EmitFunctionCall(const String& name, const Array::Ptr& arguments); + +private: + std::ofstream m_FP; + + static String EscapeIcingaString(const String& str); +}; + +} + +#endif /* CONFIGWRITER_H */