Skip to content

Commit

Permalink
PluginNotificationTask::ScriptFunc(): on Linux truncate output and co…
Browse files Browse the repository at this point in the history
…mment

not to run into an exec(3) error E2BIG due to a too long argument.
This sends a notification with truncated output instead of not sending.
  • Loading branch information
Al2Klimov committed Dec 12, 2023
1 parent 2d167cc commit 33ff948
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
44 changes: 43 additions & 1 deletion lib/methods/pluginnotificationtask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
#include "base/process.hpp"
#include "base/convert.hpp"

#ifdef __linux__
# include <linux/binfmts.h>
# include <unistd.h>

# ifndef PAGE_SIZE
# define PAGE_SIZE getpagesize()
# endif /* PAGE_SIZE */

const static auto l_MaxOutLen = MAX_ARG_STRLEN * 3u / 4u;
#endif /* __linux__ */

using namespace icinga;

REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros");
Expand All @@ -33,7 +44,11 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
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;
Expand All @@ -48,8 +63,35 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
resolvers.emplace_back("user", user);
resolvers.emplace_back("notification", notificationExtra);
resolvers.emplace_back("notification", notification);
if (service)

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);

Expand Down
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ set(base_test_SOURCES
icinga-macros.cpp
icinga-notification.cpp
icinga-perfdata.cpp
methods-pluginnotificationtask.cpp
remote-configpackageutility.cpp
remote-url.cpp
${base_OBJS}
$<TARGET_OBJECTS:config>
$<TARGET_OBJECTS:remote>
$<TARGET_OBJECTS:icinga>
$<TARGET_OBJECTS:methods>
)

if(ICINGA2_UNITY_BUILD)
Expand Down Expand Up @@ -156,6 +158,7 @@ add_boost_test(base
icinga_perfdata/multi
icinga_perfdata/scientificnotation
icinga_perfdata/parse_edgecases
methods_pluginnotificationtask/truncate_long_output
remote_configpackageutility/ValidateName
remote_url/id_and_path
remote_url/parameters
Expand Down
75 changes: 75 additions & 0 deletions test/methods-pluginnotificationtask.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */

#include "base/array.hpp"
#include "icinga/checkresult.hpp"
#include "icinga/host.hpp"
#include "icinga/notification.hpp"
#include "icinga/notificationcommand.hpp"
#include "icinga/service.hpp"
#include "icinga/user.hpp"
#include "methods/pluginnotificationtask.hpp"
#include <BoostTestTargetConfig.h>
#include <future>

using namespace icinga;

BOOST_AUTO_TEST_SUITE(methods_pluginnotificationtask)

BOOST_AUTO_TEST_CASE(truncate_long_output)
{
#ifdef __linux__
Host::Ptr h = new Host();
CheckResult::Ptr hcr = new CheckResult();
CheckResult::Ptr scr = new CheckResult();
Service::Ptr s = new Service();
User::Ptr u = new User();
NotificationCommand::Ptr nc = new NotificationCommand();
Notification::Ptr n = new Notification();
String placeHolder (1024 * 1024, 'x');
std::promise<Value> promise;
auto future (promise.get_future());

hcr->SetOutput("H" + placeHolder + "h", true);
scr->SetOutput("S" + placeHolder + "s", true);

h->SetName("example.com", true);
h->SetLastCheckResult(hcr, true);
h->Register();

s->SetHostName("example.com", true);
s->SetShortName("disk", true);
s->SetLastCheckResult(scr, true);
((ConfigObject*)s.get())->OnAllConfigLoaded(); // link Host

nc->SetName("mail", true);
nc->SetCommandLine(new Array({"echo", "$host.output$", "$service.output$", "$notification.comment$"}), true);
nc->Register();

n->SetFieldByName("host_name", "example.com", false, DebugInfo());
n->SetFieldByName("service_name", "disk", false, DebugInfo());
n->SetFieldByName("command", "mail", false, DebugInfo());
((ConfigObject*)n.get())->OnAllConfigLoaded(); // link Service

Checkable::ExecuteCommandProcessFinishedHandler = [&promise](const Value& commandline, const ProcessResult&) {
promise.set_value(commandline);
};

PluginNotificationTask::ScriptFunc(n, u, nullptr, NotificationCustom, "jdoe", "C" + placeHolder + "c", nullptr, false);
future.wait();

Checkable::ExecuteCommandProcessFinishedHandler = nullptr;
h->Unregister();
nc->Unregister();

String commandline = Array::Ptr(future.get())->Join(" ");

BOOST_CHECK(commandline.Contains("echo Hx"));
BOOST_CHECK(!commandline.Contains("xh"));
BOOST_CHECK(commandline.Contains("x Sx"));
BOOST_CHECK(!commandline.Contains("xs"));
BOOST_CHECK(commandline.Contains("x Cx"));
BOOST_CHECK(!commandline.Contains("xc"));
#endif /* __linux__ */
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 33ff948

Please sign in to comment.