]> granicus.if.org Git - icinga2/commitdiff
Implement 'console' cli command using the API
authorMichael Friedrich <michael.friedrich@netways.de>
Mon, 2 Nov 2015 15:35:21 +0000 (16:35 +0100)
committerMichael Friedrich <michael.friedrich@netways.de>
Wed, 4 Nov 2015 15:01:06 +0000 (16:01 +0100)
fixes #10387

17 files changed:
icinga-studio/CMakeLists.txt
icinga-studio/mainform.cpp
icinga-studio/mainform.hpp
lib/base/serializer.cpp
lib/cli/consolecommand.cpp
lib/cli/consolecommand.hpp
lib/livestatus/livestatusquery.cpp
lib/livestatus/livestatusquery.hpp
lib/remote/CMakeLists.txt
lib/remote/apiclient.cpp [moved from icinga-studio/apiclient.cpp with 57% similarity]
lib/remote/apiclient.hpp [moved from icinga-studio/apiclient.hpp with 80% similarity]
lib/remote/consolehandler.cpp [new file with mode: 0644]
lib/remote/consolehandler.hpp [new file with mode: 0644]
lib/remote/httpresponse.cpp
lib/remote/url.cpp
lib/remote/url.hpp
test/remote-url.cpp

index d05076de6882847d418a668de5e9ee7a4e8c0dec..33f466e33085657be7e555e45882d4dade93f8c5 100644 (file)
@@ -27,7 +27,7 @@ endif()
 
 add_executable(icinga-studio MACOSX_BUNDLE WIN32 icinga-studio.cpp
   forms.cpp aboutform.cpp connectform.cpp mainform.cpp
-  icinga.icns apiclient.cpp ${WindowsSources})
+  icinga.icns ${WindowsSources})
 
 include_directories(${Boost_INCLUDE_DIRS})
 target_link_libraries(icinga-studio ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES} base remote)
