1 /******************************************************************************
3 * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "remote/apilistener.hpp"
21 #include "remote/apilistener.tcpp"
22 #include "remote/jsonrpcconnection.hpp"
23 #include "remote/endpoint.hpp"
24 #include "remote/jsonrpc.hpp"
25 #include "remote/apifunction.hpp"
26 #include "base/convert.hpp"
27 #include "base/netstring.hpp"
28 #include "base/json.hpp"
29 #include "base/configtype.hpp"
30 #include "base/logger.hpp"
31 #include "base/objectlock.hpp"
32 #include "base/stdiostream.hpp"
33 #include "base/application.hpp"
34 #include "base/context.hpp"
35 #include "base/statsfunction.hpp"
36 #include "base/exception.hpp"
39 using namespace icinga;
41 REGISTER_TYPE(ApiListener);
43 boost::signals2::signal<void(bool)> ApiListener::OnMasterChanged;
45 REGISTER_STATSFUNCTION(ApiListenerStats, &ApiListener::StatsFunc);
47 REGISTER_APIFUNCTION(Hello, icinga, &ApiListener::HelloAPIHandler);
49 ApiListener::ApiListener(void)
50 : m_LogMessageCount(0)
53 void ApiListener::OnConfigLoaded(void)
55 /* set up SSL context */
56 boost::shared_ptr<X509> cert;
58 cert = GetX509Certificate(GetCertPath());
59 } catch (const std::exception&) {
60 BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate from cert path: '" + GetCertPath() + "'.", GetDebugInfo()));
64 SetIdentity(GetCertificateCN(cert));
65 } catch (const std::exception&) {
66 BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate common name from cert path: '" + GetCertPath() + "'.", GetDebugInfo()));
69 Log(LogInformation, "ApiListener")
70 << "My API identity: " << GetIdentity();
73 m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
74 } catch (const std::exception&) {
75 BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '" + GetCertPath() + "' key path: '" + GetKeyPath() + "' ca path: '" + GetCaPath() + "'.", GetDebugInfo()));
78 if (!GetCrlPath().IsEmpty()) {
80 AddCRLToSSLContext(m_SSLContext, GetCrlPath());
81 } catch (const std::exception&) {
82 BOOST_THROW_EXCEPTION(ScriptError("Cannot add certificate revocation list to SSL context for crl path: '" + GetCrlPath() + "'.", GetDebugInfo()));
87 void ApiListener::OnAllConfigLoaded(void)
89 if (!Endpoint::GetByName(GetIdentity()))
90 BOOST_THROW_EXCEPTION(ScriptError("Endpoint object for '" + GetIdentity() + "' is missing.", GetDebugInfo()));
94 * Starts the component.
96 void ApiListener::Start(void)
100 if (std::distance(ConfigType::GetObjectsByType<ApiListener>().first, ConfigType::GetObjectsByType<ApiListener>().second) > 1) {
101 Log(LogCritical, "ApiListener", "Only one ApiListener object is allowed.");
105 ObjectImpl<ApiListener>::Start();
108 boost::mutex::scoped_lock(m_LogLock);
113 /* create the primary JSON-RPC listener */
114 if (!AddListener(GetBindHost(), GetBindPort())) {
115 Log(LogCritical, "ApiListener")
116 << "Cannot add listener on host '" << GetBindHost() << "' for port '" << GetBindPort() << "'.";
117 Application::Exit(EXIT_FAILURE);
120 m_Timer = new Timer();
121 m_Timer->OnTimerExpired.connect(boost::bind(&ApiListener::ApiTimerHandler, this));
122 m_Timer->SetInterval(5);
124 m_Timer->Reschedule(0);
126 OnMasterChanged(true);
129 ApiListener::Ptr ApiListener::GetInstance(void)
131 BOOST_FOREACH(const ApiListener::Ptr& listener, ConfigType::GetObjectsByType<ApiListener>())
134 return ApiListener::Ptr();
137 boost::shared_ptr<SSL_CTX> ApiListener::GetSSLContext(void) const
142 Endpoint::Ptr ApiListener::GetMaster(void) const
144 Zone::Ptr zone = Zone::GetLocalZone();
147 return Endpoint::Ptr();
149 std::vector<String> names;
151 BOOST_FOREACH(const Endpoint::Ptr& endpoint, zone->GetEndpoints())
152 if (endpoint->IsConnected() || endpoint->GetName() == GetIdentity())
153 names.push_back(endpoint->GetName());
155 std::sort(names.begin(), names.end());
157 return Endpoint::GetByName(*names.begin());
160 bool ApiListener::IsMaster(void) const
162 Endpoint::Ptr master = GetMaster();
167 return master->GetName() == GetIdentity();
171 * Creates a new JSON-RPC listener on the specified port.
173 * @param node The host the listener should be bound to.
174 * @param service The port to listen on.
176 bool ApiListener::AddListener(const String& node, const String& service)
178 ObjectLock olock(this);
180 boost::shared_ptr<SSL_CTX> sslContext = m_SSLContext;
183 Log(LogCritical, "ApiListener", "SSL context is required for AddListener()");
187 Log(LogInformation, "ApiListener")
188 << "Adding new listener on port '" << service << "'";
190 TcpSocket::Ptr server = new TcpSocket();
193 server->Bind(node, service, AF_UNSPEC);
194 } catch (const std::exception&) {
195 Log(LogCritical, "ApiListener")
196 << "Cannot bind TCP socket for host '" << node << "' on port '" << service << "'.";
200 boost::thread thread(boost::bind(&ApiListener::ListenerThreadProc, this, server));
203 m_Servers.insert(server);
208 void ApiListener::ListenerThreadProc(const Socket::Ptr& server)
210 Utility::SetThreadName("API Listener");
216 Socket::Ptr client = server->Accept();
217 boost::thread thread(boost::bind(&ApiListener::NewClientHandler, this, client, String(), RoleServer));
219 } catch (const std::exception&) {
220 Log(LogCritical, "ApiListener", "Cannot accept new connection.");
226 * Creates a new JSON-RPC client and connects to the specified endpoint.
228 * @param endpoint The endpoint.
230 void ApiListener::AddConnection(const Endpoint::Ptr& endpoint)
233 ObjectLock olock(this);
235 boost::shared_ptr<SSL_CTX> sslContext = m_SSLContext;
238 Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()");
243 String host = endpoint->GetHost();
244 String port = endpoint->GetPort();
246 Log(LogInformation, "JsonRpcConnection")
247 << "Reconnecting to API endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'";
249 TcpSocket::Ptr client = new TcpSocket();
252 endpoint->SetConnecting(true);
253 client->Connect(host, port);
254 NewClientHandler(client, endpoint->GetName(), RoleClient);
255 endpoint->SetConnecting(false);
256 } catch (const std::exception& ex) {
257 endpoint->SetConnecting(false);
260 std::ostringstream info;
261 info << "Cannot connect to host '" << host << "' on port '" << port << "'";
262 Log(LogCritical, "ApiListener", info.str());
263 Log(LogDebug, "ApiListener")
264 << info.str() << "\n" << DiagnosticInformation(ex);
268 void ApiListener::NewClientHandler(const Socket::Ptr& client, const String& hostname, ConnectionRole role)
271 NewClientHandlerInternal(client, hostname, role);
272 } catch (const std::exception& ex) {
273 Log(LogCritical, "ApiListener")
274 << "Exception while handling new API client connection: " << DiagnosticInformation(ex);
279 * Processes a new client connection.
281 * @param client The new client.
283 void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const String& hostname, ConnectionRole role)
285 CONTEXT("Handling new API client connection");
287 TlsStream::Ptr tlsStream;
290 ObjectLock olock(this);
292 tlsStream = new TlsStream(client, hostname, role, m_SSLContext);
293 } catch (const std::exception&) {
294 Log(LogCritical, "ApiListener", "Cannot create TLS stream from client connection.");
300 tlsStream->Handshake();
301 } catch (const std::exception& ex) {
302 Log(LogCritical, "ApiListener", "Client TLS handshake failed");
306 boost::shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
308 Endpoint::Ptr endpoint;
309 bool verify_ok = false;
313 identity = GetCertificateCN(cert);
314 } catch (const std::exception&) {
315 Log(LogCritical, "ApiListener")
316 << "Cannot get certificate common name from cert path: '" << GetCertPath() << "'.";
320 verify_ok = tlsStream->IsVerifyOK();
322 Log(LogInformation, "ApiListener")
323 << "New client connection for identity '" << identity << "'" << (verify_ok ? "" : " (unauthenticated)");
327 endpoint = Endpoint::GetByName(identity);
329 Log(LogInformation, "ApiListener")
330 << "New client connection (no client certificate)";
333 bool need_sync = false;
336 need_sync = !endpoint->IsConnected();
340 if (role == RoleClient) {
341 Dictionary::Ptr message = new Dictionary();
342 message->Set("jsonrpc", "2.0");
343 message->Set("method", "icinga::Hello");
344 message->Set("params", new Dictionary());
345 JsonRpc::SendMessage(tlsStream, message);
346 ctype = ClientJsonRpc;
348 tlsStream->WaitForData(5);
350 if (!tlsStream->IsDataAvailable()) {
351 Log(LogWarning, "ApiListener", "No data received on new API connection.");
356 tlsStream->Peek(&firstByte, 1, false);
358 if (firstByte >= '0' && firstByte <= '9')
359 ctype = ClientJsonRpc;
364 if (ctype == ClientJsonRpc) {
365 Log(LogInformation, "ApiListener", "New JSON-RPC client");
367 JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, tlsStream, role);
371 endpoint->AddClient(aclient);
375 ObjectLock olock(endpoint);
377 endpoint->SetSyncing(true);
383 /* sync zone file config */
384 SendConfigUpdate(aclient);
385 /* sync runtime config */
386 SendRuntimeConfigObjects(aclient);
388 AddAnonymousClient(aclient);
390 Log(LogInformation, "ApiListener", "New HTTP client");
392 HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, tlsStream);
394 AddHttpClient(aclient);
398 void ApiListener::ApiTimerHandler(void)
400 double now = Utility::GetTime();
402 std::vector<int> files;
403 Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
404 std::sort(files.begin(), files.end());
406 BOOST_FOREACH(int ts, files) {
409 BOOST_FOREACH(const Endpoint::Ptr& endpoint, ConfigType::GetObjectsByType<Endpoint>()) {
410 if (endpoint->GetName() == GetIdentity())
413 if (endpoint->GetLogDuration() >= 0 && ts < now - endpoint->GetLogDuration())
416 if (ts > endpoint->GetLocalLogPosition()) {
423 String path = GetApiDir() + "log/" + Convert::ToString(ts);
424 Log(LogNotice, "ApiListener")
425 << "Removing old log file: " << path;
426 (void)unlink(path.CStr());
430 Zone::Ptr my_zone = Zone::GetLocalZone();
432 BOOST_FOREACH(const Zone::Ptr& zone, ConfigType::GetObjectsByType<Zone>()) {
433 /* only connect to endpoints in a) the same zone b) our parent zone c) immediate child zones */
434 if (my_zone != zone && my_zone != zone->GetParent() && zone != my_zone->GetParent()) {
435 Log(LogDebug, "ApiListener")
436 << "Not connecting to Zone '" << zone->GetName() << "' because it's not in the same zone, a parent or a child zone.";
440 BOOST_FOREACH(const Endpoint::Ptr& endpoint, zone->GetEndpoints()) {
441 /* don't connect to ourselves */
442 if (endpoint->GetName() == GetIdentity()) {
443 Log(LogDebug, "ApiListener")
444 << "Not connecting to Endpoint '" << endpoint->GetName() << "' because that's us.";
448 /* don't try to connect to endpoints which don't have a host and port */
449 if (endpoint->GetHost().IsEmpty() || endpoint->GetPort().IsEmpty()) {
450 Log(LogDebug, "ApiListener")
451 << "Not connecting to Endpoint '" << endpoint->GetName() << "' because the host/port attributes are missing.";
455 /* don't try to connect if there's already a connection attempt */
456 if (endpoint->GetConnecting()) {
457 Log(LogDebug, "ApiListener")
458 << "Not connecting to Endpoint '" << endpoint->GetName() << "' because we're already trying to connect to it.";
462 /* don't try to connect if we're already connected */
463 if (endpoint->IsConnected()) {
464 Log(LogDebug, "ApiListener")
465 << "Not connecting to Endpoint '" << endpoint->GetName() << "' because we're already connected to it.";
469 boost::thread thread(boost::bind(&ApiListener::AddConnection, this, endpoint));
474 BOOST_FOREACH(const Endpoint::Ptr& endpoint, ConfigType::GetObjectsByType<Endpoint>()) {
475 if (!endpoint->IsConnected())
478 double ts = endpoint->GetRemoteLogPosition();
483 Dictionary::Ptr lparams = new Dictionary();
484 lparams->Set("log_position", ts);
486 Dictionary::Ptr lmessage = new Dictionary();
487 lmessage->Set("jsonrpc", "2.0");
488 lmessage->Set("method", "log::SetLogPosition");
489 lmessage->Set("params", lparams);
491 BOOST_FOREACH(const JsonRpcConnection::Ptr& client, endpoint->GetClients())
492 client->SendMessage(lmessage);
494 Log(LogNotice, "ApiListener")
495 << "Setting log position for identity '" << endpoint->GetName() << "': "
496 << Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", ts);
499 Endpoint::Ptr master = GetMaster();
502 Log(LogNotice, "ApiListener")
503 << "Current zone master: " << master->GetName();
505 std::vector<String> names;
506 BOOST_FOREACH(const Endpoint::Ptr& endpoint, ConfigType::GetObjectsByType<Endpoint>())
507 if (endpoint->IsConnected())
508 names.push_back(endpoint->GetName() + " (" + Convert::ToString(endpoint->GetClients().size()) + ")");
510 Log(LogNotice, "ApiListener")
511 << "Connected endpoints: " << Utility::NaturalJoin(names);
514 void ApiListener::RelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
516 m_RelayQueue.Enqueue(boost::bind(&ApiListener::SyncRelayMessage, this, origin, secobj, message, log), true);
519 void ApiListener::PersistMessage(const Dictionary::Ptr& message, const ConfigObject::Ptr& secobj)
521 double ts = message->Get("ts");
525 Dictionary::Ptr pmessage = new Dictionary();
526 pmessage->Set("timestamp", ts);
528 pmessage->Set("message", JsonEncode(message));
530 Dictionary::Ptr secname = new Dictionary();
531 secname->Set("type", secobj->GetType()->GetName());
532 secname->Set("name", secobj->GetName());
533 pmessage->Set("secobj", secname);
535 boost::mutex::scoped_lock lock(m_LogLock);
537 NetString::WriteStringToStream(m_LogFile, JsonEncode(pmessage));
539 SetLogMessageTimestamp(ts);
541 if (m_LogMessageCount > 50000) {
549 void ApiListener::SyncSendMessage(const Endpoint::Ptr& endpoint, const Dictionary::Ptr& message)
551 ObjectLock olock(endpoint);
553 if (!endpoint->GetSyncing()) {
554 Log(LogNotice, "ApiListener")
555 << "Sending message to '" << endpoint->GetName() << "'";
557 BOOST_FOREACH(const JsonRpcConnection::Ptr& client, endpoint->GetClients())
558 client->SendMessage(message);
563 void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
565 double ts = Utility::GetTime();
566 message->Set("ts", ts);
568 Log(LogNotice, "ApiListener")
569 << "Relaying '" << message->Get("method") << "' message";
572 PersistMessage(message, secobj);
574 if (origin && origin->FromZone)
575 message->Set("originZone", origin->FromZone->GetName());
577 bool is_master = IsMaster();
578 Endpoint::Ptr master = GetMaster();
579 Zone::Ptr my_zone = Zone::GetLocalZone();
581 std::vector<Endpoint::Ptr> skippedEndpoints;
582 std::set<Zone::Ptr> finishedZones;
584 BOOST_FOREACH(const Endpoint::Ptr& endpoint, ConfigType::GetObjectsByType<Endpoint>()) {
585 /* don't relay messages to ourselves or disconnected endpoints */
586 if (endpoint->GetName() == GetIdentity() || !endpoint->IsConnected())
589 Zone::Ptr target_zone = endpoint->GetZone();
591 /* don't relay the message to the zone through more than one endpoint */
592 if (finishedZones.find(target_zone) != finishedZones.end()) {
593 skippedEndpoints.push_back(endpoint);
597 /* don't relay messages back to the endpoint which we got the message from */
598 if (origin && origin->FromClient && endpoint == origin->FromClient->GetEndpoint()) {
599 skippedEndpoints.push_back(endpoint);
603 /* don't relay messages back to the zone which we got the message from */
604 if (origin && origin->FromZone && target_zone == origin->FromZone) {
605 skippedEndpoints.push_back(endpoint);
609 /* only relay message to the master if we're not currently the master */
610 if (!is_master && master != endpoint) {
611 skippedEndpoints.push_back(endpoint);
615 /* only relay the message to a) the same zone, b) the parent zone and c) direct child zones */
616 if (target_zone != my_zone && target_zone != my_zone->GetParent() &&
617 secobj->GetZoneName() != target_zone->GetName()) {
618 skippedEndpoints.push_back(endpoint);
622 /* only relay messages to zones which have access to the object */
623 if (!target_zone->CanAccessObject(secobj))
626 finishedZones.insert(target_zone);
628 SyncSendMessage(endpoint, message);
631 BOOST_FOREACH(const Endpoint::Ptr& endpoint, skippedEndpoints)
632 endpoint->SetLocalLogPosition(ts);
635 String ApiListener::GetApiDir(void)
637 return Application::GetLocalStateDir() + "/lib/icinga2/api/";
640 /* must hold m_LogLock */
641 void ApiListener::OpenLogFile(void)
643 String path = GetApiDir() + "log/current";
645 std::fstream *fp = new std::fstream(path.CStr(), std::fstream::out | std::ofstream::app);
648 Log(LogWarning, "ApiListener")
649 << "Could not open spool file: " << path;
653 m_LogFile = new StdioStream(fp, true);
654 m_LogMessageCount = 0;
655 SetLogMessageTimestamp(Utility::GetTime());
658 /* must hold m_LogLock */
659 void ApiListener::CloseLogFile(void)
668 /* must hold m_LogLock */
669 void ApiListener::RotateLogFile(void)
671 double ts = GetLogMessageTimestamp();
674 ts = Utility::GetTime();
676 String oldpath = GetApiDir() + "log/current";
677 String newpath = GetApiDir() + "log/" + Convert::ToString(static_cast<int>(ts)+1);
678 (void) rename(oldpath.CStr(), newpath.CStr());
681 void ApiListener::LogGlobHandler(std::vector<int>& files, const String& file)
683 String name = Utility::BaseName(file);
688 ts = Convert::ToLong(name);
690 catch (const std::exception&) {
697 void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client)
699 Endpoint::Ptr endpoint = client->GetEndpoint();
701 CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");
704 double peer_ts = endpoint->GetLocalLogPosition();
705 double logpos_ts = peer_ts;
706 bool last_sync = false;
708 Endpoint::Ptr target_endpoint = client->GetEndpoint();
709 ASSERT(target_endpoint);
711 Zone::Ptr target_zone = target_endpoint->GetZone();
717 boost::mutex::scoped_lock lock(m_LogLock);
722 if (count == -1 || count > 50000) {
731 std::vector<int> files;
732 Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
733 std::sort(files.begin(), files.end());
735 BOOST_FOREACH(int ts, files) {
736 String path = GetApiDir() + "log/" + Convert::ToString(ts);
741 Log(LogNotice, "ApiListener")
742 << "Replaying log: " << path;
744 std::fstream *fp = new std::fstream(path.CStr(), std::fstream::in | std::fstream::binary);
745 StdioStream::Ptr logStream = new StdioStream(fp, true);
748 StreamReadContext src;
750 Dictionary::Ptr pmessage;
753 StreamReadStatus srs = NetString::ReadStringFromStream(logStream, &message, src);
755 if (srs == StatusEof)
758 if (srs != StatusNewItem)
761 pmessage = JsonDecode(message);
762 } catch (const std::exception&) {
763 Log(LogWarning, "ApiListener")
764 << "Unexpected end-of-file for cluster log: " << path;
766 /* Log files may be incomplete or corrupted. This is perfectly OK. */
770 if (pmessage->Get("timestamp") <= peer_ts)
773 Dictionary::Ptr secname = pmessage->Get("secobj");
776 ConfigType::Ptr dtype = ConfigType::GetByName(secname->Get("type"));
781 ConfigObject::Ptr secobj = dtype->GetObject(secname->Get("name"));
786 if (!target_zone->CanAccessObject(secobj))
790 NetString::WriteStringToStream(client->GetStream(), pmessage->Get("message"));
793 peer_ts = pmessage->Get("timestamp");
795 if (ts > logpos_ts + 10) {
798 Dictionary::Ptr lparams = new Dictionary();
799 lparams->Set("log_position", logpos_ts);
801 Dictionary::Ptr lmessage = new Dictionary();
802 lmessage->Set("jsonrpc", "2.0");
803 lmessage->Set("method", "log::SetLogPosition");
804 lmessage->Set("params", lparams);
806 JsonRpc::SendMessage(client->GetStream(), lmessage);
814 Log(LogInformation, "ApiListener")
815 << "Replayed " << count << " messages.";
818 Log(LogNotice, "ApiListener")
819 << "Replayed " << count << " messages.";
823 ObjectLock olock2(endpoint);
824 endpoint->SetSyncing(false);
834 void ApiListener::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
836 Dictionary::Ptr nodes = new Dictionary();
837 std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
839 ApiListener::Ptr listener = ApiListener::GetInstance();
844 stats = listener->GetStatus();
846 ObjectLock olock(stats.second);
847 BOOST_FOREACH(const Dictionary::Pair& kv, stats.second)
848 perfdata->Add("'api_" + kv.first + "'=" + Convert::ToString(kv.second));
850 status->Set("api", stats.first);
853 std::pair<Dictionary::Ptr, Dictionary::Ptr> ApiListener::GetStatus(void)
855 Dictionary::Ptr status = new Dictionary();
856 Dictionary::Ptr perfdata = new Dictionary();
859 status->Set("identity", GetIdentity());
861 double count_endpoints = 0;
862 Array::Ptr not_connected_endpoints = new Array();
863 Array::Ptr connected_endpoints = new Array();
865 Zone::Ptr my_zone = Zone::GetLocalZone();
867 BOOST_FOREACH(const Zone::Ptr& zone, ConfigType::GetObjectsByType<Zone>()) {
868 /* only check endpoints in a) the same zone b) our parent zone c) immediate child zones */
869 if (my_zone != zone && my_zone != zone->GetParent() && zone != my_zone->GetParent()) {
870 Log(LogDebug, "ApiListener")
871 << "Not checking connection to Zone '" << zone->GetName() << "' because it's not in the same zone, a parent or a child zone.";
875 BOOST_FOREACH(const Endpoint::Ptr& endpoint, zone->GetEndpoints()) {
876 if (endpoint->GetName() == GetIdentity())
881 if (!endpoint->IsConnected())
882 not_connected_endpoints->Add(endpoint->GetName());
884 connected_endpoints->Add(endpoint->GetName());
888 status->Set("num_endpoints", count_endpoints);
889 status->Set("num_conn_endpoints", connected_endpoints->GetLength());
890 status->Set("num_not_conn_endpoints", not_connected_endpoints->GetLength());
891 status->Set("conn_endpoints", connected_endpoints);
892 status->Set("not_conn_endpoints", not_connected_endpoints);
894 perfdata->Set("num_endpoints", count_endpoints);
895 perfdata->Set("num_conn_endpoints", Convert::ToDouble(connected_endpoints->GetLength()));
896 perfdata->Set("num_not_conn_endpoints", Convert::ToDouble(not_connected_endpoints->GetLength()));
898 return std::make_pair(status, perfdata);
901 void ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient)
903 ObjectLock olock(this);
904 m_AnonymousClients.insert(aclient);
907 void ApiListener::RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient)
909 ObjectLock olock(this);
910 m_AnonymousClients.erase(aclient);
913 std::set<JsonRpcConnection::Ptr> ApiListener::GetAnonymousClients(void) const
915 ObjectLock olock(this);
916 return m_AnonymousClients;
919 void ApiListener::AddHttpClient(const HttpServerConnection::Ptr& aclient)
921 ObjectLock olock(this);
922 m_HttpClients.insert(aclient);
925 void ApiListener::RemoveHttpClient(const HttpServerConnection::Ptr& aclient)
927 ObjectLock olock(this);
928 m_HttpClients.erase(aclient);
931 std::set<HttpServerConnection::Ptr> ApiListener::GetHttpClients(void) const
933 ObjectLock olock(this);
934 return m_HttpClients;
937 Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)