diff --git a/components/Makefile.am b/components/Makefile.am index 1dbf4996b..d3f7bbb11 100644 --- a/components/Makefile.am +++ b/components/Makefile.am @@ -6,5 +6,6 @@ SUBDIRS = \ compat \ delegation \ demo \ + livestatus \ notification \ replication diff --git a/components/livestatus/Makefile.am b/components/livestatus/Makefile.am new file mode 100644 index 000000000..b1745b604 --- /dev/null +++ b/components/livestatus/Makefile.am @@ -0,0 +1,37 @@ +## Process this file with automake to produce Makefile.in + +pkglib_LTLIBRARIES = \ + livestatus.la + +livestatus_la_SOURCES = \ + livestatuscomponent.cpp \ + livestatuscomponent.h \ + livestatusconnection.cpp \ + livestatusconnection.h \ + livestatusquery.cpp \ + livestatusquery.h \ + livestatustable.cpp \ + livestatustable.h \ + i2-livestatus.h + +livestatus_la_CPPFLAGS = \ + $(BOOST_CPPFLAGS) \ + -I${top_srcdir}/lib/base \ + -I${top_srcdir}/lib/config \ + -I${top_srcdir}/lib/remoting \ + -I${top_srcdir}/lib/icinga + +livestatus_la_LDFLAGS = \ + $(BOOST_LDFLAGS) \ + -module \ + -no-undefined \ + @RELEASE_INFO@ \ + @VERSION_INFO@ + +livestatus_la_LIBADD = \ + $(BOOST_SIGNALS_LIB) \ + $(BOOST_THREAD_LIB) \ + ${top_builddir}/lib/base/libbase.la \ + ${top_builddir}/lib/config/libconfig.la \ + ${top_builddir}/lib/remoting/libremoting.la \ + ${top_builddir}/lib/icinga/libicinga.la diff --git a/components/livestatus/i2-livestatus.h b/components/livestatus/i2-livestatus.h new file mode 100644 index 000000000..a94e818d6 --- /dev/null +++ b/components/livestatus/i2-livestatus.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * 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 I2LIVESTATUS_H +#define I2LIVESTATUS_H + +/** + * @defgroup livestatus Livestatus component + * + * The livestatus component implements livestatus queries. + */ + +#include +#include +#include + +#include "livestatusconnection.h" +#include "livestatusquery.h" +#include "livestatustable.h" +#include "livestatuscomponent.h" + +#endif /* I2LIVESTATUS_H */ diff --git a/components/livestatus/livestatus.vcxproj b/components/livestatus/livestatus.vcxproj new file mode 100644 index 000000000..c48033fff --- /dev/null +++ b/components/livestatus/livestatus.vcxproj @@ -0,0 +1,167 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2E6C1133-730F-4875-A72C-B455B1DD4C5C} + Win32Proj + demo + + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\lib\base;$(SolutionDir)\lib\remoting;$(SolutionDir)\lib\icinga;$(SolutionDir)\lib\config;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\lib\base;$(SolutionDir)\lib\remoting;$(SolutionDir)\lib\icinga;$(SolutionDir)\lib\config;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\lib\base;$(SolutionDir)\lib\remoting;$(SolutionDir)\lib\icinga;$(SolutionDir)\lib\config;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\lib\base;$(SolutionDir)\lib\remoting;$(SolutionDir)\lib\icinga;$(SolutionDir)\lib\config;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMO_EXPORTS;%(PreprocessorDefinitions) + Level3 + false + true + + + Windows + true + base.lib;remoting.lib;icinga.lib;%(AdditionalDependencies) + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMO_EXPORTS;%(PreprocessorDefinitions) + Level3 + false + true + + + Windows + true + base.lib;remoting.lib;icinga.lib;%(AdditionalDependencies) + + + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMO_EXPORTS;%(PreprocessorDefinitions) + Level3 + false + true + + + Windows + true + true + true + base.lib;remoting.lib;icinga.lib;%(AdditionalDependencies) + + + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMO_EXPORTS;%(PreprocessorDefinitions) + Level3 + false + true + + + Windows + true + true + true + base.lib;remoting.lib;icinga.lib;%(AdditionalDependencies) + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/livestatus/livestatus.vcxproj.filters b/components/livestatus/livestatus.vcxproj.filters new file mode 100644 index 000000000..e02ed2da9 --- /dev/null +++ b/components/livestatus/livestatus.vcxproj.filters @@ -0,0 +1,24 @@ + + + + + Headerdateien + + + Headerdateien + + + + + {11a495bf-a705-4766-b3d3-9b5db266a6ef} + + + {1fb6337f-a17f-46ea-9316-2d800a94b53d} + + + + + Quelldateien + + + \ No newline at end of file diff --git a/components/livestatus/livestatuscomponent.cpp b/components/livestatus/livestatuscomponent.cpp new file mode 100644 index 000000000..e9337d1bd --- /dev/null +++ b/components/livestatus/livestatuscomponent.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + * 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-livestatus.h" + +using namespace icinga; + +REGISTER_COMPONENT("livestatus", LivestatusComponent); + +/** + * Starts the component. + */ +void LivestatusComponent::Start(void) +{ + UnixSocket::Ptr socket = boost::make_shared(); + socket->OnNewClient.connect(boost::bind(&LivestatusComponent::NewClientHandler, this, _2)); + socket->Bind(GetSocketPath()); + socket->Listen(); + socket->Start(); + m_Listener = socket; +} + +/** + * Stops the component. + */ +void LivestatusComponent::Stop(void) +{ +} + +String LivestatusComponent::GetSocketPath(void) const +{ + DynamicObject::Ptr config = GetConfig(); + + Value socketPath = config->Get("socket_path"); + if (socketPath.IsEmpty()) + return Application::GetLocalStateDir() + "/run/icinga2/livestatus"; + else + return socketPath; +} + +void LivestatusComponent::NewClientHandler(const Socket::Ptr& client) +{ + Logger::Write(LogInformation, "livestatus", "Client connected"); + + LivestatusConnection::Ptr lconnection = boost::make_shared(client); + lconnection->OnClosed.connect(boost::bind(&LivestatusComponent::ClientClosedHandler, this, _1)); + + m_Connections.insert(lconnection); + client->Start(); +} + +void LivestatusComponent::ClientClosedHandler(const Connection::Ptr& connection) +{ + LivestatusConnection::Ptr lconnection = static_pointer_cast(connection); + + Logger::Write(LogInformation, "livestatus", "Client disconnected"); + m_Connections.erase(lconnection); +} diff --git a/components/livestatus/livestatuscomponent.h b/components/livestatus/livestatuscomponent.h new file mode 100644 index 000000000..ca79f356e --- /dev/null +++ b/components/livestatus/livestatuscomponent.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * 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 LIVESTATUSCOMPONENT_H +#define LIVESTATUSCOMPONENT_H + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class LivestatusComponent : public IComponent +{ +public: + virtual void Start(void); + virtual void Stop(void); + + String GetSocketPath(void) const; + +private: + Socket::Ptr m_Listener; + set m_Connections; + + void NewClientHandler(const Socket::Ptr& client); + void ClientClosedHandler(const Connection::Ptr& connection); +}; + +} + +#endif /* LIVESTATUSCOMPONENT_H */ diff --git a/components/livestatus/livestatusconnection.cpp b/components/livestatus/livestatusconnection.cpp new file mode 100644 index 000000000..b3a2cbecd --- /dev/null +++ b/components/livestatus/livestatusconnection.cpp @@ -0,0 +1,36 @@ +/****************************************************************************** + * 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-livestatus.h" + +using namespace icinga; + +LivestatusConnection::LivestatusConnection(const Stream::Ptr& stream) + : Connection(stream) +{ } + +void LivestatusConnection::ProcessData(void) +{ + LivestatusQuery::Ptr query = LivestatusQuery::ParseQuery(GetStream()); + + if (!query) + return; + + query->Execute(GetStream()); +} diff --git a/components/livestatus/livestatusconnection.h b/components/livestatus/livestatusconnection.h new file mode 100644 index 000000000..c72cb4669 --- /dev/null +++ b/components/livestatus/livestatusconnection.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * 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 LIVESTATUSCONNECTION_H +#define LIVESTATUSCONNECTION_H + +namespace icinga +{ + +class LivestatusConnection : public Connection +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + LivestatusConnection(const Stream::Ptr& stream); + +protected: + virtual void ProcessData(void); +}; + +} + +#endif /* LIVESTATUSCONNECTION_H */ diff --git a/components/livestatus/livestatusquery.cpp b/components/livestatus/livestatusquery.cpp new file mode 100644 index 000000000..22720851b --- /dev/null +++ b/components/livestatus/livestatusquery.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + * 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-livestatus.h" + +using namespace icinga; + +LivestatusQuery::LivestatusQuery(void) + : m_KeepAlive(false), m_ColumnHeaders(true), m_Limit(-1) +{ } + +LivestatusQuery::Ptr LivestatusQuery::ParseQuery(const Stream::Ptr& stream) +{ + LivestatusQuery::Ptr query = boost::make_shared(); + String line; + + if (!stream->ReadLine(&line)) + return LivestatusQuery::Ptr(); + + size_t sp_index = line.FindFirstOf(" "); + + if (sp_index == String::NPos) + BOOST_THROW_EXCEPTION(runtime_error("Livestatus header must contain a verb.")); + + String verb = line.SubStr(0, sp_index); + String target = line.SubStr(sp_index + 1); + + query->m_Verb = verb; + + if (query->m_Verb == "COMMAND") { + query->m_Command = target; + } else if (query->m_Verb == "GET") { + query->m_Table = target; + } else { + BOOST_THROW_EXCEPTION(runtime_error("Unknown livestatus verb: " + query->m_Verb)); + } + + while (!stream->IsEOF() && stream->ReadLine(&line)) { + /* Empty line means we've reached the end of this query. */ + if (line.GetLength() == 0) + return query; + + size_t col_index = line.FindFirstOf(":"); + String header = line.SubStr(0, col_index); + String params = line.SubStr(col_index + 2); + + if (header == "ResponseHeader") + query->m_ResponseHeader = params; + } + + /* Check if we've reached EOF. */ + if (!stream->IsConnected()) + return query; + + /* Query isn't complete yet. */ + return LivestatusQuery::Ptr(); +} + +void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream) +{ + +} + +void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream) +{ + try { + Logger::Write(LogInformation, "livestatus", "Executing command: " + m_Command); + ExternalCommandProcessor::Execute(m_Command); + } catch (const std::exception& ex) { + SendResponse(stream, 452, diagnostic_information(ex)); + } +} + +void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream) +{ + PrintFixed16(stream, m_ErrorCode, m_ErrorMessage); +} + +void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const String& data) +{ + if (m_ResponseHeader == "fixed16") + PrintFixed16(stream, code, data); + else if (code == 200) + stream->Write(data.CStr(), data.GetLength()); +} + +void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const String& data) +{ + ASSERT(code >= 100 && code <= 999); + + String sCode = Convert::ToString(code); + String sLength = Convert::ToString(data.GetLength()); + + String header = sCode + String(16 - 3 - sLength.GetLength() - 1, ' ') + sLength + "\n"; + stream->Write(header.CStr(), header.GetLength()); +} + +void LivestatusQuery::Execute(const Stream::Ptr& stream) +{ + Logger::Write(LogInformation, "livestatus", "Executing livestatus query: " + m_Verb); + + if (m_Verb == "GET") + ExecuteGetHelper(stream); + else if (m_Verb == "COMMAND") + ExecuteCommandHelper(stream); + else if (m_Verb == "ERROR") + ExecuteErrorHelper(stream); + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid livestatus query verb.")); +} diff --git a/components/livestatus/livestatusquery.h b/components/livestatus/livestatusquery.h new file mode 100644 index 000000000..9a163c816 --- /dev/null +++ b/components/livestatus/livestatusquery.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * 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 LIVESTATUSQUERY_H +#define LIVESTATUSQUERY_H + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class LivestatusQuery : public Object +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + static LivestatusQuery::Ptr ParseQuery(const Stream::Ptr& stream); + + void Execute(const Stream::Ptr& stream); + +private: + String m_Verb; + + bool m_KeepAlive; + + /* Parameters for GET queries. */ + String m_Table; + vector m_Columns; + + String m_OutputFormat; + bool m_ColumnHeaders; + int m_Limit; + + String m_ResponseHeader; + + /* Parameters for COMMAND queries. */ + String m_Command; + + /* Parameters for invalid queries. */ + int m_ErrorCode; + String m_ErrorMessage; + + LivestatusQuery(void); + + void ExecuteGetHelper(const Stream::Ptr& stream); + void ExecuteCommandHelper(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); + + friend Ptr boost::make_shared(void); +}; + +} + +#endif /* LIVESTATUSQUERY_H */ diff --git a/components/livestatus/livestatustable.cpp b/components/livestatus/livestatustable.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/components/livestatus/livestatustable.h b/components/livestatus/livestatustable.h new file mode 100644 index 000000000..e69de29bb diff --git a/configure.ac b/configure.ac index cf1ec7ea2..7b8788042 100644 --- a/configure.ac +++ b/configure.ac @@ -108,6 +108,7 @@ components/checker/Makefile components/compat/Makefile components/delegation/Makefile components/demo/Makefile +components/livestatus/Makefile components/notification/Makefile components/replication/Makefile docs/Doxyfile diff --git a/lib/base/i2-base.h b/lib/base/i2-base.h index b55bc9388..2a0587a13 100644 --- a/lib/base/i2-base.h +++ b/lib/base/i2-base.h @@ -130,6 +130,7 @@ using std::type_info; #include #include #include +#include #include #include #include diff --git a/lib/base/qstring.cpp b/lib/base/qstring.cpp index 4ac2d206a..e87846086 100644 --- a/lib/base/qstring.cpp +++ b/lib/base/qstring.cpp @@ -35,6 +35,10 @@ String::String(const std::string& data) : m_Data(data) { } +String::String(size_t n, char c) + : m_Data(n, c) +{ } + String::String(const String& other) : m_Data(other.m_Data) { } @@ -124,6 +128,11 @@ void String::Replace(size_t first, size_t second, const String& str) m_Data.replace(first, second, str); } +String Join(const vector& strings, const char *delim) +{ + return boost::algorithm::join(strings, delim); +} + void String::Trim(void) { boost::algorithm::trim(m_Data); @@ -257,4 +266,3 @@ String::ConstIterator icinga::range_end(const String& x) { return x.End(); } - diff --git a/lib/base/qstring.h b/lib/base/qstring.h index 154cb6245..4465a2104 100644 --- a/lib/base/qstring.h +++ b/lib/base/qstring.h @@ -34,9 +34,13 @@ public: typedef std::string::iterator Iterator; typedef std::string::const_iterator ConstIterator; + typedef std::string::iterator iterator; + typedef std::string::const_iterator const_iterator; + String(void); String(const char *data); String(const std::string& data); + String(size_t n, char c); template String(InputIterator begin, InputIterator end) @@ -66,7 +70,7 @@ public: size_t GetLength(void) const; size_t FindFirstOf(const char *s, size_t pos = 0) const; - String SubStr(size_t first, size_t len) const; + String SubStr(size_t first, size_t len = NPos) const; void Replace(size_t first, size_t second, const String& str); template @@ -77,11 +81,19 @@ public: return tokens; } + static String Join(const vector& strings, const char *delim); + void Trim(void); void swap(String& str); Iterator erase(Iterator first, Iterator last); + template + void insert(Iterator p, InputIterator first, InputIterator last) + { + m_Data.insert(p, first, last); + } + Iterator Begin(void); ConstIterator Begin(void) const; Iterator End(void); diff --git a/lib/base/socket.cpp b/lib/base/socket.cpp index 1e88b233e..8ccd97fd0 100644 --- a/lib/base/socket.cpp +++ b/lib/base/socket.cpp @@ -392,9 +392,13 @@ size_t Socket::Read(void *buffer, size_t size) throw new logic_error("Socket does not support Read()."); } - if (m_RecvQueue->GetAvailableBytes() == 0) + if (m_RecvQueue->GetAvailableBytes() == 0) { CheckException(); + if (!IsConnected()) + SetEOF(true); + } + return m_RecvQueue->Read(buffer, size); } @@ -516,9 +520,12 @@ void Socket::HandleReadableClient(void) #endif /* _WIN32 */ break; - if (rc <= 0) + if (rc < 0) BOOST_THROW_EXCEPTION(SocketException("recv() failed", GetError())); + if (rc == 0) + SetEOF(true); + m_RecvQueue->Write(data, rc); new_data = true; @@ -586,7 +593,7 @@ bool Socket::WantsToRead(void) const */ bool Socket::WantsToReadClient(void) const { - return true; + return !IsEOF(); } /** diff --git a/lib/base/stream.cpp b/lib/base/stream.cpp index cf5289b00..7a48da892 100644 --- a/lib/base/stream.cpp +++ b/lib/base/stream.cpp @@ -22,7 +22,7 @@ using namespace icinga; Stream::Stream(void) - : m_Connected(false) + : m_Connected(false), m_EOF(false) { } Stream::~Stream(void) @@ -40,6 +40,16 @@ bool Stream::IsConnected(void) const return m_Connected; } +/** + * @threadsafety Always. + */ +bool Stream::IsEOF(void) const +{ + ObjectLock olock(this); + + return m_EOF; +} + /** * @threadsafety Always. */ @@ -56,6 +66,17 @@ void Stream::SetConnected(bool connected) OnClosed(GetSelf()); } +/** + * @threadsafety Always. + */ +void Stream::SetEOF(bool eof) +{ + { + ObjectLock olock(this); + m_EOF = eof; + } +} + /** * Checks whether an exception is available for this stream and re-throws * the exception if there is one. @@ -119,9 +140,10 @@ bool Stream::ReadLine(String *line, size_t maxLength) size_t rc = Peek(buffer, maxLength); - for (int i = 0; i < rc; i++) { + for (size_t i = 0; i < rc; i++) { if (buffer[i] == '\n') { *line = String(buffer, &(buffer[i])); + boost::algorithm::trim_right(*line); Read(NULL, rc); diff --git a/lib/base/stream.h b/lib/base/stream.h index c02d2adba..10eb647d8 100644 --- a/lib/base/stream.h +++ b/lib/base/stream.h @@ -81,6 +81,7 @@ public: virtual void Close(void); bool IsConnected(void) const; + bool IsEOF(void) const; bool ReadLine(String *line, size_t maxLength = 4096); @@ -93,12 +94,14 @@ public: protected: void SetConnected(bool connected); + void SetEOF(bool eof); void SetException(boost::exception_ptr exception); private: bool m_Running; bool m_Connected; + bool m_EOF; boost::exception_ptr m_Exception; };