index 5362e2423d381d30308aea2b2a75430d4628038a..0044402ddd060d8bf205f175ab4f22f824c1b9a8 100644 (file)
@@ -142,7 +142,6 @@ void MainForm::ObjectsCompletionHandler(boost::exception_ptr eptr, const std::ve
        m_PropertyGrid->Clear();
 
        if (eptr) {
-
                try {
                        boost::rethrow_exception(eptr);
                } catch (const std::exception& ex) {
index 2e244a71f250dba21042d68a020b00277de4b0b7..af7cd187383df45f88738ec2a424941b89ce8d0c 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef MAINFORM_H
 #define MAINFORM_H
 
-#include "icinga-studio/apiclient.hpp"
+#include "remote/apiclient.hpp"
 #include "remote/url.hpp"
 #include "base/exception.hpp"
 #include "icinga-studio/forms.h"
index 4f95ffb72a893d3fc9d5b7f757272c6eb2350651..bfcb90a5de79d925e5c041eab3e1b2858cec1ed5 100644 (file)
@@ -63,7 +63,7 @@ static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes)
        for (int i = 0; i < type->GetFieldCount(); i++) {
                Field field = type->GetFieldInfo(i);
 
-               if ((field.Attributes & attributeTypes) == 0)
+               if (attributeTypes != 0 && (field.Attributes & attributeTypes) == 0)
                        continue;
 
                fields->Set(field.Name, Serialize(input->GetField(i), attributeTypes));
index 801c5f8eead245dba08be1f136aa41522e43d51a..6fb0bd25ccd5fab9e69c266790d6fe873eb5e41a 100644 (file)
 
 #include "cli/consolecommand.hpp"
 #include "config/configcompiler.hpp"
+#include "remote/apiclient.hpp"
+#include "remote/consolehandler.hpp"
+#include "remote/url.hpp"
 #include "base/configwriter.hpp"
+#include "base/serializer.hpp"
 #include "base/json.hpp"
 #include "base/console.hpp"
 #include "base/application.hpp"
@@ -37,6 +41,8 @@ using namespace icinga;
 namespace po = boost::program_options;
 
 static ScriptFrame *l_ScriptFrame;
+static ApiClient::Ptr l_ApiClient;
+static String l_Session;
 
 REGISTER_CLICOMMAND("console", ConsoleCommand);
 
@@ -60,89 +66,41 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
 {
        visibleDesc.add_options()
                ("connect,c", po::value<std::string>(), "connect to an Icinga 2 instance")
+               ("eval,e", po::value<std::string>(), "evaluate expression and terminate")
                ("sandbox", "enable sandbox mode")
        ;
 }
 
 #ifdef HAVE_EDITLINE
-static void AddSuggestion(std::vector<String>& matches, const String& word, const String& suggestion)
-{
-       if (suggestion.Find(word) != 0)
-               return;
-
-       matches.push_back(suggestion);
-}
-
-static char *ConsoleCompleteHelper(const char *word, int state)
+char *ConsoleCommand::ConsoleCompleteHelper(const char *word, int state)
 {
        static std::vector<String> matches;
-       String aword = word;
 
        if (state == 0) {
-               matches.clear();
-
-               BOOST_FOREACH(const String& keyword, ConfigWriter::GetKeywords()) {
-                       AddSuggestion(matches, word, keyword);
-               }
-
-               {
-                       ObjectLock olock(l_ScriptFrame->Locals);
-                       BOOST_FOREACH(const Dictionary::Pair& kv, l_ScriptFrame->Locals) {
-                               AddSuggestion(matches, word, kv.first);
-                       }
-               }
-
-               {
-                       ObjectLock olock(ScriptGlobal::GetGlobals());
-                       BOOST_FOREACH(const Dictionary::Pair& kv, ScriptGlobal::GetGlobals()) {
-                               AddSuggestion(matches, word, kv.first);
+               if (!l_ApiClient)
+                       matches = ConsoleHandler::GetAutocompletionSuggestions(word, *l_ScriptFrame); 
+               else {
+                       boost::mutex mutex;
+                       boost::condition_variable cv;
+                       bool ready = false;
+                       Array::Ptr suggestions;
+
+                       l_ApiClient->AutocompleteScript(l_Session, word, l_ScriptFrame->Sandboxed,
+                           boost::bind(&ConsoleCommand::AutocompleteScriptCompletionHandler,
+                           boost::ref(mutex), boost::ref(cv), boost::ref(ready),
+                           _1, _2,
+                           boost::ref(suggestions)));
+
+                       {
+                               boost::mutex::scoped_lock lock(mutex);
+                               while (!ready)
+                                       cv.wait(lock);
                        }
-               }
-
-               String::SizeType cperiod = aword.RFind(".");
-
-               if (cperiod != -1) {
-                       String pword = aword.SubStr(0, cperiod);
-
-                       Value value;
-
-                       try {
-                               Expression *expr = ConfigCompiler::CompileText("temp", pword);
-
-                               if (expr)
-                                       value = expr->Evaluate(*l_ScriptFrame);
-
-                               if (value.IsObjectType<Dictionary>()) {
-                                       Dictionary::Ptr dict = value;
-
-                                       ObjectLock olock(dict);
-                                       BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
-                                               AddSuggestion(matches, word, pword + "." + kv.first);
-                                       }
-                               }
-
-                               Type::Ptr type = value.GetReflectionType();
 
-                               for (int i = 0; i < type->GetFieldCount(); i++) {
-                                       Field field = type->GetFieldInfo(i);
-
-                                       AddSuggestion(matches, word, pword + "." + field.Name);
-                               }
+                       matches.clear();
 
-                               while (type) {
-                                       Object::Ptr prototype = type->GetPrototype();
-                                       Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
-
-                                       if (dict) {
-                                               ObjectLock olock(dict);
-                                               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
-                                                       AddSuggestion(matches, word, pword + "." + kv.first);
-                                               }
-                                       }
-
-                                       type = type->GetBaseType();
-                               }
-                       } catch (...) { /* Ignore the exception */ }
+                       ObjectLock olock(suggestions);
+                       std::copy(suggestions->Begin(), suggestions->End(), std::back_inserter(matches));
                }
        }
 
@@ -164,30 +122,53 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
        int next_line = 1;
 
 #ifdef HAVE_EDITLINE
-       rl_completion_entry_function = ConsoleCompleteHelper;
+       rl_completion_entry_function = ConsoleCommand::ConsoleCompleteHelper;
        rl_completion_append_character = '\0';
 #endif /* HAVE_EDITLINE */
 
-       String addr, session;
+       String addr;
        ScriptFrame scriptFrame;
 
        l_ScriptFrame = &scriptFrame;
+       l_Session = Utility::NewUniqueID();
+
+       if (vm.count("sandbox"))
+               scriptFrame.Sandboxed = true;
+
+       if (!vm.count("eval"))
+               std::cout << "Icinga 2 (version: " << Application::GetAppVersion() << ")\n";
+
+       const char *addrEnv = getenv("ICINGA2_API_URL");
+       if (addrEnv)
+               addr = addrEnv;
 
        if (vm.count("connect")) {
                addr = vm["connect"].as<std::string>();
-               session = Utility::NewUniqueID();
        }
 
-       if (vm.count("sandbox")) {
-               if (vm.count("connect")) {
-                       Log(LogCritical, "ConsoleCommand", "Sandbox mode cannot be used together with --connect.");
+       if (!addr.IsEmpty()) {
+               Url::Ptr url;
+
+               try {
+                       url = new Url(addr);
+               } catch (const std::exception& ex) {
+                       Log(LogCritical, "ConsoleCommand", ex.what());
                        return EXIT_FAILURE;
                }
 
-               scriptFrame.Sandboxed = true;
-       }
+               const char *usernameEnv = getenv("ICINGA2_API_USERNAME");
+               const char *passwordEnv = getenv("ICINGA2_API_PASSWORD");
 
-       std::cout << "Icinga (version: " << Application::GetAppVersion() << ")\n";
+               if (usernameEnv)
+                       url->SetUsername(usernameEnv);
+               if (passwordEnv)
+                       url->SetPassword(passwordEnv);
+
+               if (url->GetPort().IsEmpty())
+                       url->SetPort("5665");
+
+               l_ApiClient = new ApiClient(url->GetHost(), url->GetPort(), url->GetUsername(), url->GetPassword());
+       }
 
        while (std::cin.good()) {
                String fileName = "<" + Convert::ToString(next_line) + ">";
@@ -197,148 +178,191 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
                std::string command;
 
 incomplete:
+               std::string line;
+
+               if (!vm.count("eval")) {
 #ifdef HAVE_EDITLINE
-               std::ostringstream promptbuf;
-               std::ostream& os = promptbuf;
+                       std::ostringstream promptbuf;
+                       std::ostream& os = promptbuf;
 #else /* HAVE_EDITLINE */
-               std::ostream& os = std::cout;
+                       std::ostream& os = std::cout;
 #endif /* HAVE_EDITLINE */
 
-               os << fileName;
+                       os << fileName;
 
-               if (!continuation)
-                       os << " => ";
-               else
-                       os << " .. ";
+                       if (!continuation)
+                               os << " => ";
+                       else
+                               os << " .. ";
 
 #ifdef HAVE_EDITLINE
-               String prompt = promptbuf.str();
+                       String prompt = promptbuf.str();
 
-               char *cline;
-               cline = readline(prompt.CStr());
+                       char *cline;
+                       cline = readline(prompt.CStr());
 
-               if (!cline)
-                       break;
+                       if (!cline)
+                               break;
 
-               add_history(cline);
+                       add_history(cline);
 
-               std::string line = cline;
+                       line = cline;
 
-               free(cline);
+                       free(cline);
 #else /* HAVE_EDITLINE */
-               std::string line;
-               std::getline(std::cin, line);
+                       std::getline(std::cin, line);
 #endif /* HAVE_EDITLINE */
+               } else
+                       line = vm["eval"].as<std::string>();
 
                if (!command.empty())
                        command += "\n";
 
                command += line;
 
-               if (addr.IsEmpty()) {
-                       Expression *expr = NULL;
+               Expression *expr = NULL;
 
-                       try {
-                               lines[fileName] = command;
+               try {
+                       lines[fileName] = command;
 
-                               expr = ConfigCompiler::CompileText(fileName, command);
+                       Value result;
 
-                               if (expr) {
-                                       Value result = expr->Evaluate(scriptFrame);
-                                       std::cout << ConsoleColorTag(Console_ForegroundCyan);
-                                       if (!result.IsObject() || result.IsObjectType<Array>() || result.IsObjectType<Dictionary>())
-                                               std::cout << JsonEncode(result);
-                                       else
-                                               std::cout << result;
-                                       std::cout << ConsoleColorTag(Console_Normal) << "\n";
-                               }
-                       } catch (const ScriptError& ex) {
-                               if (ex.IsIncompleteExpression()) {
-                                       continuation = true;
-                                       goto incomplete;
+                       if (!l_ApiClient) {
+                               expr = ConfigCompiler::CompileText(fileName, command);
+                               result = Serialize(expr->Evaluate(scriptFrame), 0);
+                       } else {
+                               boost::mutex mutex;
+                               boost::condition_variable cv;
+                               bool ready = false;
+                               boost::exception_ptr eptr;
+
+                               l_ApiClient->ExecuteScript(l_Session, command, scriptFrame.Sandboxed,
+                                   boost::bind(&ConsoleCommand::ExecuteScriptCompletionHandler,
+                                   boost::ref(mutex), boost::ref(cv), boost::ref(ready),
+                                   _1, _2,
+                                   boost::ref(result), boost::ref(eptr)));
+
+                               {
+                                       boost::mutex::scoped_lock lock(mutex);
+                                       while (!ready)
+                                               cv.wait(lock);
                                }
 
-                               DebugInfo di = ex.GetDebugInfo();
+                               if (eptr)
+                                       boost::rethrow_exception(eptr);
+                       }
 
-                               if (lines.find(di.Path) != lines.end()) {
-                                       String text = lines[di.Path];
+                       if (!vm.count("eval")) {
+                               std::cout << ConsoleColorTag(Console_ForegroundCyan);
+                               ConfigWriter::EmitValue(std::cout, 1, result);
+                               std::cout << ConsoleColorTag(Console_Normal) << "\n";
+                       } else {
+                               std::cout << JsonEncode(result) << "\n";
+                               break;
+                       }
+               } catch (const ScriptError& ex) {
+                       if (ex.IsIncompleteExpression()) {
+                               continuation = true;
+                               goto incomplete;
+                       }
 
-                                       std::vector<String> ulines;
-                                       boost::algorithm::split(ulines, text, boost::is_any_of("\n"));
+                       DebugInfo di = ex.GetDebugInfo();
 
-                                       for (int i = 1; i <= ulines.size(); i++) {
-                                               int start, len;
+                       if (lines.find(di.Path) != lines.end()) {
+                               String text = lines[di.Path];
 
-                                               if (i == di.FirstLine)
-                                                       start = di.FirstColumn;
-                                               else
-                                                       start = 0;
+                               std::vector<String> ulines;
+                               boost::algorithm::split(ulines, text, boost::is_any_of("\n"));
 
-                                               if (i == di.LastLine)
-                                                       len = di.LastColumn - di.FirstColumn + 1;
-                                               else
-                                                       len = ulines[i - 1].GetLength();
+                               for (int i = 1; i <= ulines.size(); i++) {
+                                       int start, len;
 
-                                               int offset;
+                                       if (i == di.FirstLine)
+                                               start = di.FirstColumn;
+                                       else
+                                               start = 0;
 
-                                               if (di.Path != fileName) {
-                                                       std::cout << di.Path << ": " << ulines[i - 1] << "\n";
-                                                       offset = 2;
-                                               } else
-                                                       offset = 4;
+                                       if (i == di.LastLine)
+                                               len = di.LastColumn - di.FirstColumn + 1;
+                                       else
+                                               len = ulines[i - 1].GetLength();
+
+                                       int offset;
 
-                                               if (i >= di.FirstLine && i <= di.LastLine) {
-                                                       std::cout << String(di.Path.GetLength() + offset, ' ');
-                                                       std::cout << String(start, ' ') << String(len, '^') << "\n";
-                                               }
+                                       if (di.Path != fileName) {
+                                               std::cout << di.Path << ": " << ulines[i - 1] << "\n";
+                                               offset = 2;
+                                       } else
+                                               offset = 4;
+
+                                       if (i >= di.FirstLine && i <= di.LastLine) {
+                                               std::cout << String(di.Path.GetLength() + offset, ' ');
+                                               std::cout << String(start, ' ') << String(len, '^') << "\n";
                                        }
-                               } else {
-                                       ShowCodeFragment(std::cout, di);
                                }
-
-                               std::cout << ex.what() << "\n";
-                       } catch (const std::exception& ex) {
-                               std::cout << "Error: " << DiagnosticInformation(ex) << "\n";
+                       } else {
+                               ShowCodeFragment(std::cout, di);
                        }
 
-                       delete expr;
-               } else {
-                       Socket::Ptr socket;
+                       std::cout << ex.what() << "\n";
 
-#ifndef _WIN32
-                       if (addr.FindFirstOf("/") != String::NPos) {
-                               UnixSocket::Ptr usocket = new UnixSocket();
-                               usocket->Connect(addr);
-                               socket = usocket;
-                       } else {
-#endif /* _WIN32 */
-                               Log(LogCritical, "ConsoleCommand", "Sorry, TCP sockets aren't supported yet.");
-                               return 1;
-#ifndef _WIN32
-                       }
-#endif /* _WIN32 */
+                       if (vm.count("eval"))
+                               return EXIT_FAILURE;
+               } catch (const std::exception& ex) {
+                       std::cout << "Error: " << DiagnosticInformation(ex) << "\n";
 
-                       String query = "SCRIPT " + session + "\n" + line + "\n\n";
+                       if (vm.count("eval"))
+                               return EXIT_FAILURE;
+               }
 
-                       NetworkStream::Ptr ns = new NetworkStream(socket);
-                       ns->Write(query.CStr(), query.GetLength());
+               delete expr;
+       }
 
-                       String result;
-                       char buf[1024];
+       return EXIT_SUCCESS;
+}
 
-                       while (!ns->IsEof()) {
-                               size_t rc = ns->Read(buf, sizeof(buf), true);
-                               result += String(buf, buf + rc);
-                       }
+void ConsoleCommand::ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
+    bool& ready, boost::exception_ptr eptr, const Value& result, Value& resultOut, boost::exception_ptr& eptrOut)
+{
+       if (eptr) {
+               try {
+                       boost::rethrow_exception(eptr);
+               } catch (const ScriptError& ex) {
+                       eptrOut = boost::current_exception();
+               } catch (const std::exception& ex) {
+                       Log(LogCritical, "ConsoleCommand")
+                           << "HTTP query failed: " << ex.what();
+                       Application::Exit(EXIT_FAILURE);
+               }
+       }
 
-                       if (result.GetLength() < 16) {
-                               Log(LogCritical, "ConsoleCommand", "Received invalid response from Livestatus.");
-                               continue;
-                       }
+       resultOut = result;
+
+       {
+               boost::mutex::scoped_lock lock(mutex);
+               ready = true;
+               cv.notify_all();
+       }
+}
 
-                       std::cout << result.SubStr(16) << "\n";
+void ConsoleCommand::AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
+    bool& ready, boost::exception_ptr eptr, const Array::Ptr& result, Array::Ptr& resultOut)
+{
+       if (eptr) {
+               try {
+                       boost::rethrow_exception(eptr);
+               } catch (const std::exception& ex) {
+                       Log(LogCritical, "ConsoleCommand")
+                           << "HTTP query failed: " << ex.what();
+                       Application::Exit(EXIT_FAILURE);
                }
        }
 
-       return 0;
+       resultOut = result;
+
+       {
+               boost::mutex::scoped_lock lock(mutex);
+               ready = true;
+               cv.notify_all();
+       }
 }
index b5795b964e2399841a4740e21b7737378fa5f0b9..f144e9a042e5ad5b3698f48f6213c29b1bdd077e 100644 (file)
@@ -21,6 +21,7 @@
 #define CONSOLECOMMAND_H
 
 #include "cli/clicommand.hpp"
+#include "base/exception.hpp"
 
 namespace icinga
 {
@@ -41,6 +42,22 @@ public:
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
            boost::program_options::options_description& hiddenDesc) const override;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
+
+private:
+       mutable boost::mutex m_Mutex;
+       mutable boost::condition_variable m_CV;
+       mutable bool m_CommandReady;
+
+       static void ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
+           bool& ready, boost::exception_ptr eptr, const Value& result, Value& resultOut,
+           boost::exception_ptr& eptrOut);
+       static void AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
+           bool& ready, boost::exception_ptr eptr, const Array::Ptr& result, Array::Ptr& resultOut);
+
+#ifdef HAVE_EDITLINE
+       static char *ConsoleCompleteHelper(const char *word, int state);
+#endif /* HAVE_EDITLINE */
+
 };
 
 }
index c216da6bc3fcb220a39c1fc517efec6712df644c..0e048d7151b9f5e46d0359f48629f05d89793d26 100644 (file)
@@ -31,7 +31,6 @@
 #include "livestatus/orfilter.hpp"
 #include "livestatus/andfilter.hpp"
 #include "icinga/externalcommandprocessor.hpp"
-#include "config/configcompiler.hpp"
 #include "base/debug.hpp"
 #include "base/convert.hpp"
 #include "base/objectlock.hpp"
@@ -52,36 +51,6 @@ using namespace icinga;
 
 static int l_ExternalCommands = 0;
 static boost::mutex l_QueryMutex;
-static std::map<String, LivestatusScriptFrame> l_LivestatusScriptFrames;
-static Timer::Ptr l_FrameCleanupTimer;
-static boost::mutex l_LivestatusScriptMutex;
-
-static void ScriptFrameCleanupHandler(void)
-{
-       boost::mutex::scoped_lock lock(l_LivestatusScriptMutex);
-
-       std::vector<String> cleanup_keys;
-
-       typedef std::pair<String, LivestatusScriptFrame> KVPair;
-
-       BOOST_FOREACH(const KVPair& kv, l_LivestatusScriptFrames) {
-               if (kv.second.Seen < Utility::GetTime() - 1800)
-                       cleanup_keys.push_back(kv.first);
-       }
-
-       BOOST_FOREACH(const String& key, cleanup_keys)
-               l_LivestatusScriptFrames.erase(key);
-}
-
-static void InitScriptFrameCleanup(void)
-{
-       l_FrameCleanupTimer = new Timer();
-       l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
-       l_FrameCleanupTimer->SetInterval(30);
-       l_FrameCleanupTimer->Start();
-}
-
-INITIALIZE_ONCE(InitScriptFrameCleanup);
 
 LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String& compat_log_path)
        : m_KeepAlive(false), m_OutputFormat("csv"), m_ColumnHeaders(true), m_Limit(-1), m_ErrorCode(0),
@@ -123,16 +92,6 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        if (m_Verb == "COMMAND") {
                m_KeepAlive = true;
                m_Command = target;
-       } else if (m_Verb == "SCRIPT") {
-               m_Session = target;
-
-               for (unsigned int i = 1; i < lines.size(); i++) {
-                       if (m_Command != "")
-                               m_Command += "\n";
-                       m_Command += lines[i];
-               }
-
-               return;
        } else if (m_Verb == "GET") {
                m_Table = target;
        } else {
@@ -625,54 +584,6 @@ void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream)
        SendResponse(stream, LivestatusErrorOK, "");
 }
 
-void LivestatusQuery::ExecuteScriptHelper(const Stream::Ptr& stream)
-{
-       Log(LogInformation, "LivestatusQuery")
-           << "Executing expression: " << m_Command;
-
-       m_ResponseHeader = "fixed16";
-
-       LivestatusScriptFrame& lsf = l_LivestatusScriptFrames[m_Session];
-       lsf.Seen = Utility::GetTime();
-
-       if (!lsf.Locals)
-               lsf.Locals = new Dictionary();
-
-       String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
-       lsf.NextLine++;
-
-       lsf.Lines[fileName] = m_Command;
-
-       Expression *expr = NULL;
-       Value result;
-       try {
-               expr = ConfigCompiler::CompileText(fileName, m_Command);
-               ScriptFrame frame;
-               frame.Locals = lsf.Locals;
-               frame.Self = lsf.Locals;
-               result = expr->Evaluate(frame);
-       } catch (const ScriptError& ex) {
-               delete expr;
-
-               DebugInfo di = ex.GetDebugInfo();
-
-               std::ostringstream msgbuf;
-
-               msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
-                   << String(di.Path.GetLength() + 2, ' ')
-                   << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
-                   << ex.what() << "\n";
-
-               SendResponse(stream, LivestatusErrorQuery, msgbuf.str());
-               return;
-       } catch (...) {
-               delete expr;
-               throw;
-       }
-       delete expr;
-       SendResponse(stream, LivestatusErrorOK, JsonEncode(Serialize(result, FAEphemeral | FAState | FAConfig), true));
-}
-
 void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream)
 {
        Log(LogDebug, "LivestatusQuery")
@@ -720,8 +631,6 @@ bool LivestatusQuery::Execute(const Stream::Ptr& stream)
                        ExecuteGetHelper(stream);
                else if (m_Verb == "COMMAND")
                        ExecuteCommandHelper(stream);
-               else if (m_Verb == "SCRIPT")
-                       ExecuteScriptHelper(stream);
                else if (m_Verb == "ERROR")
                        ExecuteErrorHelper(stream);
                else
index 822bde239d0d85cbee91e57b564c9286fd56909b..7ad31760a96acc9142864671abb361a94e69fa89 100644 (file)
@@ -40,18 +40,6 @@ enum LivestatusError
        LivestatusErrorQuery = 452
 };
 
