From 15159b1632d52fac18b7ed98ae09f7a9afe83ecb Mon Sep 17 00:00:00 2001
From: Mattia Codato <mattia.codato@wuerth-phoenix.com>
Date: Fri, 10 Jul 2020 16:56:07 +0200
Subject: [PATCH] Add ExecuteCommandProcessFinishedHandler and checkable param
 to ExecuteRemoteCheck

---
 lib/icinga/checkable-check.cpp     |  8 +++++--
 lib/icinga/checkable.cpp           |  1 +
 lib/icinga/checkable.hpp           |  5 ++++-
 lib/icinga/clusterevents-check.cpp | 35 ++++++++++++++++++++++++++----
 lib/methods/executeactiontask.cpp  |  2 +-
 lib/methods/pluginchecktask.cpp    | 13 ++++++++---
 6 files changed, 53 insertions(+), 11 deletions(-)

diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp
index 68361fe17..cc32f09ee 100644
--- a/lib/icinga/checkable-check.cpp
+++ b/lib/icinga/checkable-check.cpp
@@ -492,7 +492,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig
 	}
 }
 
-void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros)
+void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros, const Checkable::Ptr& checkable)
 {
 	CONTEXT("Executing remote check for object '" + GetName() + "'");
 
@@ -503,7 +503,11 @@ void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros)
 	cr->SetScheduleStart(scheduled_start);
 	cr->SetExecutionStart(before_check);
 
-	GetCheckCommand()->Execute(this, cr, resolvedMacros, true);
+	if (!checkable) {
+		GetCheckCommand()->Execute(this, cr, resolvedMacros, true);
+	} else {
+		GetCheckCommand()->Execute(checkable, cr, resolvedMacros, true);
+	}
 }
 
 void Checkable::ExecuteCheck()
diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp
index 0f4d1399b..3f31d5667 100644
--- a/lib/icinga/checkable.cpp
+++ b/lib/icinga/checkable.cpp
@@ -20,6 +20,7 @@ boost::signals2::signal<void (const Checkable::Ptr&, const String&, double, cons
 boost::signals2::signal<void (const Checkable::Ptr&, double)> Checkable::OnFlappingChange;
 
 static Timer::Ptr l_CheckablesFireSuppressedNotifications;
+thread_local std::function<void(const Checkable::Ptr&, const CheckResult::Ptr&, const Value& /* commandLine */, const ProcessResult&)> Checkable::ExecuteCommandProcessFinishedHandler;
 
 void Checkable::StaticInitialize()
 {
diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp
index 0eb1c5950..38ecf0106 100644
--- a/lib/icinga/checkable.hpp
+++ b/lib/icinga/checkable.hpp
@@ -5,6 +5,7 @@
 
 #include "base/atomic.hpp"
 #include "base/timer.hpp"
+#include "base/process.hpp"
 #include "icinga/i2-icinga.hpp"
 #include "icinga/checkable-ti.hpp"
 #include "icinga/timeperiod.hpp"
@@ -14,6 +15,7 @@
 #include "remote/endpoint.hpp"
 #include "remote/messageorigin.hpp"
 #include <cstdint>
+#include <functional>
 
 namespace icinga
 {
@@ -55,6 +57,7 @@ public:
 	DECLARE_OBJECTNAME(Checkable);
 
 	static void StaticInitialize();
+	static thread_local std::function<void(const Checkable::Ptr&, const CheckResult::Ptr&, const Value& /* commandLine */, const ProcessResult&)> ExecuteCommandProcessFinishedHandler;
 
 	Checkable();
 
@@ -94,7 +97,7 @@ public:
 
 	static void UpdateStatistics(const CheckResult::Ptr& cr, CheckableType type);
 
-	void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr);
+	void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr, const Checkable::Ptr& checkbale = nullptr);
 	void ExecuteCheck();
 	void ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin = nullptr);
 
diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp
index 5db8c009b..8ab03f655 100644
--- a/lib/icinga/clusterevents-check.cpp
+++ b/lib/icinga/clusterevents-check.cpp
@@ -6,6 +6,7 @@
 #include "base/configuration.hpp"
 #include "base/serializer.hpp"
 #include "base/exception.hpp"
+#include "methods/executeactiontask.hpp"
 #include <boost/thread/once.hpp>
 #include <thread>
 
@@ -97,12 +98,38 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons
 		return;
 	}
 
+	Checkable::Ptr checkable = nullptr;
 	if (params->Contains("source")) {
-		Log(LogCritical, "ApiListener", "Not implemented.");
-
 		String uuid = params->Get("source");
 
-		return;
+		Host::Ptr host = Host::GetByName(params->Get("host"));
+		if (!host) {
+			Log(LogCritical, "ApiListener", "Host not found.");
+			return;
+		}
+
+		if (params->Contains("service"))
+			checkable = host->GetServiceByShortName(params->Get("service"));
+		else
+			checkable = host;
+
+		if (!checkable) {
+			Log(LogCritical, "ApiListener", "Checkable not found.");
+			return;
+		}
+
+		ObjectLock oLock (checkable);
+
+		if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
+			Log(LogNotice, "ApiListener")
+					<< "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName()
+					<< "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
+			return;
+		}
+
+		Checkable::ExecuteCommandProcessFinishedHandler = ExecuteActionTask::ProcessFinishedHandler;
+	} else {
+		Checkable::ExecuteCommandProcessFinishedHandler = nullptr;
 	}
 
 	if (!listener->GetAcceptCommands()) {
@@ -182,7 +209,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons
 
 	if (command_type == "check_command") {
 		try {
-			host->ExecuteRemoteCheck(macros);
+			host->ExecuteRemoteCheck(macros, checkable);
 		} catch (const std::exception& ex) {
 			CheckResult::Ptr cr = new CheckResult();
 			cr->SetState(ServiceUnknown);
diff --git a/lib/methods/executeactiontask.cpp b/lib/methods/executeactiontask.cpp
index c40a7ba24..305bc3213 100644
--- a/lib/methods/executeactiontask.cpp
+++ b/lib/methods/executeactiontask.cpp
@@ -54,7 +54,7 @@ void ExecuteActionTask::ProcessFinishedHandler(const Checkable::Ptr& checkable,
 
 	executedParams->Set("check_result", cr);
 
-	/* FIXME command endpoint was overwrite by macro? */
+	/* FIXME command endpoint overwritten by macro? */
 	Endpoint::Ptr commandEndpoint = checkable->GetCommandEndpoint();
 	bool local = !commandEndpoint || commandEndpoint == Endpoint::GetLocalEndpoint();
 	if (local) {
diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp
index 70523b49f..43851f31d 100644
--- a/lib/methods/pluginchecktask.cpp
+++ b/lib/methods/pluginchecktask.cpp
@@ -43,9 +43,16 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
 	if (!checkable->GetCheckTimeout().IsEmpty())
 		timeout = checkable->GetCheckTimeout();
 
-	PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(),
-		resolvers, resolvedMacros, useResolvedMacros, timeout,
-		std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2));
+
+	if (Checkable::ExecuteCommandProcessFinishedHandler) {
+		PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(),
+			resolvers, resolvedMacros, useResolvedMacros, timeout,
+			std::bind(Checkable::ExecuteCommandProcessFinishedHandler, checkable, cr, _1, _2));
+	} else {
+		PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(),
+			resolvers, resolvedMacros, useResolvedMacros, timeout,
+			std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2));
+	}
 
 	if (!resolvedMacros || useResolvedMacros) {
 		Checkable::CurrentConcurrentChecks.fetch_add(1);