Skip to content

Commit 175153c

Browse files
committed
PluginNotificationTask::ScriptFunc(): on Linux truncate output and comment
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.
1 parent 97cd05d commit 175153c

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

lib/methods/pluginnotificationtask.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@
1212
#include "base/process.hpp"
1313
#include "base/convert.hpp"
1414

15+
#ifdef __linux__
16+
# include <linux/binfmts.h>
17+
# include <unistd.h>
18+
19+
# ifndef PAGE_SIZE
20+
// MAX_ARG_STRLEN is a multiple of PAGE_SIZE which is missing
21+
# define PAGE_SIZE getpagesize()
22+
# endif /* PAGE_SIZE */
23+
24+
// Make e.g. the $host.output$ itself even 10% shorter to leave enough room
25+
// for e.g. --host-output= as in --host-output=$host.output$, but without int overflow
26+
const static auto l_MaxOutLen = MAX_ARG_STRLEN - MAX_ARG_STRLEN / 10u;
27+
#endif /* __linux__ */
28+
1529
using namespace icinga;
1630

1731
REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros");
@@ -33,7 +47,11 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
3347
Dictionary::Ptr notificationExtra = new Dictionary({
3448
{ "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types.
3549
{ "author", author },
50+
#ifdef __linux__
51+
{ "comment", comment.SubStr(0, l_MaxOutLen) }
52+
#else /* __linux__ */
3653
{ "comment", comment }
54+
#endif /* __linux__ */
3755
});
3856

3957
Host::Ptr host;
@@ -48,8 +66,35 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
4866
resolvers.emplace_back("user", user);
4967
resolvers.emplace_back("notification", notificationExtra);
5068
resolvers.emplace_back("notification", notification);
51-
if (service)
69+
70+
if (service) {
71+
#ifdef __linux__
72+
auto cr (service->GetLastCheckResult());
73+
74+
if (cr) {
75+
auto output (cr->GetOutput());
76+
77+
if (output.GetLength() > l_MaxOutLen) {
78+
resolvers.emplace_back("service", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
79+
}
80+
}
81+
#endif /* __linux__ */
82+
5283
resolvers.emplace_back("service", service);
84+
}
85+
86+
#ifdef __linux__
87+
auto hcr (host->GetLastCheckResult());
88+
89+
if (hcr) {
90+
auto output (hcr->GetOutput());
91+
92+
if (output.GetLength() > l_MaxOutLen) {
93+
resolvers.emplace_back("host", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}}));
94+
}
95+
}
96+
#endif /* __linux__ */
97+
5398
resolvers.emplace_back("host", host);
5499
resolvers.emplace_back("command", commandObj);
55100

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ set(base_test_SOURCES
3131
icinga-macros.cpp
3232
icinga-notification.cpp
3333
icinga-perfdata.cpp
34+
methods-pluginnotificationtask.cpp
3435
remote-configpackageutility.cpp
3536
remote-url.cpp
3637
${base_OBJS}
3738
$<TARGET_OBJECTS:config>
3839
$<TARGET_OBJECTS:remote>
3940
$<TARGET_OBJECTS:icinga>
41+
$<TARGET_OBJECTS:methods>
4042
)
4143

4244
if(ICINGA2_UNITY_BUILD)
@@ -161,6 +163,7 @@ add_boost_test(base
161163
icinga_perfdata/multi
162164
icinga_perfdata/scientificnotation
163165
icinga_perfdata/parse_edgecases
166+
methods_pluginnotificationtask/truncate_long_output
164167
remote_configpackageutility/ValidateName
165168
remote_url/id_and_path
166169
remote_url/parameters
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */
2+
3+
#include "base/array.hpp"
4+
#include "icinga/checkresult.hpp"
5+
#include "icinga/host.hpp"
6+
#include "icinga/notification.hpp"
7+
#include "icinga/notificationcommand.hpp"
8+
#include "icinga/service.hpp"
9+
#include "icinga/user.hpp"
10+
#include "methods/pluginnotificationtask.hpp"
11+
#include <BoostTestTargetConfig.h>
12+
#include <future>
13+
14+
using namespace icinga;
15+
16+
BOOST_AUTO_TEST_SUITE(methods_pluginnotificationtask)
17+
18+
BOOST_AUTO_TEST_CASE(truncate_long_output)
19+
{
20+
#ifdef __linux__
21+
Host::Ptr h = new Host();
22+
CheckResult::Ptr hcr = new CheckResult();
23+
CheckResult::Ptr scr = new CheckResult();
24+
Service::Ptr s = new Service();
25+
User::Ptr u = new User();
26+
NotificationCommand::Ptr nc = new NotificationCommand();
27+
Notification::Ptr n = new Notification();
28+
String placeHolder (1024 * 1024, 'x');
29+
std::promise<String> promise;
30+
auto future (promise.get_future());
31+
32+
hcr->SetOutput("H" + placeHolder + "h", true);
33+
scr->SetOutput("S" + placeHolder + "s", true);
34+
35+
h->SetName("example.com", true);
36+
h->SetLastCheckResult(hcr, true);
37+
h->Register();
38+
39+
s->SetHostName("example.com", true);
40+
s->SetShortName("disk", true);
41+
s->SetLastCheckResult(scr, true);
42+
s->OnAllConfigLoaded(); // link Host
43+
44+
nc->SetCommandLine(
45+
new Array({
46+
"echo",
47+
"host_output=$host.output$",
48+
"service_output=$service.output$",
49+
"notification_comment=$notification.comment$",
50+
"output=$output$",
51+
"comment=$comment$"
52+
}),
53+
true
54+
);
55+
56+
nc->SetName("mail", true);
57+
nc->Register();
58+
59+
n->SetFieldByName("host_name", "example.com", false, DebugInfo());
60+
n->SetFieldByName("service_name", "disk", false, DebugInfo());
61+
n->SetFieldByName("command", "mail", false, DebugInfo());
62+
n->OnAllConfigLoaded(); // link Service
63+
64+
Checkable::ExecuteCommandProcessFinishedHandler = [&promise](const Value&, const ProcessResult& pr) {
65+
promise.set_value(pr.Output);
66+
};
67+
68+
PluginNotificationTask::ScriptFunc(n, u, nullptr, NotificationCustom, "jdoe", "C" + placeHolder + "c", nullptr, false);
69+
future.wait();
70+
71+
Checkable::ExecuteCommandProcessFinishedHandler = nullptr;
72+
h->Unregister();
73+
nc->Unregister();
74+
75+
auto output (future.get());
76+
77+
BOOST_CHECK(output.Contains("host_output=Hx"));
78+
BOOST_CHECK(!output.Contains("xh"));
79+
BOOST_CHECK(output.Contains("x service_output=Sx"));
80+
BOOST_CHECK(!output.Contains("xs"));
81+
BOOST_CHECK(output.Contains("x notification_comment=Cx"));
82+
BOOST_CHECK(!output.Contains("xc"));
83+
BOOST_CHECK(output.Contains("x output=Sx"));
84+
BOOST_CHECK(output.Contains("x comment=Cx"));
85+
#endif /* __linux__ */
86+
}
87+
88+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)