-struct LivestatusScriptFrame
-{
-       double Seen;
-       int NextLine;
-       std::map<String, String> Lines;
-       Dictionary::Ptr Locals;
-
-       LivestatusScriptFrame(void)
-               : Seen(0), NextLine(1)
-       { }
-};
-
 /**
  * @ingroup livestatus
  */
@@ -92,7 +80,7 @@ private:
        /* Parameters for invalid queries. */
        int m_ErrorCode;
        String m_ErrorMessage;
-       
+
        unsigned long m_LogTimeFrom;
        unsigned long m_LogTimeUntil;
        String m_CompatLogPath;
@@ -106,12 +94,11 @@ private:
 
        void ExecuteGetHelper(const Stream::Ptr& stream);
        void ExecuteCommandHelper(const Stream::Ptr& stream);
-       void ExecuteScriptHelper(const Stream::Ptr& stream);
        void ExecuteErrorHelper(const Stream::Ptr& stream);
 
        void SendResponse(const Stream::Ptr& stream, int code, const String& data);
        void PrintFixed16(const Stream::Ptr& stream, int code, const String& data);
-       
+
        static Filter::Ptr ParseFilter(const String& params, unsigned long& from, unsigned long& until);
 };
 
index 11243f782f5f19ef0db82651a02a91c07146d90c..e0328dabcfbcbc939913354a6683d1cd627974b3 100644 (file)
@@ -21,10 +21,10 @@ mkclass_target(endpoint.ti endpoint.tcpp endpoint.thpp)
 mkclass_target(zone.ti zone.tcpp zone.thpp)
 
 set(remote_SOURCES
-  actionshandler.cpp apiaction.cpp
+  actionshandler.cpp apiaction.cpp apiclient.cpp
   apifunction.cpp apilistener.cpp apilistener.thpp apilistener-configsync.cpp
   apilistener-filesync.cpp apiuser.cpp apiuser.thpp authority.cpp base64.cpp
-  configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
+  consolehandler.cpp configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
   configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
   endpoint.cpp endpoint.thpp eventshandler.cpp eventqueue.cpp filterutility.cpp
   httpchunkedencoding.cpp httpclientconnection.cpp httpserverconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
similarity index 57%
rename from icinga-studio/apiclient.cpp
rename to lib/remote/apiclient.cpp
index 2a232123852c4277dddc34e03eb7fc12149b6c0f..11dd39e97f5d8275b3f6b7a8af38b1f27d3ec7f7 100644 (file)
@@ -17,7 +17,7 @@
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "icinga-studio/apiclient.hpp"
+#include "remote/apiclient.hpp"
 #include "remote/base64.hpp"
 #include "base/json.hpp"
 #include "base/logger.hpp"
@@ -116,7 +116,6 @@ void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCall
        path.push_back("objects");
        path.push_back(pluralType);
        url->SetPath(path);
-       String qp;
 
        std::map<String, std::vector<String> > params;
 
@@ -206,3 +205,159 @@ void ApiClient::ObjectsHttpCompletionCallback(HttpRequest& request,
                callback(boost::current_exception(), std::vector<ApiObject::Ptr>());
        }
 }
