summaryrefslogtreecommitdiffstats
path: root/lib/methods
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:32:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:32:39 +0000
commit56ae875861ab260b80a030f50c4aff9f9dc8fff0 (patch)
tree531412110fc901a5918c7f7442202804a83cada9 /lib/methods
parentInitial commit. (diff)
downloadicinga2-56ae875861ab260b80a030f50c4aff9f9dc8fff0.tar.xz
icinga2-56ae875861ab260b80a030f50c4aff9f9dc8fff0.zip
Adding upstream version 2.14.2.upstream/2.14.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/methods')
-rw-r--r--lib/methods/CMakeLists.txt34
-rw-r--r--lib/methods/clusterchecktask.cpp117
-rw-r--r--lib/methods/clusterchecktask.hpp29
-rw-r--r--lib/methods/clusterzonechecktask.cpp218
-rw-r--r--lib/methods/clusterzonechecktask.hpp28
-rw-r--r--lib/methods/dummychecktask.cpp75
-rw-r--r--lib/methods/dummychecktask.hpp30
-rw-r--r--lib/methods/exceptionchecktask.cpp41
-rw-r--r--lib/methods/exceptionchecktask.hpp29
-rw-r--r--lib/methods/i2-methods.hpp15
-rw-r--r--lib/methods/icingachecktask.cpp209
-rw-r--r--lib/methods/icingachecktask.hpp29
-rw-r--r--lib/methods/ifwapichecktask.cpp531
-rw-r--r--lib/methods/ifwapichecktask.hpp27
-rw-r--r--lib/methods/methods-itl.conf90
-rw-r--r--lib/methods/nullchecktask.cpp50
-rw-r--r--lib/methods/nullchecktask.hpp30
-rw-r--r--lib/methods/nulleventtask.cpp26
-rw-r--r--lib/methods/nulleventtask.hpp30
-rw-r--r--lib/methods/pluginchecktask.cpp89
-rw-r--r--lib/methods/pluginchecktask.hpp33
-rw-r--r--lib/methods/plugineventtask.cpp61
-rw-r--r--lib/methods/plugineventtask.hpp33
-rw-r--r--lib/methods/pluginnotificationtask.cpp123
-rw-r--r--lib/methods/pluginnotificationtask.hpp36
-rw-r--r--lib/methods/randomchecktask.cpp65
-rw-r--r--lib/methods/randomchecktask.hpp29
-rw-r--r--lib/methods/sleepchecktask.cpp67
-rw-r--r--lib/methods/sleepchecktask.hpp30
-rw-r--r--lib/methods/timeperiodtask.cpp35
-rw-r--r--lib/methods/timeperiodtask.hpp28
31 files changed, 2267 insertions, 0 deletions
diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt
new file mode 100644
index 0000000..a7c3090
--- /dev/null
+++ b/lib/methods/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+mkembedconfig_target(methods-itl.conf methods-itl.cpp)
+
+set(methods_SOURCES
+ i2-methods.hpp methods-itl.cpp
+ clusterchecktask.cpp clusterchecktask.hpp
+ clusterzonechecktask.cpp clusterzonechecktask.hpp
+ dummychecktask.cpp dummychecktask.hpp
+ exceptionchecktask.cpp exceptionchecktask.hpp
+ icingachecktask.cpp icingachecktask.hpp
+ ifwapichecktask.cpp ifwapichecktask.hpp
+ nullchecktask.cpp nullchecktask.hpp
+ nulleventtask.cpp nulleventtask.hpp
+ pluginchecktask.cpp pluginchecktask.hpp
+ plugineventtask.cpp plugineventtask.hpp
+ pluginnotificationtask.cpp pluginnotificationtask.hpp
+ randomchecktask.cpp randomchecktask.hpp
+ timeperiodtask.cpp timeperiodtask.hpp
+ sleepchecktask.cpp sleepchecktask.hpp
+)
+
+if(ICINGA2_UNITY_BUILD)
+ mkunity_target(methods methods methods_SOURCES)
+endif()
+
+add_library(methods OBJECT ${methods_SOURCES})
+
+add_dependencies(methods base config icinga)
+
+set_target_properties (
+ methods PROPERTIES
+ FOLDER Lib
+)
diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp
new file mode 100644
index 0000000..6ce28ca
--- /dev/null
+++ b/lib/methods/clusterchecktask.cpp
@@ -0,0 +1,117 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/clusterchecktask.hpp"
+#include "remote/apilistener.hpp"
+#include "remote/endpoint.hpp"
+#include "icinga/cib.hpp"
+#include "icinga/service.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "icinga/checkcommand.hpp"
+#include "base/application.hpp"
+#include "base/objectlock.hpp"
+#include "base/convert.hpp"
+#include "base/utility.hpp"
+#include "base/function.hpp"
+#include "base/configtype.hpp"
+#include <boost/algorithm/string/join.hpp>
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+ String commandName = command->GetName();
+
+ ApiListener::Ptr listener = ApiListener::GetInstance();
+ if (!listener) {
+ String output = "No API listener is configured for this instance.";
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = 126;
+ pr.Output = output;
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetOutput(output);
+ cr->SetState(ServiceUnknown);
+ checkable->ProcessCheckResult(cr);
+ }
+
+ return;
+ }
+
+ std::pair<Dictionary::Ptr, Dictionary::Ptr> stats = listener->GetStatus();
+ Dictionary::Ptr status = stats.first;
+ int numConnEndpoints = status->Get("num_conn_endpoints");
+ int numNotConnEndpoints = status->Get("num_not_conn_endpoints");
+
+ ServiceState state;
+ String output = "Icinga 2 Cluster";
+
+ if (numNotConnEndpoints > 0) {
+ output += " Problem: " + Convert::ToString(numNotConnEndpoints) + " endpoints are not connected.";
+ output += "\n(" + FormatArray(status->Get("not_conn_endpoints")) + ")";
+
+ state = ServiceCritical;
+ } else {
+ output += " OK: " + Convert::ToString(numConnEndpoints) + " endpoints are connected.";
+ output += "\n(" + FormatArray(status->Get("conn_endpoints")) + ")";
+
+ state = ServiceOK;
+ }
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ /* use feature stats perfdata */
+ std::pair<Dictionary::Ptr, Array::Ptr> feature_stats = CIB::GetFeatureStats();
+ cr->SetPerformanceData(feature_stats.second);
+
+ cr->SetCommand(commandName);
+ cr->SetState(state);
+ cr->SetOutput(output);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
+
+String ClusterCheckTask::FormatArray(const Array::Ptr& arr)
+{
+ bool first = true;
+ String str;
+
+ if (arr) {
+ ObjectLock olock(arr);
+ for (const Value& value : arr) {
+ if (first)
+ first = false;
+ else
+ str += ", ";
+
+ str += Convert::ToString(value);
+ }
+ }
+
+ return str;
+}
diff --git a/lib/methods/clusterchecktask.hpp b/lib/methods/clusterchecktask.hpp
new file mode 100644
index 0000000..16ee8a5
--- /dev/null
+++ b/lib/methods/clusterchecktask.hpp
@@ -0,0 +1,29 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CLUSTERCHECKTASK_H
+#define CLUSTERCHECKTASK_H
+
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+/**
+ * Cluster check type.
+ *
+ * @ingroup methods
+ */
+class ClusterCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ ClusterCheckTask();
+ static String FormatArray(const Array::Ptr& arr);
+};
+
+}
+
+#endif /* CLUSTERCHECKTASK_H */
diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp
new file mode 100644
index 0000000..fd52534
--- /dev/null
+++ b/lib/methods/clusterzonechecktask.cpp
@@ -0,0 +1,218 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/clusterzonechecktask.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "remote/apilistener.hpp"
+#include "remote/endpoint.hpp"
+#include "remote/zone.hpp"
+#include "base/function.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ ApiListener::Ptr listener = ApiListener::GetInstance();
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+ String commandName = command->GetName();
+
+ if (!listener) {
+ String output = "No API listener is configured for this instance.";
+ ServiceState state = ServiceUnknown;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetCommand(commandName);
+ cr->SetOutput(output);
+ cr->SetState(state);
+ checkable->ProcessCheckResult(cr);
+ }
+
+ return;
+ }
+
+ Value raw_command = command->GetCommandLine();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", command);
+
+ String zoneName = MacroProcessor::ResolveMacros("$cluster_zone$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ String missingLagWarning;
+ String missingLagCritical;
+
+ double lagWarning = MacroProcessor::ResolveMacros("$cluster_lag_warning$", resolvers, checkable->GetLastCheckResult(),
+ &missingLagWarning, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ double lagCritical = MacroProcessor::ResolveMacros("$cluster_lag_critical$", resolvers, checkable->GetLastCheckResult(),
+ &missingLagCritical, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ if (zoneName.IsEmpty()) {
+ String output = "Macro 'cluster_zone' must be set.";
+ ServiceState state = ServiceUnknown;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetCommand(commandName);
+ cr->SetOutput(output);
+ cr->SetState(state);
+ checkable->ProcessCheckResult(cr);
+ }
+
+ return;
+ }
+
+ Zone::Ptr zone = Zone::GetByName(zoneName);
+
+ if (!zone) {
+ String output = "Zone '" + zoneName + "' does not exist.";
+ ServiceState state = ServiceUnknown;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetCommand(commandName);
+ cr->SetOutput(output);
+ cr->SetState(state);
+ checkable->ProcessCheckResult(cr);
+ }
+ return;
+ }
+
+ bool connected = false;
+ double zoneLag = 0;
+
+ double lastMessageSent = 0;
+ double lastMessageReceived = 0;
+ double messagesSentPerSecond = 0;
+ double messagesReceivedPerSecond = 0;
+ double bytesSentPerSecond = 0;
+ double bytesReceivedPerSecond = 0;
+
+ {
+ auto endpoints (zone->GetEndpoints());
+
+ for (const Endpoint::Ptr& endpoint : endpoints) {
+ if (endpoint->GetConnected())
+ connected = true;
+
+ double eplag = ApiListener::CalculateZoneLag(endpoint);
+
+ if (eplag > 0 && eplag > zoneLag)
+ zoneLag = eplag;
+
+ if (endpoint->GetLastMessageSent() > lastMessageSent)
+ lastMessageSent = endpoint->GetLastMessageSent();
+
+ if (endpoint->GetLastMessageReceived() > lastMessageReceived)
+ lastMessageReceived = endpoint->GetLastMessageReceived();
+
+ messagesSentPerSecond += endpoint->GetMessagesSentPerSecond();
+ messagesReceivedPerSecond += endpoint->GetMessagesReceivedPerSecond();
+ bytesSentPerSecond += endpoint->GetBytesSentPerSecond();
+ bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond();
+ }
+
+ if (!connected && endpoints.size() == 1u && *endpoints.begin() == Endpoint::GetLocalEndpoint()) {
+ connected = true;
+ }
+ }
+
+ ServiceState state;
+ String output;
+
+ if (connected) {
+ state = ServiceOK;
+ output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag);
+
+ /* Check whether the thresholds have been resolved and compare them */
+ if (missingLagCritical.IsEmpty() && zoneLag > lagCritical) {
+ state = ServiceCritical;
+ output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)
+ + " greater than critical threshold: " + Utility::FormatDuration(lagCritical);
+ } else if (missingLagWarning.IsEmpty() && zoneLag > lagWarning) {
+ state = ServiceWarning;
+ output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)
+ + " greater than warning threshold: " + Utility::FormatDuration(lagWarning);
+ }
+ } else {
+ state = ServiceCritical;
+ output = "Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag);
+ }
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetCommand(commandName);
+ cr->SetState(state);
+ cr->SetOutput(output);
+ cr->SetPerformanceData(new Array({
+ new PerfdataValue("slave_lag", zoneLag, false, "s", lagWarning, lagCritical),
+ new PerfdataValue("last_messages_sent", lastMessageSent),
+ new PerfdataValue("last_messages_received", lastMessageReceived),
+ new PerfdataValue("sum_messages_sent_per_second", messagesSentPerSecond),
+ new PerfdataValue("sum_messages_received_per_second", messagesReceivedPerSecond),
+ new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond),
+ new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond)
+ }));
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
diff --git a/lib/methods/clusterzonechecktask.hpp b/lib/methods/clusterzonechecktask.hpp
new file mode 100644
index 0000000..2af442c
--- /dev/null
+++ b/lib/methods/clusterzonechecktask.hpp
@@ -0,0 +1,28 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef CLUSTERZONECHECKTASK_H
+#define CLUSTERZONECHECKTASK_H
+
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+/**
+ * Cluster zone check type.
+ *
+ * @ingroup methods
+ */
+class ClusterZoneCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ ClusterZoneCheckTask();
+};
+
+}
+
+#endif /* CLUSTERZONECHECKTASK_H */
diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp
new file mode 100644
index 0000000..905a022
--- /dev/null
+++ b/lib/methods/dummychecktask.cpp
@@ -0,0 +1,75 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef _WIN32
+# include <stdlib.h>
+#endif /* _WIN32 */
+#include "methods/dummychecktask.hpp"
+#include "icinga/pluginutility.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/convert.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", command);
+
+ int dummyState = MacroProcessor::ResolveMacros("$dummy_state$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ String dummyText = MacroProcessor::ResolveMacros("$dummy_text$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ /* Parse output and performance data. */
+ std::pair<String, String> co = PluginUtility::ParseCheckOutput(dummyText);
+
+ double now = Utility::GetTime();
+ String commandName = command->GetName();
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = dummyText;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = dummyState;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetOutput(co.first);
+ cr->SetPerformanceData(PluginUtility::SplitPerfdata(co.second));
+ cr->SetState(PluginUtility::ExitStatusToState(dummyState));
+ cr->SetExitStatus(dummyState);
+ cr->SetExecutionStart(now);
+ cr->SetExecutionEnd(now);
+ cr->SetCommand(commandName);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
diff --git a/lib/methods/dummychecktask.hpp b/lib/methods/dummychecktask.hpp
new file mode 100644
index 0000000..621bbfb
--- /dev/null
+++ b/lib/methods/dummychecktask.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DUMMYCHECKTASK_H
+#define DUMMYCHECKTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional check types. Implements the "dummy" check type.
+ *
+ * @ingroup methods
+ */
+class DummyCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ DummyCheckTask();
+};
+
+}
+
+#endif /* DUMMYCHECKTASK_H */
diff --git a/lib/methods/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp
new file mode 100644
index 0000000..47707f2
--- /dev/null
+++ b/lib/methods/exceptionchecktask.cpp
@@ -0,0 +1,41 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef _WIN32
+# include <stdlib.h>
+#endif /* _WIN32 */
+#include "methods/exceptionchecktask.hpp"
+#include "base/utility.hpp"
+#include "base/convert.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+#include "base/exception.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ ScriptError scriptError = ScriptError("Test") << boost::errinfo_api_function("Test");
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = scriptError.what();
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = 3;
+
+ Checkable::ExecuteCommandProcessFinishedHandler("", pr);
+ } else {
+ BOOST_THROW_EXCEPTION(ScriptError("Test") << boost::errinfo_api_function("Test"));
+ }
+}
diff --git a/lib/methods/exceptionchecktask.hpp b/lib/methods/exceptionchecktask.hpp
new file mode 100644
index 0000000..09db104
--- /dev/null
+++ b/lib/methods/exceptionchecktask.hpp
@@ -0,0 +1,29 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef EXCEPTIONCHECKTASK_H
+#define EXCEPTIONCHECKTASK_H
+
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional check types. Implements the "exception" check type.
+ *
+ * @ingroup methods
+ */
+class ExceptionCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ ExceptionCheckTask();
+};
+
+}
+
+#endif /* EXCEPTIONCHECKTASK_H */
diff --git a/lib/methods/i2-methods.hpp b/lib/methods/i2-methods.hpp
new file mode 100644
index 0000000..ffd8002
--- /dev/null
+++ b/lib/methods/i2-methods.hpp
@@ -0,0 +1,15 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef I2METHODS_H
+#define I2METHODS_H
+
+/**
+ * @defgroup methods Icinga methods
+ *
+ * The methods library implements methods for various task (e.g. checks, event
+ * handlers, etc.).
+ */
+
+#include "base/i2-base.hpp"
+
+#endif /* I2METHODS_H */
diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp
new file mode 100644
index 0000000..d3eae1f
--- /dev/null
+++ b/lib/methods/icingachecktask.cpp
@@ -0,0 +1,209 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/icingachecktask.hpp"
+#include "icinga/cib.hpp"
+#include "icinga/service.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "icinga/clusterevents.hpp"
+#include "icinga/checkable.hpp"
+#include "remote/apilistener.hpp"
+#include "base/application.hpp"
+#include "base/objectlock.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/function.hpp"
+#include "base/configtype.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", command);
+
+ String missingIcingaMinVersion;
+
+ String icingaMinVersion = MacroProcessor::ResolveMacros("$icinga_min_version$", resolvers, checkable->GetLastCheckResult(),
+ &missingIcingaMinVersion, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ double interval = Utility::GetTime() - Application::GetStartTime();
+
+ if (interval > 60)
+ interval = 60;
+
+ /* use feature stats perfdata */
+ std::pair<Dictionary::Ptr, Array::Ptr> feature_stats = CIB::GetFeatureStats();
+
+ Array::Ptr perfdata = feature_stats.second;
+
+ perfdata->Add(new PerfdataValue("active_host_checks", CIB::GetActiveHostChecksStatistics(interval) / interval));
+ perfdata->Add(new PerfdataValue("passive_host_checks", CIB::GetPassiveHostChecksStatistics(interval) / interval));
+ perfdata->Add(new PerfdataValue("active_host_checks_1min", CIB::GetActiveHostChecksStatistics(60)));
+ perfdata->Add(new PerfdataValue("passive_host_checks_1min", CIB::GetPassiveHostChecksStatistics(60)));
+ perfdata->Add(new PerfdataValue("active_host_checks_5min", CIB::GetActiveHostChecksStatistics(60 * 5)));
+ perfdata->Add(new PerfdataValue("passive_host_checks_5min", CIB::GetPassiveHostChecksStatistics(60 * 5)));
+ perfdata->Add(new PerfdataValue("active_host_checks_15min", CIB::GetActiveHostChecksStatistics(60 * 15)));
+ perfdata->Add(new PerfdataValue("passive_host_checks_15min", CIB::GetPassiveHostChecksStatistics(60 * 15)));
+
+ perfdata->Add(new PerfdataValue("active_service_checks", CIB::GetActiveServiceChecksStatistics(interval) / interval));
+ perfdata->Add(new PerfdataValue("passive_service_checks", CIB::GetPassiveServiceChecksStatistics(interval) / interval));
+ perfdata->Add(new PerfdataValue("active_service_checks_1min", CIB::GetActiveServiceChecksStatistics(60)));
+ perfdata->Add(new PerfdataValue("passive_service_checks_1min", CIB::GetPassiveServiceChecksStatistics(60)));
+ perfdata->Add(new PerfdataValue("active_service_checks_5min", CIB::GetActiveServiceChecksStatistics(60 * 5)));
+ perfdata->Add(new PerfdataValue("passive_service_checks_5min", CIB::GetPassiveServiceChecksStatistics(60 * 5)));
+ perfdata->Add(new PerfdataValue("active_service_checks_15min", CIB::GetActiveServiceChecksStatistics(60 * 15)));
+ perfdata->Add(new PerfdataValue("passive_service_checks_15min", CIB::GetPassiveServiceChecksStatistics(60 * 15)));
+
+ perfdata->Add(new PerfdataValue("current_pending_callbacks", Application::GetTP().GetPending()));
+ perfdata->Add(new PerfdataValue("current_concurrent_checks", Checkable::CurrentConcurrentChecks.load()));
+ perfdata->Add(new PerfdataValue("remote_check_queue", ClusterEvents::GetCheckRequestQueueSize()));
+
+ CheckableCheckStatistics scs = CIB::CalculateServiceCheckStats();
+
+ perfdata->Add(new PerfdataValue("min_latency", scs.min_latency));
+ perfdata->Add(new PerfdataValue("max_latency", scs.max_latency));
+ perfdata->Add(new PerfdataValue("avg_latency", scs.avg_latency));
+ perfdata->Add(new PerfdataValue("min_execution_time", scs.min_execution_time));
+ perfdata->Add(new PerfdataValue("max_execution_time", scs.max_execution_time));
+ perfdata->Add(new PerfdataValue("avg_execution_time", scs.avg_execution_time));
+
+ ServiceStatistics ss = CIB::CalculateServiceStats();
+
+ perfdata->Add(new PerfdataValue("num_services_ok", ss.services_ok));
+ perfdata->Add(new PerfdataValue("num_services_warning", ss.services_warning));
+ perfdata->Add(new PerfdataValue("num_services_critical", ss.services_critical));
+ perfdata->Add(new PerfdataValue("num_services_unknown", ss.services_unknown));
+ perfdata->Add(new PerfdataValue("num_services_pending", ss.services_pending));
+ perfdata->Add(new PerfdataValue("num_services_unreachable", ss.services_unreachable));
+ perfdata->Add(new PerfdataValue("num_services_flapping", ss.services_flapping));
+ perfdata->Add(new PerfdataValue("num_services_in_downtime", ss.services_in_downtime));
+ perfdata->Add(new PerfdataValue("num_services_acknowledged", ss.services_acknowledged));
+ perfdata->Add(new PerfdataValue("num_services_handled", ss.services_handled));
+ perfdata->Add(new PerfdataValue("num_services_problem", ss.services_problem));
+
+ double uptime = Application::GetUptime();
+ perfdata->Add(new PerfdataValue("uptime", uptime));
+
+ HostStatistics hs = CIB::CalculateHostStats();
+
+ perfdata->Add(new PerfdataValue("num_hosts_up", hs.hosts_up));
+ perfdata->Add(new PerfdataValue("num_hosts_down", hs.hosts_down));
+ perfdata->Add(new PerfdataValue("num_hosts_pending", hs.hosts_pending));
+ perfdata->Add(new PerfdataValue("num_hosts_unreachable", hs.hosts_unreachable));
+ perfdata->Add(new PerfdataValue("num_hosts_flapping", hs.hosts_flapping));
+ perfdata->Add(new PerfdataValue("num_hosts_in_downtime", hs.hosts_in_downtime));
+ perfdata->Add(new PerfdataValue("num_hosts_acknowledged", hs.hosts_acknowledged));
+ perfdata->Add(new PerfdataValue("num_hosts_handled", hs.hosts_handled));
+ perfdata->Add(new PerfdataValue("num_hosts_problem", hs.hosts_problem));
+
+ std::vector<Endpoint::Ptr> endpoints = ConfigType::GetObjectsByType<Endpoint>();
+
+ double lastMessageSent = 0;
+ double lastMessageReceived = 0;
+ double messagesSentPerSecond = 0;
+ double messagesReceivedPerSecond = 0;
+ double bytesSentPerSecond = 0;
+ double bytesReceivedPerSecond = 0;
+
+ for (const Endpoint::Ptr& endpoint : endpoints)
+ {
+ if (endpoint->GetLastMessageSent() > lastMessageSent)
+ lastMessageSent = endpoint->GetLastMessageSent();
+
+ if (endpoint->GetLastMessageReceived() > lastMessageReceived)
+ lastMessageReceived = endpoint->GetLastMessageReceived();
+
+ messagesSentPerSecond += endpoint->GetMessagesSentPerSecond();
+ messagesReceivedPerSecond += endpoint->GetMessagesReceivedPerSecond();
+ bytesSentPerSecond += endpoint->GetBytesSentPerSecond();
+ bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond();
+ }
+
+ perfdata->Add(new PerfdataValue("last_messages_sent", lastMessageSent));
+ perfdata->Add(new PerfdataValue("last_messages_received", lastMessageReceived));
+ perfdata->Add(new PerfdataValue("sum_messages_sent_per_second", messagesSentPerSecond));
+ perfdata->Add(new PerfdataValue("sum_messages_received_per_second", messagesReceivedPerSecond));
+ perfdata->Add(new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond));
+ perfdata->Add(new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond));
+
+ cr->SetPerformanceData(perfdata);
+ ServiceState state = ServiceOK;
+
+ String appVersion = Application::GetAppVersion();
+
+ String output = "Icinga 2 has been running for " + Utility::FormatDuration(uptime) +
+ ". Version: " + appVersion;
+
+ /* Indicate a warning if the last reload failed. */
+ double lastReloadFailed = Application::GetLastReloadFailed();
+
+ if (lastReloadFailed > 0) {
+ output += "; Last reload attempt failed at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", lastReloadFailed);
+ state =ServiceWarning;
+ }
+
+ /* Indicate a warning when the last synced config caused a stage validation error. */
+ ApiListener::Ptr listener = ApiListener::GetInstance();
+
+ if (listener) {
+ Dictionary::Ptr validationResult = listener->GetLastFailedZonesStageValidation();
+
+ if (validationResult) {
+ output += "; Last zone sync stage validation failed at "
+ + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", validationResult->Get("ts"));
+
+ state = ServiceWarning;
+ }
+ }
+
+ String parsedAppVersion = Utility::ParseVersion(appVersion);
+
+ /* Return an error if the version is less than specified (optional). */
+ if (missingIcingaMinVersion.IsEmpty() && !icingaMinVersion.IsEmpty() && Utility::CompareVersion(icingaMinVersion, parsedAppVersion) < 0) {
+ output += "; Minimum version " + icingaMinVersion + " is not installed.";
+ state = ServiceCritical;
+ }
+
+ String commandName = command->GetName();
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetState(state);
+ cr->SetOutput(output);
+ cr->SetCommand(commandName);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
diff --git a/lib/methods/icingachecktask.hpp b/lib/methods/icingachecktask.hpp
new file mode 100644
index 0000000..93def62
--- /dev/null
+++ b/lib/methods/icingachecktask.hpp
@@ -0,0 +1,29 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef ICINGACHECKTASK_H
+#define ICINGACHECKTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+/**
+ * Icinga check type.
+ *
+ * @ingroup methods
+ */
+class IcingaCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ IcingaCheckTask();
+};
+
+}
+
+#endif /* ICINGACHECKTASK_H */
diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp
new file mode 100644
index 0000000..8516d70
--- /dev/null
+++ b/lib/methods/ifwapichecktask.cpp
@@ -0,0 +1,531 @@
+/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */
+
+#ifndef _WIN32
+# include <stdlib.h>
+#endif /* _WIN32 */
+#include "methods/ifwapichecktask.hpp"
+#include "methods/pluginchecktask.hpp"
+#include "icinga/checkresult-ti.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "icinga/pluginutility.hpp"
+#include "base/base64.hpp"
+#include "base/defer.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/convert.hpp"
+#include "base/function.hpp"
+#include "base/io-engine.hpp"
+#include "base/json.hpp"
+#include "base/logger.hpp"
+#include "base/shared.hpp"
+#include "base/tcpsocket.hpp"
+#include "base/tlsstream.hpp"
+#include "remote/apilistener.hpp"
+#include "remote/url.hpp"
+#include <boost/asio.hpp>
+#include <boost/beast/core.hpp>
+#include <boost/beast/http.hpp>
+#include <boost/system/system_error.hpp>
+#include <exception>
+#include <set>
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+static void ReportIfwCheckResult(
+ const Checkable::Ptr& checkable, const Value& cmdLine, const CheckResult::Ptr& cr,
+ const String& output, double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr
+)
+{
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = perfdata ? output + " |" + String(perfdata->Join(" ")) : output;
+ pr.ExecutionStart = start;
+ pr.ExecutionEnd = end;
+ pr.ExitStatus = exitcode;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(cmdLine, pr);
+ } else {
+ auto splittedPerfdata (perfdata);
+
+ if (perfdata) {
+ splittedPerfdata = new Array();
+ ObjectLock oLock (perfdata);
+
+ for (String pv : perfdata) {
+ PluginUtility::SplitPerfdata(pv)->CopyTo(splittedPerfdata);
+ }
+ }
+
+ cr->SetOutput(output);
+ cr->SetPerformanceData(splittedPerfdata);
+ cr->SetState((ServiceState)exitcode);
+ cr->SetExitStatus(exitcode);
+ cr->SetExecutionStart(start);
+ cr->SetExecutionEnd(end);
+ cr->SetCommand(cmdLine);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
+
+static void ReportIfwCheckResult(
+ boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Value& cmdLine,
+ const CheckResult::Ptr& cr, const String& output, double start
+)
+{
+ double end = Utility::GetTime();
+ CpuBoundWork cbw (yc);
+
+ ReportIfwCheckResult(checkable, cmdLine, cr, output, start, end);
+}
+
+static const char* GetUnderstandableError(const std::exception& ex)
+{
+ auto se (dynamic_cast<const boost::system::system_error*>(&ex));
+
+ if (se && se->code() == boost::asio::error::operation_aborted) {
+ return "Timeout exceeded";
+ }
+
+ return ex.what();
+}
+
+static void DoIfwNetIo(
+ boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine,
+ const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& san, const String& psPort,
+ AsioTlsStream& conn, boost::beast::http::request<boost::beast::http::string_body>& req, double start
+)
+{
+ namespace http = boost::beast::http;
+
+ boost::beast::flat_buffer buf;
+ http::response<http::string_body> resp;
+
+ try {
+ Connect(conn.lowest_layer(), psHost, psPort, yc);
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(
+ yc, checkable, cmdLine, cr,
+ "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex),
+ start
+ );
+ return;
+ }
+
+ auto& sslConn (conn.next_layer());
+
+ try {
+ sslConn.async_handshake(conn.next_layer().client, yc);
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(
+ yc, checkable, cmdLine, cr,
+ "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san
+ + "') port '" + psPort + "' failed: " + GetUnderstandableError(ex),
+ start
+ );
+ return;
+ }
+
+ if (!sslConn.IsVerifyOK()) {
+ auto cert (sslConn.GetPeerCertificate());
+ Value cn;
+
+ try {
+ cn = GetCertificateCN(cert);
+ } catch (const std::exception&) {
+ }
+
+ ReportIfwCheckResult(
+ yc, checkable, cmdLine, cr,
+ "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + san + "'; CN: "
+ + (cn.IsString() ? "'" + cn + "'" : "N/A") + ") port '" + psPort + "': " + sslConn.GetVerifyError(),
+ start
+ );
+ return;
+ }
+
+ try {
+ http::async_write(conn, req, yc);
+ conn.async_flush(yc);
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(
+ yc, checkable, cmdLine, cr,
+ "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex),
+ start
+ );
+ return;
+ }
+
+ try {
+ http::async_read(conn, buf, resp, yc);
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(
+ yc, checkable, cmdLine, cr,
+ "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex),
+ start
+ );
+ return;
+ }
+
+ double end = Utility::GetTime();
+
+ {
+ boost::system::error_code ec;
+ sslConn.async_shutdown(yc[ec]);
+ }
+
+ CpuBoundWork cbw (yc);
+ Value jsonRoot;
+
+ try {
+ jsonRoot = JsonDecode(resp.body());
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end
+ );
+ return;
+ }
+
+ if (!jsonRoot.IsObjectType<Dictionary>()) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Got JSON, but not an object, from IfW API on host '"
+ + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot),
+ start, end
+ );
+ return;
+ }
+
+ Value jsonBranch;
+
+ if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Missing ." + psCommand + " in JSON object from IfW API on host '"
+ + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot),
+ start, end
+ );
+ return;
+ }
+
+ if (!jsonBranch.IsObjectType<Dictionary>()) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "." + psCommand + " in JSON from IfW API on host '"
+ + psHost + "' port '" + psPort + "' is not an object: " + JsonEncode(jsonBranch),
+ start, end
+ );
+ return;
+ }
+
+ Dictionary::Ptr result = jsonBranch;
+
+ Value exitcode;
+
+ if (!result->Get("exitcode", &exitcode)) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '"
+ + psHost + "' port '" + psPort + "': " + JsonEncode(result),
+ start, end
+ );
+ return;
+ }
+
+ static const std::set<double> exitcodes {ServiceOK, ServiceWarning, ServiceCritical, ServiceUnknown};
+ static const auto exitcodeList (Array::FromSet(exitcodes)->Join(", "));
+
+ if (!exitcode.IsNumber() || exitcodes.find(exitcode) == exitcodes.end()) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Got bad exitcode " + JsonEncode(exitcode) + " from IfW API on host '" + psHost + "' port '" + psPort
+ + "', expected one of: " + exitcodeList,
+ start, end
+ );
+ return;
+ }
+
+ auto perfdataVal (result->Get("perfdata"));
+ Array::Ptr perfdata;
+
+ try {
+ perfdata = perfdataVal;
+ } catch (const std::exception&) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Got bad perfdata " + JsonEncode(perfdataVal) + " from IfW API on host '"
+ + psHost + "' port '" + psPort + "', expected an array",
+ start, end
+ );
+ return;
+ }
+
+ if (perfdata) {
+ ObjectLock oLock (perfdata);
+
+ for (auto& pv : perfdata) {
+ if (!pv.IsString()) {
+ ReportIfwCheckResult(
+ checkable, cmdLine, cr,
+ "Got bad perfdata value " + JsonEncode(perfdata) + " from IfW API on host '"
+ + psHost + "' port '" + psPort + "', expected an array of strings",
+ start, end
+ );
+ return;
+ }
+ }
+ }
+
+ ReportIfwCheckResult(checkable, cmdLine, cr, result->Get("checkresult"), start, end, exitcode, perfdata);
+}
+
+void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ namespace asio = boost::asio;
+ namespace http = boost::beast::http;
+ using http::field;
+
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ // We're going to just resolve macros for the actual check execution happening elsewhere
+ if (resolvedMacros && !useResolvedMacros) {
+ auto commandEndpoint (checkable->GetCommandEndpoint());
+
+ // There's indeed a command endpoint, obviously for the actual check execution
+ if (commandEndpoint) {
+ // But it doesn't have this function, yet ("ifw-api-check-command")
+ if (!(commandEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand)) {
+ // Assume "ifw-api-check-command" has been imported into a check command which can also work
+ // based on "plugin-check-command", delegate respectively and hope for the best
+ PluginCheckTask::ScriptFunc(checkable, cr, resolvedMacros, useResolvedMacros);
+ return;
+ }
+ }
+ }
+
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+ auto lcr (checkable->GetLastCheckResult());
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", command);
+
+ auto resolveMacros ([&resolvers, &lcr, &resolvedMacros, useResolvedMacros](const char* macros) -> Value {
+ return MacroProcessor::ResolveMacros(
+ macros, resolvers, lcr, nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros
+ );
+ });
+
+ String psCommand = resolveMacros("$ifw_api_command$");
+ Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$");
+ String psHost = resolveMacros("$ifw_api_host$");
+ String psPort = resolveMacros("$ifw_api_port$");
+ String expectedSan = resolveMacros("$ifw_api_expected_san$");
+ String cert = resolveMacros("$ifw_api_cert$");
+ String key = resolveMacros("$ifw_api_key$");
+ String ca = resolveMacros("$ifw_api_ca$");
+ String crl = resolveMacros("$ifw_api_crl$");
+ String username = resolveMacros("$ifw_api_username$");
+ String password = resolveMacros("$ifw_api_password$");
+
+ Dictionary::Ptr params = new Dictionary();
+
+ if (arguments) {
+ ObjectLock oLock (arguments);
+ Array::Ptr emptyCmd = new Array();
+
+ for (auto& kv : arguments) {
+ Dictionary::Ptr argSpec;
+
+ if (kv.second.IsObjectType<Dictionary>()) {
+ argSpec = Dictionary::Ptr(kv.second)->ShallowClone();
+ } else {
+ argSpec = new Dictionary({{ "value", kv.second }});
+ }
+
+ // See default branch of below switch
+ argSpec->Set("repeat_key", false);
+
+ {
+ ObjectLock oLock (argSpec);
+
+ for (auto& kv : argSpec) {
+ if (kv.second.GetType() == ValueObject) {
+ auto now (Utility::GetTime());
+
+ ReportIfwCheckResult(
+ checkable, command->GetName(), cr,
+ "$ifw_api_arguments$ may not directly contain objects (especially functions).", now, now
+ );
+
+ return;
+ }
+ }
+ }
+
+ /* MacroProcessor::ResolveArguments() converts
+ *
+ * [ "check_example" ]
+ * and
+ * {
+ * "-f" = { set_if = "$example_flag$" }
+ * "-a" = "$example_arg$"
+ * }
+ *
+ * to
+ *
+ * [ "check_example", "-f", "-a", "X" ]
+ *
+ * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ].
+ */
+ Array::Ptr arg = MacroProcessor::ResolveArguments(
+ emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, lcr, resolvedMacros, useResolvedMacros
+ );
+
+ switch (arg ? arg->GetLength() : 0) {
+ case 0:
+ break;
+ case 1: // [ "-f" ]
+ params->Set(arg->Get(0), true);
+ break;
+ case 2: // [ "-a", "X" ]
+ params->Set(arg->Get(0), arg->Get(1));
+ break;
+ default: { // [ "-a", "X", "Y" ]
+ auto k (arg->Get(0));
+
+ arg->Remove(0);
+ params->Set(k, arg);
+ }
+ }
+ }
+ }
+
+ auto checkTimeout (command->GetTimeout());
+ auto checkableTimeout (checkable->GetCheckTimeout());
+
+ if (!checkableTimeout.IsEmpty())
+ checkTimeout = checkableTimeout;
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ if (psHost.IsEmpty()) {
+ psHost = "localhost";
+ }
+
+ if (expectedSan.IsEmpty()) {
+ expectedSan = IcingaApplication::GetInstance()->GetNodeName();
+ }
+
+ if (cert.IsEmpty()) {
+ cert = ApiListener::GetDefaultCertPath();
+ }
+
+ if (key.IsEmpty()) {
+ key = ApiListener::GetDefaultKeyPath();
+ }
+
+ if (ca.IsEmpty()) {
+ ca = ApiListener::GetDefaultCaPath();
+ }
+
+ Url::Ptr uri = new Url();
+
+ uri->SetPath({ "v1", "checker" });
+ uri->SetQuery({{ "command", psCommand }});
+
+ static const auto userAgent ("Icinga/" + Application::GetAppVersion());
+ auto relative (uri->Format());
+ auto body (JsonEncode(params));
+ auto req (Shared<http::request<http::string_body>>::Make());
+
+ req->method(http::verb::post);
+ req->target(relative);
+ req->set(field::accept, "application/json");
+ req->set(field::content_type, "application/json");
+ req->set(field::host, expectedSan + ":" + psPort);
+ req->set(field::user_agent, userAgent);
+ req->body() = body;
+ req->content_length(req->body().size());
+
+ static const auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower());
+
+ Array::Ptr cmdLine = new Array({
+ "curl", "--verbose", curlTlsMinVersion, "--fail-with-body",
+ "--connect-to", expectedSan + ":" + psPort + ":" + psHost + ":" + psPort,
+ "--ciphers", DEFAULT_TLS_CIPHERS,
+ "--cert", cert,
+ "--key", key,
+ "--cacert", ca,
+ "--request", "POST",
+ "--url", "https://" + expectedSan + ":" + psPort + relative,
+ "--user-agent", userAgent,
+ "--header", "Accept: application/json",
+ "--header", "Content-Type: application/json",
+ "--data-raw", body
+ });
+
+ if (!crl.IsEmpty()) {
+ cmdLine->Add("--crlfile");
+ cmdLine->Add(crl);
+ }
+
+ if (!username.IsEmpty() && !password.IsEmpty()) {
+ auto authn (username + ":" + password);
+
+ req->set(field::authorization, "Basic " + Base64::Encode(authn));
+ cmdLine->Add("--user");
+ cmdLine->Add(authn);
+ }
+
+ auto& io (IoEngine::Get().GetIoContext());
+ auto strand (Shared<asio::io_context::strand>::Make(io));
+ Shared<asio::ssl::context>::Ptr ctx;
+ double start = Utility::GetTime();
+
+ try {
+ ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo());
+ } catch (const std::exception& ex) {
+ ReportIfwCheckResult(checkable, cmdLine, cr, ex.what(), start, Utility::GetTime());
+ return;
+ }
+
+ auto conn (Shared<AsioTlsStream>::Make(io, *ctx, expectedSan));
+
+ IoEngine::SpawnCoroutine(
+ *strand,
+ [strand, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](asio::yield_context yc) {
+ Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)),
+ [&conn, &checkable](boost::asio::yield_context yc) {
+ Log(LogNotice, "IfwApiCheckTask")
+ << "Timeout while checking " << checkable->GetReflectionType()->GetName()
+ << " '" << checkable->GetName() << "', cancelling attempt";
+
+ boost::system::error_code ec;
+ conn->lowest_layer().cancel(ec);
+ }
+ );
+
+ Defer cancelTimeout ([&timeout]() { timeout->Cancel(); });
+
+ DoIfwNetIo(yc, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, *conn, *req, start);
+ }
+ );
+}
diff --git a/lib/methods/ifwapichecktask.hpp b/lib/methods/ifwapichecktask.hpp
new file mode 100644
index 0000000..3932733
--- /dev/null
+++ b/lib/methods/ifwapichecktask.hpp
@@ -0,0 +1,27 @@
+/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */
+
+#pragma once
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Executes checks via Icinga for Windows API.
+ *
+ * @ingroup methods
+ */
+class IfwApiCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ IfwApiCheckTask();
+};
+
+}
diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf
new file mode 100644
index 0000000..6249692
--- /dev/null
+++ b/lib/methods/methods-itl.conf
@@ -0,0 +1,90 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+System.assert(Internal.run_with_activation_context(function() {
+ template CheckCommand "icinga-check-command" use (IcingaCheck = Internal.IcingaCheck) {
+ execute = IcingaCheck
+
+ vars.icinga_min_version = ""
+ }
+
+ template CheckCommand "cluster-check-command" use (ClusterCheck = Internal.ClusterCheck) {
+ execute = ClusterCheck
+ }
+
+ template CheckCommand "cluster-zone-check-command" use (ClusterZoneCheck = Internal.ClusterZoneCheck) {
+ execute = ClusterZoneCheck
+ }
+
+ template CheckCommand "plugin-check-command" use (PluginCheck = Internal.PluginCheck) default {
+ execute = PluginCheck
+ }
+
+ template NotificationCommand "plugin-notification-command" use (PluginNotification = Internal.PluginNotification) default {
+ execute = PluginNotification
+ }
+
+ template EventCommand "plugin-event-command" use (PluginEvent = Internal.PluginEvent) default {
+ execute = PluginEvent
+ }
+
+ template CheckCommand "dummy-check-command" use (DummyCheck = Internal.DummyCheck) {
+ execute = DummyCheck
+ }
+
+ template CheckCommand "random-check-command" use (RandomCheck = Internal.RandomCheck) {
+ execute = RandomCheck
+ }
+
+ template CheckCommand "exception-check-command" use (ExceptionCheck = Internal.ExceptionCheck) {
+ execute = ExceptionCheck
+ }
+
+ template CheckCommand "null-check-command" use (NullCheck = Internal.NullCheck) {
+ execute = NullCheck
+ }
+
+ template CheckCommand "ifw-api-check-command" use (IfwApiCheck = Internal.IfwApiCheck) {
+ execute = IfwApiCheck
+ }
+
+ template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) {
+ execute = NullEvent
+ }
+
+ template TimePeriod "empty-timeperiod" use (EmptyTimePeriod = Internal.EmptyTimePeriod) {
+ update = EmptyTimePeriod
+ }
+
+ template TimePeriod "even-minutes-timeperiod" use (EvenMinutesTimePeriod = Internal.EvenMinutesTimePeriod) {
+ update = EvenMinutesTimePeriod
+ }
+
+ template CheckCommand "sleep-check-command" use (SleepCheck = Internal.SleepCheck) {
+ execute = SleepCheck
+
+ vars.sleep_time = 1s
+ }
+}))
+
+var methods = [
+ "IcingaCheck",
+ "IfwApiCheck",
+ "ClusterCheck",
+ "ClusterZoneCheck",
+ "PluginCheck",
+ "ClrCheck",
+ "PluginNotification",
+ "PluginEvent",
+ "DummyCheck",
+ "RandomCheck",
+ "ExceptionCheck",
+ "NullCheck",
+ "NullEvent",
+ "EmptyTimePeriod",
+ "EvenMinutesTimePeriod",
+ "SleepCheck"
+]
+
+for (method in methods) {
+ Internal.remove(method)
+}
diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp
new file mode 100644
index 0000000..ee66029
--- /dev/null
+++ b/lib/methods/nullchecktask.cpp
@@ -0,0 +1,50 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef _WIN32
+# include <stdlib.h>
+#endif /* _WIN32 */
+#include "methods/nullchecktask.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/convert.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void NullCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ String output = "Hello from ";
+ output += IcingaApplication::GetInstance()->GetNodeName();
+ ServiceState state = ServiceOK;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler("", pr);
+ } else {
+ cr->SetOutput(output);
+ cr->SetPerformanceData(new Array({
+ new PerfdataValue("time", Convert::ToDouble(Utility::GetTime()))
+ }));
+ cr->SetState(state);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
diff --git a/lib/methods/nullchecktask.hpp b/lib/methods/nullchecktask.hpp
new file mode 100644
index 0000000..954cf8d
--- /dev/null
+++ b/lib/methods/nullchecktask.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef NULLCHECKTASK_H
+#define NULLCHECKTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional check types. Implements the "null" check type.
+ *
+ * @ingroup methods
+ */
+class NullCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ NullCheckTask();
+};
+
+}
+
+#endif /* NULLCHECKTASK_H */
diff --git a/lib/methods/nulleventtask.cpp b/lib/methods/nulleventtask.cpp
new file mode 100644
index 0000000..3c02f23
--- /dev/null
+++ b/lib/methods/nulleventtask.cpp
@@ -0,0 +1,26 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/nulleventtask.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros");
+
+void NullEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = "";
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = 0;
+
+ Checkable::ExecuteCommandProcessFinishedHandler("", pr);
+ }
+}
diff --git a/lib/methods/nulleventtask.hpp b/lib/methods/nulleventtask.hpp
new file mode 100644
index 0000000..153470f
--- /dev/null
+++ b/lib/methods/nulleventtask.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef NULLEVENTTASK_H
+#define NULLEVENTTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional event handler types. Implements the "null" event handler type.
+ *
+ * @ingroup methods
+ */
+class NullEventTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ NullEventTask();
+};
+
+}
+
+#endif /* NULLEVENTTASK_H */
diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp
new file mode 100644
index 0000000..b4749fb
--- /dev/null
+++ b/lib/methods/pluginchecktask.cpp
@@ -0,0 +1,89 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/pluginchecktask.hpp"
+#include "icinga/pluginutility.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "base/configtype.hpp"
+#include "base/logger.hpp"
+#include "base/function.hpp"
+#include "base/utility.hpp"
+#include "base/process.hpp"
+#include "base/convert.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", commandObj);
+
+ int timeout = commandObj->GetTimeout();
+
+ if (!checkable->GetCheckTimeout().IsEmpty())
+ timeout = checkable->GetCheckTimeout();
+
+ std::function<void(const Value& commandLine, const ProcessResult&)> callback;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ callback = Checkable::ExecuteCommandProcessFinishedHandler;
+ } else {
+ callback = [checkable, cr](const Value& commandLine, const ProcessResult& pr) {
+ ProcessFinishedHandler(checkable, cr, commandLine, pr);
+ };
+ }
+
+ PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(),
+ resolvers, resolvedMacros, useResolvedMacros, timeout, callback);
+
+ if (!resolvedMacros || useResolvedMacros) {
+ Checkable::CurrentConcurrentChecks.fetch_add(1);
+ Checkable::IncreasePendingChecks();
+ }
+}
+
+void PluginCheckTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr)
+{
+ Checkable::CurrentConcurrentChecks.fetch_sub(1);
+ Checkable::DecreasePendingChecks();
+
+ if (pr.ExitStatus > 3) {
+ Process::Arguments parguments = Process::PrepareCommand(commandLine);
+ Log(LogWarning, "PluginCheckTask")
+ << "Check command for object '" << checkable->GetName() << "' (PID: " << pr.PID
+ << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code "
+ << pr.ExitStatus << ", output: " << pr.Output;
+ }
+
+ String output = pr.Output.Trim();
+
+ std::pair<String, String> co = PluginUtility::ParseCheckOutput(output);
+ cr->SetCommand(commandLine);
+ cr->SetOutput(co.first);
+ cr->SetPerformanceData(PluginUtility::SplitPerfdata(co.second));
+ cr->SetState(PluginUtility::ExitStatusToState(pr.ExitStatus));
+ cr->SetExitStatus(pr.ExitStatus);
+ cr->SetExecutionStart(pr.ExecutionStart);
+ cr->SetExecutionEnd(pr.ExecutionEnd);
+
+ checkable->ProcessCheckResult(cr);
+}
diff --git a/lib/methods/pluginchecktask.hpp b/lib/methods/pluginchecktask.hpp
new file mode 100644
index 0000000..a4fc3a3
--- /dev/null
+++ b/lib/methods/pluginchecktask.hpp
@@ -0,0 +1,33 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef PLUGINCHECKTASK_H
+#define PLUGINCHECKTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "base/process.hpp"
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+/**
+ * Implements service checks based on external plugins.
+ *
+ * @ingroup methods
+ */
+class PluginCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ PluginCheckTask();
+
+ static void ProcessFinishedHandler(const Checkable::Ptr& service,
+ const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr);
+};
+
+}
+
+#endif /* PLUGINCHECKTASK_H */
diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp
new file mode 100644
index 0000000..00efb6c
--- /dev/null
+++ b/lib/methods/plugineventtask.cpp
@@ -0,0 +1,61 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/plugineventtask.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "icinga/pluginutility.hpp"
+#include "base/configtype.hpp"
+#include "base/logger.hpp"
+#include "base/function.hpp"
+#include "base/utility.hpp"
+#include "base/process.hpp"
+#include "base/convert.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros");
+
+void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+
+ EventCommand::Ptr commandObj = EventCommand::ExecuteOverride ? EventCommand::ExecuteOverride : checkable->GetEventCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", commandObj);
+
+ int timeout = commandObj->GetTimeout();
+ std::function<void(const Value& commandLine, const ProcessResult&)> callback;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ callback = Checkable::ExecuteCommandProcessFinishedHandler;
+ } else {
+ callback = [checkable](const Value& commandLine, const ProcessResult& pr) { ProcessFinishedHandler(checkable, commandLine, pr); };
+ }
+
+ PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(),
+ resolvers, resolvedMacros, useResolvedMacros, timeout, callback);
+}
+
+void PluginEventTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr)
+{
+ if (pr.ExitStatus != 0) {
+ Process::Arguments parguments = Process::PrepareCommand(commandLine);
+ Log(LogWarning, "PluginEventTask")
+ << "Event command for object '" << checkable->GetName() << "' (PID: " << pr.PID
+ << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code "
+ << pr.ExitStatus << ", output: " << pr.Output;
+ }
+}
diff --git a/lib/methods/plugineventtask.hpp b/lib/methods/plugineventtask.hpp
new file mode 100644
index 0000000..8908a82
--- /dev/null
+++ b/lib/methods/plugineventtask.hpp
@@ -0,0 +1,33 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef PLUGINEVENTTASK_H
+#define PLUGINEVENTTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/process.hpp"
+
+namespace icinga
+{
+
+/**
+ * Implements event handlers based on external plugins.
+ *
+ * @ingroup methods
+ */
+class PluginEventTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ PluginEventTask();
+
+ static void ProcessFinishedHandler(const Checkable::Ptr& checkable,
+ const Value& commandLine, const ProcessResult& pr);
+};
+
+}
+
+#endif /* PLUGINEVENTTASK_H */
diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp
new file mode 100644
index 0000000..95911fa
--- /dev/null
+++ b/lib/methods/pluginnotificationtask.cpp
@@ -0,0 +1,123 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/pluginnotificationtask.hpp"
+#include "icinga/notification.hpp"
+#include "icinga/notificationcommand.hpp"
+#include "icinga/pluginutility.hpp"
+#include "icinga/service.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+#include "base/utility.hpp"
+#include "base/process.hpp"
+#include "base/convert.hpp"
+
+#ifdef __linux__
+# include <linux/binfmts.h>
+# include <unistd.h>
+
+# ifndef PAGE_SIZE
+// MAX_ARG_STRLEN is a multiple of PAGE_SIZE which is missing
+# define PAGE_SIZE getpagesize()
+# endif /* PAGE_SIZE */
+
+// Make e.g. the $host.output$ itself even 10% shorter to leave enough room
+// for e.g. --host-output= as in --host-output=$host.output$, but without int overflow
+const static auto l_MaxOutLen = MAX_ARG_STRLEN - MAX_ARG_STRLEN / 10u;
+#endif /* __linux__ */
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros");
+
+void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
+ const User::Ptr& user, const CheckResult::Ptr& cr, int itype,
+ const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros,
+ bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(notification);
+ REQUIRE_NOT_NULL(user);
+
+ NotificationCommand::Ptr commandObj = NotificationCommand::ExecuteOverride ? NotificationCommand::ExecuteOverride : notification->GetCommand();
+
+ auto type = static_cast<NotificationType>(itype);
+
+ Checkable::Ptr checkable = notification->GetCheckable();
+
+ Dictionary::Ptr notificationExtra = new Dictionary({
+ { "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types.
+ { "author", author },
+#ifdef __linux__
+ { "comment", comment.SubStr(0, l_MaxOutLen) }
+#else /* __linux__ */
+ { "comment", comment }
+#endif /* __linux__ */
+ });
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ resolvers.emplace_back("user", user);
+ resolvers.emplace_back("notification", notificationExtra);
+ resolvers.emplace_back("notification", notification);
+
+ if (service) {
+#ifdef __linux__
+ auto cr (service->GetLastCheckResult());
+
+ if (cr) {
+ auto output (cr->GetOutput());
+
+ if (output.GetLength() > l_MaxOutLen) {
+ resolvers.emplace_back("service", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
+ }
+ }
+#endif /* __linux__ */
+
+ resolvers.emplace_back("service", service);
+ }
+
+#ifdef __linux__
+ auto hcr (host->GetLastCheckResult());
+
+ if (hcr) {
+ auto output (hcr->GetOutput());
+
+ if (output.GetLength() > l_MaxOutLen) {
+ resolvers.emplace_back("host", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
+ }
+ }
+#endif /* __linux__ */
+
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", commandObj);
+
+ int timeout = commandObj->GetTimeout();
+ std::function<void(const Value& commandLine, const ProcessResult&)> callback;
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ callback = Checkable::ExecuteCommandProcessFinishedHandler;
+ } else {
+ callback = [checkable](const Value& commandline, const ProcessResult& pr) { ProcessFinishedHandler(checkable, commandline, pr); };
+ }
+
+ PluginUtility::ExecuteCommand(commandObj, checkable, cr, resolvers,
+ resolvedMacros, useResolvedMacros, timeout, callback);
+}
+
+void PluginNotificationTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr)
+{
+ if (pr.ExitStatus != 0) {
+ Process::Arguments parguments = Process::PrepareCommand(commandLine);
+ Log(LogWarning, "PluginNotificationTask")
+ << "Notification command for object '" << checkable->GetName() << "' (PID: " << pr.PID
+ << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code "
+ << pr.ExitStatus << ", output: " << pr.Output;
+ }
+}
diff --git a/lib/methods/pluginnotificationtask.hpp b/lib/methods/pluginnotificationtask.hpp
new file mode 100644
index 0000000..66d6539
--- /dev/null
+++ b/lib/methods/pluginnotificationtask.hpp
@@ -0,0 +1,36 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef PLUGINNOTIFICATIONTASK_H
+#define PLUGINNOTIFICATIONTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/notification.hpp"
+#include "icinga/service.hpp"
+#include "base/process.hpp"
+
+namespace icinga
+{
+
+/**
+ * Implements sending notifications based on external plugins.
+ *
+ * @ingroup methods
+ */
+class PluginNotificationTask
+{
+public:
+ static void ScriptFunc(const Notification::Ptr& notification,
+ const User::Ptr& user, const CheckResult::Ptr& cr, int itype,
+ const String& author, const String& comment,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ PluginNotificationTask();
+
+ static void ProcessFinishedHandler(const Checkable::Ptr& checkable,
+ const Value& commandLine, const ProcessResult& pr);
+};
+
+}
+
+#endif /* PLUGINNOTIFICATIONTASK_H */
diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp
new file mode 100644
index 0000000..9b133ef
--- /dev/null
+++ b/lib/methods/randomchecktask.cpp
@@ -0,0 +1,65 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef _WIN32
+# include <stdlib.h>
+#endif /* _WIN32 */
+#include "methods/randomchecktask.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "icinga/checkcommand.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ double now = Utility::GetTime();
+ double uptime = Application::GetUptime();
+
+ String output = "Hello from " + IcingaApplication::GetInstance()->GetNodeName()
+ + ". Icinga 2 has been running for " + Utility::FormatDuration(uptime)
+ + ". Version: " + Application::GetAppVersion();
+
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+ String commandName = command->GetName();
+ ServiceState state = static_cast<ServiceState>(Utility::Random() % 4);
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
+ } else {
+ cr->SetOutput(output);
+
+ double random = Utility::Random() % 1000;
+ cr->SetPerformanceData(new Array({
+ new PerfdataValue("time", now),
+ new PerfdataValue("value", random),
+ new PerfdataValue("value_1m", random * 0.9),
+ new PerfdataValue("value_5m", random * 0.8),
+ new PerfdataValue("uptime", uptime),
+ }));
+
+ cr->SetState(state);
+ cr->SetCommand(commandName);
+
+ checkable->ProcessCheckResult(cr);
+ }
+}
diff --git a/lib/methods/randomchecktask.hpp b/lib/methods/randomchecktask.hpp
new file mode 100644
index 0000000..00ce663
--- /dev/null
+++ b/lib/methods/randomchecktask.hpp
@@ -0,0 +1,29 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef RANDOMCHECKTASK_H
+#define RANDOMCHECKTASK_H
+
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional check types. Implements the "null" check type.
+ *
+ * @ingroup methods
+ */
+class RandomCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ RandomCheckTask();
+};
+
+}
+
+#endif /* RANDOMCHECKTASK_H */
diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp
new file mode 100644
index 0000000..af6b063
--- /dev/null
+++ b/lib/methods/sleepchecktask.cpp
@@ -0,0 +1,67 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/sleepchecktask.hpp"
+#include "icinga/pluginutility.hpp"
+#include "base/utility.hpp"
+#include "base/convert.hpp"
+#include "base/function.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, SleepCheck, &SleepCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ REQUIRE_NOT_NULL(checkable);
+ REQUIRE_NOT_NULL(cr);
+
+ CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", commandObj);
+
+ double sleepTime = MacroProcessor::ResolveMacros("$sleep_time$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ Utility::Sleep(sleepTime);
+
+ String output = "Slept for " + Convert::ToString(sleepTime) + " seconds.";
+
+ double now = Utility::GetTime();
+ CheckCommand::Ptr command = checkable->GetCheckCommand();
+ String commandName = command->GetName();
+
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = output;
+ pr.ExecutionStart = now - sleepTime;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = 0;
+
+ Checkable::ExecuteCommandProcessFinishedHandler("", pr);
+ } else {
+ cr->SetOutput(output);
+ cr->SetExecutionStart(now);
+ cr->SetExecutionEnd(now);
+ cr->SetCommand(commandName);
+
+ checkable->ProcessCheckResult(cr);
+ }
+} \ No newline at end of file
diff --git a/lib/methods/sleepchecktask.hpp b/lib/methods/sleepchecktask.hpp
new file mode 100644
index 0000000..b104f60
--- /dev/null
+++ b/lib/methods/sleepchecktask.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef SLEEPCHECKTASK_H
+#define SLEEPCHECKTASK_H
+
+#include "methods/i2-methods.hpp"
+#include "icinga/service.hpp"
+#include "base/dictionary.hpp"
+
+namespace icinga
+{
+
+/**
+ * Test class for additional check types. Implements the "sleep" check type.
+ *
+ * @ingroup methods
+ */
+class SleepCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ SleepCheckTask();
+};
+
+}
+
+#endif /* SLEEPCHECKTASK_H */ \ No newline at end of file
diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp
new file mode 100644
index 0000000..bb3f1bb
--- /dev/null
+++ b/lib/methods/timeperiodtask.cpp
@@ -0,0 +1,35 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "methods/timeperiodtask.hpp"
+#include "base/function.hpp"
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end");
+REGISTER_FUNCTION_NONCONST(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end");
+
+Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double, double)
+{
+ REQUIRE_NOT_NULL(tp);
+
+ Array::Ptr segments = new Array();
+ return segments;
+}
+
+Array::Ptr TimePeriodTask::EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end)
+{
+ REQUIRE_NOT_NULL(tp);
+
+ ArrayData segments;
+
+ for (long t = begin / 60 - 1; t * 60 < end; t++) {
+ if ((t % 2) == 0) {
+ segments.push_back(new Dictionary({
+ { "begin", t * 60 },
+ { "end", (t + 1) * 60 }
+ }));
+ }
+ }
+
+ return new Array(std::move(segments));
+}
diff --git a/lib/methods/timeperiodtask.hpp b/lib/methods/timeperiodtask.hpp
new file mode 100644
index 0000000..0dff1c6
--- /dev/null
+++ b/lib/methods/timeperiodtask.hpp
@@ -0,0 +1,28 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef TIMEPERIODTASK_H
+#define TIMEPERIODTASK_H
+
+#include "icinga/timeperiod.hpp"
+
+namespace icinga
+{
+
+/**
+* Test timeperiod functions.
+*
+* @ingroup methods
+*/
+class TimePeriodTask
+{
+public:
+ static Array::Ptr EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end);
+ static Array::Ptr EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end);
+
+private:
+ TimePeriodTask();
+};
+
+}
+
+#endif /* TIMEPERIODTASK_H */