diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index 121effb76..4eb5d7286 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -24,6 +24,7 @@ set(cli_SOURCES objectlistcommand.cpp pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp pkirequestcommand.cpp pkisavecertcommand.cpp pkiticketcommand.cpp pkiutility.cpp + replcommand.cpp repositoryclearchangescommand.cpp repositorycommitcommand.cpp repositoryobjectcommand.cpp repositoryutility.cpp variablegetcommand.cpp variablelistcommand.cpp variableutility.cpp ) diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index 7193d2d53..c269214e9 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -106,12 +106,14 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con if (vm.count("config") > 0) { BOOST_FOREACH(const String& configPath, vm["config"].as >()) { Expression *expression = ConfigCompiler::CompileFile(configPath); - ExecuteExpression(expression); + if (expression) + ExecuteExpression(expression); delete expression; } } else if (!vm.count("no-config")) { Expression *expression = ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf"); - ExecuteExpression(expression); + if (expression) + ExecuteExpression(expression); delete expression; } @@ -128,7 +130,8 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con String name, fragment; BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) { Expression *expression = ConfigCompiler::CompileText(name, fragment); - ExecuteExpression(expression); + if (expression) + ExecuteExpression(expression); delete expression; } diff --git a/lib/cli/replcommand.cpp b/lib/cli/replcommand.cpp new file mode 100644 index 000000000..e2f495073 --- /dev/null +++ b/lib/cli/replcommand.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 "cli/replcommand.hpp" +#include "config/configcompiler.hpp" +#include "config/configcompilercontext.hpp" +#include "base/json.hpp" +#include + +using namespace icinga; +namespace po = boost::program_options; + +REGISTER_CLICOMMAND("repl", ReplCommand); + +String ReplCommand::GetDescription(void) const +{ + return "Interprets Icinga script expressions."; +} + +String ReplCommand::GetShortDescription(void) const +{ + return "Icinga REPL console"; +} + +bool ReplCommand::IsHidden(void) const +{ + return true; +} + +void ReplCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const +{ +} + +/** + * The entry point for the "repl" CLI command. + * + * @returns An exit status. + */ +int ReplCommand::Run(const po::variables_map& vm, const std::vector& ap) const +{ + VMFrame frame; + + while (std::cin.good()) { + std::cout << "=> "; + + std::string line; + std::getline(std::cin, line); + + Expression *expr; + + try { + ConfigCompilerContext::GetInstance()->Reset(); + + expr = ConfigCompiler::CompileText("", line); + + BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) { + std::cout << (message.Error ? "Error" : "Warning") << ": " << message.Text << "\n"; + } + + if (expr) { + Value result = expr->Evaluate(frame); + if (!result.IsObject() || result.IsObjectType() || result.IsObjectType()) + std::cout << JsonEncode(result) << "\n"; + else + std::cout << result << "\n"; + } + } catch (const ConfigError& ex) { + std::cout << ex.what() << "\n"; + } catch (const std::exception& ex) { + std::cout << "Error: " << DiagnosticInformation(ex) << "\n"; + } + + delete expr; + } +} diff --git a/lib/cli/replcommand.hpp b/lib/cli/replcommand.hpp new file mode 100644 index 000000000..cbef3dfea --- /dev/null +++ b/lib/cli/replcommand.hpp @@ -0,0 +1,48 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 REPLCOMMAND_H +#define REPLCOMMAND_H + +#include "cli/clicommand.hpp" + +namespace icinga +{ + +/** + * The "repl" CLI command. + * + * @ingroup cli + */ +class ReplCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(ReplCommand); + + virtual String GetDescription(void) const; + virtual String GetShortDescription(void) const; + virtual bool IsHidden(void) const; + virtual void InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; +}; + +} + +#endif /* REPLCOMMAND_H */ diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 341a11035..9ba840a72 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -255,8 +255,10 @@ Expression *ConfigCompiler::Compile(void) m_Expressions.push(std::vector()); try { - if (yyparse(this) != 0) - BOOST_THROW_EXCEPTION(ConfigError("Syntax error")); + if (yyparse(this) != 0) { + m_Expressions.pop(); + return NULL; + } DictExpression *expr = new DictExpression(m_Expressions.top()); m_Expressions.pop(); @@ -279,7 +281,8 @@ Expression *ConfigCompiler::Compile(void) %} %% -statements: statement sep +statements: newlines + | statement sep | statements statement sep ; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 71fd610ab..8d62ea2ea 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -85,6 +85,7 @@ add_boost_test(base base_value/convert base_value/format config_ops/simple + config_ops/advanced icinga_perfdata/simple icinga_perfdata/multiple icinga_perfdata/uom diff --git a/test/config-ops.cpp b/test/config-ops.cpp index a6c1da778..ae0ecd179 100644 --- a/test/config-ops.cpp +++ b/test/config-ops.cpp @@ -30,6 +30,18 @@ BOOST_AUTO_TEST_CASE(simple) Expression *expr; Dictionary::Ptr dict; + expr = ConfigCompiler::CompileText("", ""); + BOOST_CHECK(expr->Evaluate(frame) == Empty); + delete expr; + + expr = ConfigCompiler::CompileText("", "\n3"); + BOOST_CHECK(expr->Evaluate(frame) == 3); + delete expr; + + expr = ConfigCompiler::CompileText("", "{ 3\n\n5 }"); + BOOST_CHECK(expr->Evaluate(frame) != Empty); + delete expr; + expr = ConfigCompiler::CompileText("", "1 + 3"); BOOST_CHECK(expr->Evaluate(frame) == 4); delete expr; @@ -90,8 +102,8 @@ BOOST_AUTO_TEST_CASE(simple) BOOST_CHECK(expr->Evaluate(frame)); delete expr; - expr = ConfigCompiler::CompileText("", "!0 == true"); - BOOST_CHECK(expr->Evaluate(frame)); + expr = ConfigCompiler::CompileText("", "~0"); + BOOST_CHECK(expr->Evaluate(frame) == (double)~(long)0); delete expr; expr = ConfigCompiler::CompileText("", "4 << 8"); @@ -156,6 +168,53 @@ BOOST_AUTO_TEST_CASE(simple) BOOST_CHECK(dict->GetLength() == 1); BOOST_CHECK(dict->Get("a") == 3); + expr = ConfigCompiler::CompileText("", "test"); + BOOST_CHECK_THROW(expr->Evaluate(frame), ConfigError); + delete expr; +} + +BOOST_AUTO_TEST_CASE(advanced) +{ + VMFrame frame; + Expression *expr; + + expr = ConfigCompiler::CompileText("", "regex(\"^Hello\", \"Hello World\")"); + BOOST_CHECK(expr->Evaluate(frame)); + delete expr; + + expr = ConfigCompiler::CompileText("", "\"regex\"(\"^Hello\", \"Hello World\")"); + BOOST_CHECK(expr->Evaluate(frame)); + delete expr; + + expr = ConfigCompiler::CompileText("", "__boost_test()"); + BOOST_CHECK_THROW(expr->Evaluate(frame), ConfigError); + delete expr; + + Object::Ptr self = new Object(); + VMFrame frame2(self); + expr = ConfigCompiler::CompileText("", "this"); + BOOST_CHECK(expr->Evaluate(frame2) == Value(self)); + delete expr; + + expr = ConfigCompiler::CompileText("", "local v = 7; v"); + BOOST_CHECK(expr->Evaluate(frame)); + delete expr; + + expr = ConfigCompiler::CompileText("", "{ a = 3 }.a"); + BOOST_CHECK(expr->Evaluate(frame) == 3); + delete expr; + + expr = ConfigCompiler::CompileText("", "[ 2, 3 ][1]"); + BOOST_CHECK(expr->Evaluate(frame) == 3); + delete expr; + + /* Uncomment this once #7800 is fixed + expr = ConfigCompiler::CompileText("", "local v = { a = 3}; v.a"); + BOOST_CHECK(expr->Evaluate(frame) == 3); + delete expr;*/ + + expr = ConfigCompiler::CompileText("", "a = 3 b = 3"); + BOOST_CHECK(expr == NULL); } BOOST_AUTO_TEST_SUITE_END()