+
+void ApiClient::ExecuteScript(const String& session, const String& command, bool sandboxed,
+    const ExecuteScriptCompletionCallback& callback) const
+{
+       Url::Ptr url = new Url();
+       url->SetScheme("https");
+       url->SetHost(m_Connection->GetHost());
+       url->SetPort(m_Connection->GetPort());
+
+       std::vector<String> path;
+       path.push_back("v1");
+       path.push_back("console");
+       path.push_back("execute-script");
+       url->SetPath(path);
+
+       std::map<String, std::vector<String> > params;
+       params["session"].push_back(session);
+       params["command"].push_back(command);
+       params["sandboxed"].push_back(sandboxed ? "1" : "0");
+       url->SetQuery(params);
+
+       try {
+               boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
+               req->RequestMethod = "POST";
+               req->RequestUrl = url;
+               req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
+               m_Connection->SubmitRequest(req, boost::bind(ExecuteScriptHttpCompletionCallback, _1, _2, callback));
+       } catch (const std::exception& ex) {
+               callback(boost::current_exception(), Empty);
+       }
+}
+
+void ApiClient::ExecuteScriptHttpCompletionCallback(HttpRequest& request,
+    HttpResponse& response, const ExecuteScriptCompletionCallback& callback)
+{
+       Dictionary::Ptr result;
+
+       String body;
+       char buffer[1024];
+       size_t count;
+
+       while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
+               body += String(buffer, buffer + count);
+
+       try {
+               if (response.StatusCode < 200 || response.StatusCode > 299) {
+                       std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body;
+
+                       BOOST_THROW_EXCEPTION(ScriptError(message));
+               }
+
+               result = JsonDecode(body);
+
+               Array::Ptr results = result->Get("results");
+               Value result;
+               bool incompleteExpression = false;
+               String errorMessage = "Unexpected result from API.";
+
+               if (results && results->GetLength() > 0) {
+                       Dictionary::Ptr resultInfo = results->Get(0);
+                       errorMessage = resultInfo->Get("status");
+
+                       if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) {
+                               result = resultInfo->Get("result");
+                       } else {
+                               DebugInfo di;
+                               Dictionary::Ptr debugInfo = resultInfo->Get("debug_info");
+                               if (debugInfo) {
+                                       di.Path = debugInfo->Get("path");
+                                       di.FirstLine = debugInfo->Get("first_line");
+                                       di.FirstColumn = debugInfo->Get("first_column");
+                                       di.LastLine = debugInfo->Get("last_line");
+                                       di.LastColumn = debugInfo->Get("last_column");
+                               }
+                               bool incompleteExpression = resultInfo->Get("incomplete_expression");
+                               BOOST_THROW_EXCEPTION(ScriptError(errorMessage, di, incompleteExpression));
+                       }
+               }
+
+               callback(boost::exception_ptr(), result);
+       } catch (const std::exception& ex) {
+               callback(boost::current_exception(), Empty);
+       }
+}
+
+void ApiClient::AutocompleteScript(const String& session, const String& command, bool sandboxed,
+    const AutocompleteScriptCompletionCallback& callback) const
+{
+       Url::Ptr url = new Url();
+       url->SetScheme("https");
+       url->SetHost(m_Connection->GetHost());
+       url->SetPort(m_Connection->GetPort());
+
+       std::vector<String> path;
+       path.push_back("v1");
+       path.push_back("console");
+       path.push_back("auto-complete-script");
+       url->SetPath(path);
+
+       std::map<String, std::vector<String> > params;
+       params["session"].push_back(session);
+       params["command"].push_back(command);
+       params["sandboxed"].push_back(sandboxed ? "1" : "0");
+       url->SetQuery(params);
+
+       try {
+               boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
+               req->RequestMethod = "POST";
+               req->RequestUrl = url;
+               req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
+               m_Connection->SubmitRequest(req, boost::bind(AutocompleteScriptHttpCompletionCallback, _1, _2, callback));
+       } catch (const std::exception& ex) {
+               callback(boost::current_exception(), Array::Ptr());
+       }
+}
+
+void ApiClient::AutocompleteScriptHttpCompletionCallback(HttpRequest& request,
+    HttpResponse& response, const AutocompleteScriptCompletionCallback& callback)
+{
+       Dictionary::Ptr result;
+
+       String body;
+       char buffer[1024];
+       size_t count;
+
+       while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
+               body += String(buffer, buffer + count);
+
+       try {
+               if (response.StatusCode < 200 || response.StatusCode > 299) {
+                       std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body;
+
+                       BOOST_THROW_EXCEPTION(ScriptError(message));
+               }
+
+               result = JsonDecode(body);
+
+               Array::Ptr results = result->Get("results");
+               Array::Ptr suggestions;
+               String errorMessage = "Unexpected result from API.";
+
+               if (results && results->GetLength() > 0) {
+                       Dictionary::Ptr resultInfo = results->Get(0);
+                       errorMessage = resultInfo->Get("status");
+
+                       if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299)
+                               suggestions = resultInfo->Get("suggestions");
+                       else
+                               BOOST_THROW_EXCEPTION(ScriptError(errorMessage));
+               }
+
+               callback(boost::exception_ptr(), suggestions);
+       } catch (const std::exception& ex) {
+               callback(boost::current_exception(), Array::Ptr());
+       }
+}
similarity index 80%
rename from icinga-studio/apiclient.hpp
rename to lib/remote/apiclient.hpp
index a4ab2283c3e3de8eb47c3aa0681fc1d914ac62ea..273714bcbfab0b5c756e8cad58772e41c7b5b2a8 100644 (file)
@@ -96,6 +96,14 @@ public:
            const std::vector<String>& names = std::vector<String>(),
            const std::vector<String>& attrs = std::vector<String>()) const;
 
