diff --git a/lib/remote/apilistener-filesync.cpp b/lib/remote/apilistener-filesync.cpp index 7cd1c00e1..ce51d16e5 100644 --- a/lib/remote/apilistener-filesync.cpp +++ b/lib/remote/apilistener-filesync.cpp @@ -23,6 +23,7 @@ using namespace icinga; REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler); REGISTER_APIFUNCTION(HaveZones, config, &ApiListener::ConfigHaveZonesHandler); +REGISTER_APIFUNCTION(WantZones, config, &ApiListener::ConfigWantZonesHandler); boost::mutex ApiListener::m_ConfigSyncStageLock; @@ -550,6 +551,101 @@ Value ApiListener::ConfigHaveZonesHandler(const MessageOrigin::Ptr& origin, cons return Empty; } +Value ApiListener::ConfigWantZonesHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + Log(LogNotice, "ApiListener") + << "Received request for zones: " << JsonEncode(params); + + /* check permissions */ + auto listener (ApiListener::GetInstance()); + + if (!listener) { + return Empty; + } + + auto endpoint (origin->FromClient->GetEndpoint()); + auto identity (origin->FromClient->GetIdentity()); + auto clientZone (endpoint->GetZone()); + + /* discard messages if the client is not configured on this node */ + if (!endpoint) { + Log(LogNotice, "ApiListener") + << "Discarding 'config want zones' message from '" << identity + << "': Invalid endpoint origin (client not allowed)."; + return Empty; + } + + auto zone (endpoint->GetZone()); + Array::Ptr zones (params->Get("zones")); + auto zonesDir (GetApiZonesDir()); + Dictionary::Ptr haveFiles = new Dictionary(); + ObjectLock oLock (zones); + + for (auto& zoneName : zones) { + auto zone (Zone::GetByName(zoneName)); + + if (!zone) { + Log(LogWarning, "ApiListener") + << "No such zone '" << zoneName << "' in zones request from '" << identity << "'."; + continue; + } + + if (!zone->IsChildOf(clientZone) && !zone->IsGlobal()) { + Log(LogWarning, "ApiListener") + << "Unauthorized access to zone '" << zoneName << "' in zones request from '" << identity << "'."; + continue; + } + + String checksumsPath = zonesDir + zoneName + "/.checksums"; + std::ifstream fp (checksumsPath.CStr(), std::ifstream::binary); + + if (!fp) { + Log(LogWarning, "ApiListener") + << "No local .checksums for zone '" << zoneName << "' for zones request from '" << identity << "'."; + continue; + } + + Log(LogNotice, "ApiListener") + << "Informing '" << identity << "' about files in zone '" << zoneName << "'."; + + haveFiles->Set(zoneName, JsonDecode(String( + std::istreambuf_iterator(fp), std::istreambuf_iterator() + ))); + } + + if (!haveFiles->GetLength()) { + Log(LogNotice, "ApiListener") + << "Not informing '" << identity << "' about files in any zone."; + return Empty; + } + + JsonRpcConnection::Ptr client; + + for (auto& conn : endpoint->GetClients()) { + client = conn; + break; + } + + if (!client) { + Log(LogNotice, "ApiListener") + << "Not informing '" << identity << "' about files in any zone as we're not connected."; + return Empty; + } + + Log(LogInformation, "ApiListener") + << "Informing '" << identity << "' about files in " << haveFiles->GetLength() << " zone(s)."; + + client->SendMessage(new Dictionary({ + { "jsonrpc", "2.0" }, + { "method", "config::HaveFiles" }, + { "params", new Dictionary({ + { "checksums", haveFiles } + }) } + })); + + return Empty; +} + void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { /* Only one transaction is allowed, concurrent message handlers need to wait. diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index e42fc1620..db781abff 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -86,6 +86,7 @@ public: /* filesync */ static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ConfigHaveZonesHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + static Value ConfigWantZonesHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static void HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); /* configsync */