+       typedef boost::function<void(boost::exception_ptr, const Value&)> ExecuteScriptCompletionCallback;
+       void ExecuteScript(const String& session, const String& command, bool sandboxed,
+           const ExecuteScriptCompletionCallback& callback) const;
+       
+       typedef boost::function<void(boost::exception_ptr, const Array::Ptr&)> AutocompleteScriptCompletionCallback;
+       void AutocompleteScript(const String& session, const String& command, bool sandboxed,
+           const AutocompleteScriptCompletionCallback& callback) const;
+
 private:
        HttpClientConnection::Ptr m_Connection;
        String m_User;
@@ -105,6 +113,10 @@ private:
            HttpResponse& response, const TypesCompletionCallback& callback);
        static void ObjectsHttpCompletionCallback(HttpRequest& request,
            HttpResponse& response, const ObjectsCompletionCallback& callback);
+       static void ExecuteScriptHttpCompletionCallback(HttpRequest& request,
+           HttpResponse& response, const ExecuteScriptCompletionCallback& callback);
+       static void AutocompleteScriptHttpCompletionCallback(HttpRequest& request,
+           HttpResponse& response, const AutocompleteScriptCompletionCallback& callback);
 };
 
 }
diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp
new file mode 100644 (file)
index 0000000..5169fef
--- /dev/null
@@ -0,0 +1,290 @@
+/******************************************************************************
+ * 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 "remote/consolehandler.hpp"
+#include "remote/httputility.hpp"
+#include "remote/filterutility.hpp"
+#include "config/configcompiler.hpp"
+#include "base/configtype.hpp"
+#include "base/configwriter.hpp"
+#include "base/scriptglobal.hpp"
+#include "base/logger.hpp"
+#include "base/serializer.hpp"
+#include "base/timer.hpp"
+#include "base/initialize.hpp"
+#include <boost/algorithm/string.hpp>
+#include <set>
+
+using namespace icinga;
+
+REGISTER_URLHANDLER("/v1/console", ConsoleHandler);
+
+static int l_ExternalCommands = 0;
+static boost::mutex l_QueryMutex;
+static std::map<String, ApiScriptFrame> l_ApiScriptFrames;
+static Timer::Ptr l_FrameCleanupTimer;
+static boost::mutex l_ApiScriptMutex;
+
+static void ScriptFrameCleanupHandler(void)
+{
+       boost::mutex::scoped_lock lock(l_ApiScriptMutex);
+
+       std::vector<String> cleanup_keys;
+
+       typedef std::pair<String, ApiScriptFrame> KVPair;
+
+       BOOST_FOREACH(const KVPair& kv, l_ApiScriptFrames) {
+               if (kv.second.Seen < Utility::GetTime() - 1800)
+                       cleanup_keys.push_back(kv.first);
+       }
+
+       BOOST_FOREACH(const String& key, cleanup_keys)
+               l_ApiScriptFrames.erase(key);
+}
+
+static void InitScriptFrameCleanup(void)
+{
+       l_FrameCleanupTimer = new Timer();
+       l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
+       l_FrameCleanupTimer->SetInterval(30);
+       l_FrameCleanupTimer->Start();
+}
+
+INITIALIZE_ONCE(InitScriptFrameCleanup);
+
+bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+{
+       if (request.RequestUrl->GetPath().size() > 3)
+               return false;
+
+       if (request.RequestMethod != "POST")
+               return false;
+
+       QueryDescription qd;
+       Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
+
+       String methodName = request.RequestUrl->GetPath()[2];
+       
+       String permission = "console/" + methodName;
+       FilterUtility::CheckPermission(user, permission);
+
+       String session = HttpUtility::GetLastParameter(params, "session");
+
+       if (session.IsEmpty())
+               session = Utility::NewUniqueID();
+
+       String command = HttpUtility::GetLastParameter(params, "command");
+
+       bool sandboxed = HttpUtility::GetLastParameter(params, "sandboxed");
+
+       if (methodName == "execute-script") {
+               return ExecuteScriptHelper(request, response, command, session, sandboxed);
+       } else if (methodName == "auto-complete-script") {
+               return AutocompleteScriptHelper(request, response, command, session, sandboxed);
+       }
+
+       return true;
+}
+
+bool ConsoleHandler::ExecuteScriptHelper(HttpRequest& request, HttpResponse& response,
+    const String& command, const String& session, bool sandboxed)
+{
+       Log(LogInformation, "Console")
+           << "Executing expression: " << command;
+
+       ApiScriptFrame& lsf = l_ApiScriptFrames[session];
+       lsf.Seen = Utility::GetTime();
+
+       if (!lsf.Locals)
+               lsf.Locals = new Dictionary();
+
+       String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
+       lsf.NextLine++;
+
+       lsf.Lines[fileName] = command;
+
+       Array::Ptr results = new Array();
+       Dictionary::Ptr resultInfo = new Dictionary();
+       Expression *expr = NULL;
+       Value exprResult;
+
+       try {
+               expr = ConfigCompiler::CompileText(fileName, command);
+
+               ScriptFrame frame;
+               frame.Locals = lsf.Locals;
+               frame.Self = lsf.Locals;
+               frame.Sandboxed = sandboxed;
+
+               exprResult = expr->Evaluate(frame);
+
+               resultInfo->Set("code", 200);
+               resultInfo->Set("status", "Executed successfully.");
+               resultInfo->Set("result", Serialize(exprResult, 0));
+       } catch (const ScriptError& ex) {
+               DebugInfo di = ex.GetDebugInfo();
+
+               std::ostringstream msgbuf;
+
+               msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
+                   << String(di.Path.GetLength() + 2, ' ')
+                   << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
+                   << ex.what() << "\n";
+
+               resultInfo->Set("code", 500);
+               resultInfo->Set("status", String(msgbuf.str()));
+               resultInfo->Set("incomplete_expression", ex.IsIncompleteExpression());
+
+               Dictionary::Ptr debugInfo = new Dictionary();
+               debugInfo->Set("path", di.Path);
+               debugInfo->Set("first_line", di.FirstLine);
+               debugInfo->Set("first_column", di.FirstColumn);
+               debugInfo->Set("last_line", di.LastLine);
+               debugInfo->Set("last_column", di.LastColumn);
+               resultInfo->Set("debug_info", debugInfo);
+       } catch (...) {
+               delete expr;
+               throw;
+       }
+       delete expr;
+
+       results->Add(resultInfo);
+
+       Dictionary::Ptr result = new Dictionary();
+       result->Set("results", results);
+
+       response.SetStatus(200, "OK");
+       HttpUtility::SendJsonBody(response, result);
+
+       return true;
+}
+
+bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response,
+    const String& command, const String& session, bool sandboxed)
+{
+       Log(LogInformation, "Console")
+           << "Auto-completing expression: " << command;
+
+       ApiScriptFrame& lsf = l_ApiScriptFrames[session];
+       lsf.Seen = Utility::GetTime();
+
+       if (!lsf.Locals)
+               lsf.Locals = new Dictionary();
+
+       Array::Ptr results = new Array();
+       Dictionary::Ptr resultInfo = new Dictionary();
+
+       ScriptFrame frame;
+       frame.Locals = lsf.Locals;
+       frame.Self = lsf.Locals;
+       frame.Sandboxed = sandboxed;
+
+       resultInfo->Set("code", 200);
+       resultInfo->Set("status", "Auto-completed successfully.");
+       resultInfo->Set("suggestions", Array::FromVector(GetAutocompletionSuggestions(command, frame)));
+
+       results->Add(resultInfo);
+
+       Dictionary::Ptr result = new Dictionary();
+       result->Set("results", results);
+
+       response.SetStatus(200, "OK");
+       HttpUtility::SendJsonBody(response, result);
+
+       return true;
+}
+
+static void AddSuggestion(std::vector<String>& matches, const String& word, const String& suggestion)
+{
+       if (suggestion.Find(word) != 0)
+               return;
+
+       matches.push_back(suggestion);
+}
+
+std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& word, ScriptFrame& frame)
+{      
+       std::vector<String> matches;
+
+       BOOST_FOREACH(const String& keyword, ConfigWriter::GetKeywords()) {
+               AddSuggestion(matches, word, keyword);
+       }
+
+       {
+               ObjectLock olock(frame.Locals);
+               BOOST_FOREACH(const Dictionary::Pair& kv, frame.Locals) {
+                       AddSuggestion(matches, word, kv.first);
+               }
+       }
+
+       {
+               ObjectLock olock(ScriptGlobal::GetGlobals());
+               BOOST_FOREACH(const Dictionary::Pair& kv, ScriptGlobal::GetGlobals()) {
+                       AddSuggestion(matches, word, kv.first);
+               }
+       }
+
+       String::SizeType cperiod = word.RFind(".");
+
+       if (cperiod != -1) {
+               String pword = word.SubStr(0, cperiod);
+
+               Value value;
+
+               try {
+                       Expression *expr = ConfigCompiler::CompileText("temp", pword);
+
+                       if (expr)
+                               value = expr->Evaluate(frame);
+
+                       if (value.IsObjectType<Dictionary>()) {
+                               Dictionary::Ptr dict = value;
+
+                               ObjectLock olock(dict);
+                               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                                       AddSuggestion(matches, word, pword + "." + kv.first);
+                               }
+                       }
+
+                       Type::Ptr type = value.GetReflectionType();
+
+                       for (int i = 0; i < type->GetFieldCount(); i++) {
+                               Field field = type->GetFieldInfo(i);
+
+                               AddSuggestion(matches, word, pword + "." + field.Name);
+                       }
+
+                       while (type) {
+                               Object::Ptr prototype = type->GetPrototype();
+                               Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
+
+                               if (dict) {
+                                       ObjectLock olock(dict);
+                                       BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                                               AddSuggestion(matches, word, pword + "." + kv.first);
+                                       }
+                               }
+
+                               type = type->GetBaseType();
+                       }
+               } catch (...) { /* Ignore the exception */ }
+       }
+
+       return matches;
+}
diff --git a/lib/remote/consolehandler.hpp b/lib/remote/consolehandler.hpp
new file mode 100644 (file)
index 0000000..146a747
--- /dev/null
@@ -0,0 +1,60 @@
+/******************************************************************************
+ * 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 CONSOLEHANDLER_H
+#define CONSOLEHANDLER_H
+
+#include "remote/httphandler.hpp"
+#include "base/scriptframe.hpp"
+
+namespace icinga
+{
+
+struct I2_REMOTE_API ApiScriptFrame
+{
+       double Seen;
+       int NextLine;
+       std::map<String, String> Lines;
+       Dictionary::Ptr Locals;
+
+       ApiScriptFrame(void)
+               : Seen(0), NextLine(1)
+       { }
+};
+
+class I2_REMOTE_API ConsoleHandler : public HttpHandler
+{
+public:
+       DECLARE_PTR_TYPEDEFS(ConsoleHandler);
+
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override;
+
+       static std::vector<String> GetAutocompletionSuggestions(const String& word, ScriptFrame& frame);
+
+private:
+       static bool ExecuteScriptHelper(HttpRequest& request, HttpResponse& response,
+           const String& command, const String& session, bool sandboxed);
+       static bool AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response,
+           const String& command, const String& session, bool sandboxed);
+
+};
+
+}
+
+#endif /* CONSOLEHANDLER_H */
index dbd841e2a03fe43809dd5fbfb289f05689f21761..4110573723afc09f186cea0a9191c51e48a202d2 100644 (file)
@@ -192,7 +192,7 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait)
                        if (srs != StatusNewItem)
                                return false;
 
-                       Log(LogInformation, "HttpResponse")
+                       Log(LogNotice, "HttpResponse")
                                << "Read " << size << " bytes";
 
                        m_Body->Write(data, size);
index 537f221dd2b91c123efec184a70dcb3569680288..05a54aa98a09cc3541e533d3518ad003caee21c6 100644 (file)
@@ -177,12 +177,14 @@ void Url::SetScheme(const String& scheme)
        m_Scheme = scheme;
 }
 
-void Url::SetAuthority(const String& username, const String& password, const String& host, const String& port)
+void Url::SetUsername(const String& username)
 {
        m_Username = username;
+}
+
+void Url::SetPassword(const String& password)
+{
        m_Password = password;
-       m_Host = host;
-       m_Port = port;
 }
 
 void Url::SetHost(const String& host)
index c416b4009a692c109a8689d8ba9f73c44e3a8614..11812634215fa380d8a3bfde96615ceff656d9cf 100644 (file)
@@ -59,12 +59,13 @@ public:
        String GetFragment(void) const;
 
        void SetScheme(const String& scheme);
-       void SetAuthority(const String& username, const String& password, 
-           const String& host, const String& port);
+       void SetUsername(const String& username);
+       void SetPassword(const String& password);
        void SetHost(const String& host);
        void SetPort(const String& port);
        void SetPath(const std::vector<String>& path);
        void SetQuery(const std::map<String, std::vector<String> >& query);
+
        void AddQueryElement(const String& name, const String& query);
        void SetQueryElements(const String& name, const std::vector<String>& query);
        void SetFragment(const String& fragment);
index 21cd99c6662be4379d9aa1801c558de7048d911c..afea891b2b11e75ec3e0c62ab2d68edfb45445bd 100644 (file)
@@ -47,7 +47,10 @@ BOOST_AUTO_TEST_CASE(get_and_set)
 {
        Url::Ptr url = new Url();
        url->SetScheme("ftp");
-       url->SetAuthority("Horst", "Seehofer", "koenigreich.bayern", "1918");
+       url->SetUsername("Horst");
+       url->SetPassword("Seehofer");
+       url->SetHost("koenigreich.bayern");
+       url->SetPort("1918");
        std::vector<String> p = boost::assign::list_of("path")("to")("münchen");
        url->SetPath(p);
        BOOST_CHECK(url->Format(true) == "ftp://Horst:Seehofer@koenigreich.bayern:1918/path/to/m%C3%BCnchen");