]> granicus.if.org Git - icinga2/blob - lib/icinga/clusterevents.cpp
Merge pull request #5760 from Icinga/fix/http-client-bugs
[icinga2] / lib / icinga / clusterevents.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/clusterevents.hpp"
21 #include "icinga/service.hpp"
22 #include "remote/apilistener.hpp"
23 #include "remote/endpoint.hpp"
24 #include "remote/messageorigin.hpp"
25 #include "remote/zone.hpp"
26 #include "remote/apifunction.hpp"
27 #include "remote/eventqueue.hpp"
28 #include "base/application.hpp"
29 #include "base/configtype.hpp"
30 #include "base/utility.hpp"
31 #include "base/perfdatavalue.hpp"
32 #include "base/exception.hpp"
33 #include "base/initialize.hpp"
34 #include "base/serializer.hpp"
35 #include "base/json.hpp"
36 #include <fstream>
37
38 using namespace icinga;
39
40 INITIALIZE_ONCE(&ClusterEvents::StaticInitialize);
41
42 REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler);
43 REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler);
44 REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
45 REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler);
46 REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
47 REGISTER_APIFUNCTION(SetAcknowledgement, event, &ClusterEvents::AcknowledgementSetAPIHandler);
48 REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ClusterEvents::AcknowledgementClearedAPIHandler);
49 REGISTER_APIFUNCTION(ExecuteCommand, event, &ClusterEvents::ExecuteCommandAPIHandler);
50 REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotificationsAPIHandler);
51 REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler);
52 REGISTER_APIFUNCTION(NotificationSentToAllUsers, event, &ClusterEvents::NotificationSentToAllUsersAPIHandler);
53
54 void ClusterEvents::StaticInitialize(void)
55 {
56         Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
57         Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler);
58         Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
59         Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler);
60         Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler);
61         Checkable::OnNotificationsRequested.connect(&ClusterEvents::SendNotificationsHandler);
62         Checkable::OnNotificationSentToUser.connect(&ClusterEvents::NotificationSentUserHandler);
63         Checkable::OnNotificationSentToAllUsers.connect(&ClusterEvents::NotificationSentToAllUsersHandler);
64
65         Checkable::OnAcknowledgementSet.connect(&ClusterEvents::AcknowledgementSetHandler);
66         Checkable::OnAcknowledgementCleared.connect(&ClusterEvents::AcknowledgementClearedHandler);
67 }
68
69 Dictionary::Ptr ClusterEvents::MakeCheckResultMessage(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
70 {
71         Dictionary::Ptr message = new Dictionary();
72         message->Set("jsonrpc", "2.0");
73         message->Set("method", "event::CheckResult");
74
75         Host::Ptr host;
76         Service::Ptr service;
77         tie(host, service) = GetHostService(checkable);
78
79         Dictionary::Ptr params = new Dictionary();
80         params->Set("host", host->GetName());
81         if (service)
82                 params->Set("service", service->GetShortName());
83         else {
84                 Value agent_service_name = checkable->GetExtension("agent_service_name");
85
86                 if (!agent_service_name.IsEmpty())
87                         params->Set("service", agent_service_name);
88         }
89         params->Set("cr", Serialize(cr));
90
91         message->Set("params", params);
92
93         return message;
94 }
95
96 void ClusterEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin)
97 {
98         ApiListener::Ptr listener = ApiListener::GetInstance();
99
100         if (!listener)
101                 return;
102
103         Dictionary::Ptr message = MakeCheckResultMessage(checkable, cr);
104         listener->RelayMessage(origin, checkable, message, true);
105 }
106
107 Value ClusterEvents::CheckResultAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
108 {
109         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
110
111         if (!endpoint) {
112                 Log(LogNotice, "ClusterEvents")
113                     << "Discarding 'check result' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
114                 return Empty;
115         }
116
117         if (!params)
118                 return Empty;
119
120         CheckResult::Ptr cr;
121         Array::Ptr vperf;
122
123         if (params->Contains("cr")) {
124                 cr = new CheckResult();
125                 Dictionary::Ptr vcr = params->Get("cr");
126
127                 if (vcr && vcr->Contains("performance_data")) {
128                         vperf = vcr->Get("performance_data");
129
130                         if (vperf)
131                                 vcr->Remove("performance_data");
132
133                         Deserialize(cr, vcr, true);
134                 }
135         }
136
137         if (!cr)
138                 return Empty;
139
140         Array::Ptr rperf = new Array();
141
142         if (vperf) {
143                 ObjectLock olock(vperf);
144                 for (const Value& vp : vperf) {
145                         Value p;
146
147                         if (vp.IsObjectType<Dictionary>()) {
148                                 PerfdataValue::Ptr val = new PerfdataValue();
149                                 Deserialize(val, vp, true);
150                                 rperf->Add(val);
151                         } else
152                                 rperf->Add(vp);
153                 }
154         }
155
156         cr->SetPerformanceData(rperf);
157
158         Host::Ptr host = Host::GetByName(params->Get("host"));
159
160         if (!host)
161                 return Empty;
162
163         Checkable::Ptr checkable;
164
165         if (params->Contains("service"))
166                 checkable = host->GetServiceByShortName(params->Get("service"));
167         else
168                 checkable = host;
169
170         if (!checkable)
171                 return Empty;
172
173         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable) && endpoint != checkable->GetCommandEndpoint()) {
174                 Log(LogNotice, "ClusterEvents")
175                     << "Discarding 'check result' message for checkable '" << checkable->GetName()
176                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
177                 return Empty;
178         }
179
180         if (!checkable->IsPaused() && Zone::GetLocalZone() == checkable->GetZone() && endpoint == checkable->GetCommandEndpoint())
181                 checkable->ProcessCheckResult(cr);
182         else
183                 checkable->ProcessCheckResult(cr, origin);
184
185         return Empty;
186 }
187
188 void ClusterEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
189 {
190         ApiListener::Ptr listener = ApiListener::GetInstance();
191
192         if (!listener)
193                 return;
194
195         Host::Ptr host;
196         Service::Ptr service;
197         tie(host, service) = GetHostService(checkable);
198
199         Dictionary::Ptr params = new Dictionary();
200         params->Set("host", host->GetName());
201         if (service)
202                 params->Set("service", service->GetShortName());
203         params->Set("next_check", checkable->GetNextCheck());
204
205         Dictionary::Ptr message = new Dictionary();
206         message->Set("jsonrpc", "2.0");
207         message->Set("method", "event::SetNextCheck");
208         message->Set("params", params);
209
210         listener->RelayMessage(origin, checkable, message, true);
211 }
212
213 Value ClusterEvents::NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
214 {
215         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
216
217         if (!endpoint) {
218                 Log(LogNotice, "ClusterEvents")
219                     << "Discarding 'next check changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
220                 return Empty;
221         }
222
223         if (!params)
224                 return Empty;
225
226         Host::Ptr host = Host::GetByName(params->Get("host"));
227
228         if (!host)
229                 return Empty;
230
231         Checkable::Ptr checkable;
232
233         if (params->Contains("service"))
234                 checkable = host->GetServiceByShortName(params->Get("service"));
235         else
236                 checkable = host;
237
238         if (!checkable)
239                 return Empty;
240
241         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
242                 Log(LogNotice, "ClusterEvents")
243                     << "Discarding 'next check changed' message for checkable '" << checkable->GetName()
244                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
245                 return Empty;
246         }
247
248         double nextCheck = params->Get("next_check");
249
250         if (nextCheck < Application::GetStartTime() + 60)
251                 return Empty;
252
253         checkable->SetNextCheck(params->Get("next_check"), false, origin);
254
255         return Empty;
256 }
257
258 void ClusterEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
259 {
260         ApiListener::Ptr listener = ApiListener::GetInstance();
261
262         if (!listener)
263                 return;
264
265         Dictionary::Ptr params = new Dictionary();
266         params->Set("notification", notification->GetName());
267         params->Set("next_notification", notification->GetNextNotification());
268
269         Dictionary::Ptr message = new Dictionary();
270         message->Set("jsonrpc", "2.0");
271         message->Set("method", "event::SetNextNotification");
272         message->Set("params", params);
273
274         listener->RelayMessage(origin, notification, message, true);
275 }
276
277 Value ClusterEvents::NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
278 {
279         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
280
281         if (!endpoint) {
282                 Log(LogNotice, "ClusterEvents")
283                     << "Discarding 'next notification changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
284                 return Empty;
285         }
286
287         if (!params)
288                 return Empty;
289
290         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
291
292         if (!notification)
293                 return Empty;
294
295         if (origin->FromZone && !origin->FromZone->CanAccessObject(notification)) {
296                 Log(LogNotice, "ClusterEvents")
297                     << "Discarding 'next notification changed' message for notification '" << notification->GetName()
298                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
299                 return Empty;
300         }
301
302         double nextNotification = params->Get("next_notification");
303
304         if (nextNotification < Utility::GetTime())
305                 return Empty;
306
307         notification->SetNextNotification(nextNotification, false, origin);
308
309         return Empty;
310 }
311
312 void ClusterEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
313 {
314         ApiListener::Ptr listener = ApiListener::GetInstance();
315
316         if (!listener)
317                 return;
318
319         Host::Ptr host;
320         Service::Ptr service;
321         tie(host, service) = GetHostService(checkable);
322
323         Dictionary::Ptr params = new Dictionary();
324         params->Set("host", host->GetName());
325         if (service)
326                 params->Set("service", service->GetShortName());
327         params->Set("forced", checkable->GetForceNextCheck());
328
329         Dictionary::Ptr message = new Dictionary();
330         message->Set("jsonrpc", "2.0");
331         message->Set("method", "event::SetForceNextCheck");
332         message->Set("params", params);
333
334         listener->RelayMessage(origin, checkable, message, true);
335 }
336
337 Value ClusterEvents::ForceNextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
338 {
339         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
340
341         if (!endpoint) {
342                 Log(LogNotice, "ClusterEvents")
343                     << "Discarding 'force next check changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
344                 return Empty;
345         }
346
347         if (!params)
348                 return Empty;
349
350         Host::Ptr host = Host::GetByName(params->Get("host"));
351
352         if (!host)
353                 return Empty;
354
355         Checkable::Ptr checkable;
356
357         if (params->Contains("service"))
358                 checkable = host->GetServiceByShortName(params->Get("service"));
359         else
360                 checkable = host;
361
362         if (!checkable)
363                 return Empty;
364
365         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
366                 Log(LogNotice, "ClusterEvents")
367                     << "Discarding 'force next check' message for checkable '" << checkable->GetName()
368                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
369                 return Empty;
370         }
371
372         checkable->SetForceNextCheck(params->Get("forced"), false, origin);
373
374         return Empty;
375 }
376
377 void ClusterEvents::ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
378 {
379         ApiListener::Ptr listener = ApiListener::GetInstance();
380
381         if (!listener)
382                 return;
383
384         Host::Ptr host;
385         Service::Ptr service;
386         tie(host, service) = GetHostService(checkable);
387
388         Dictionary::Ptr params = new Dictionary();
389         params->Set("host", host->GetName());
390         if (service)
391                 params->Set("service", service->GetShortName());
392         params->Set("forced", checkable->GetForceNextNotification());
393
394         Dictionary::Ptr message = new Dictionary();
395         message->Set("jsonrpc", "2.0");
396         message->Set("method", "event::SetForceNextNotification");
397         message->Set("params", params);
398
399         listener->RelayMessage(origin, checkable, message, true);
400 }
401
402 Value ClusterEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
403 {
404         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
405
406         if (!endpoint) {
407                 Log(LogNotice, "ClusterEvents")
408                     << "Discarding 'force next notification changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
409                 return Empty;
410         }
411
412         if (!params)
413                 return Empty;
414
415         Host::Ptr host = Host::GetByName(params->Get("host"));
416
417         if (!host)
418                 return Empty;
419
420         Checkable::Ptr checkable;
421
422         if (params->Contains("service"))
423                 checkable = host->GetServiceByShortName(params->Get("service"));
424         else
425                 checkable = host;
426
427         if (!checkable)
428                 return Empty;
429
430         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
431                 Log(LogNotice, "ClusterEvents")
432                     << "Discarding 'force next notification' message for checkable '" << checkable->GetName()
433                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
434                 return Empty;
435         }
436
437         checkable->SetForceNextNotification(params->Get("forced"), false, origin);
438
439         return Empty;
440 }
441
442 void ClusterEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable,
443     const String& author, const String& comment, AcknowledgementType type,
444     bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin)
445 {
446         ApiListener::Ptr listener = ApiListener::GetInstance();
447
448         if (!listener)
449                 return;
450
451         Host::Ptr host;
452         Service::Ptr service;
453         tie(host, service) = GetHostService(checkable);
454
455         Dictionary::Ptr params = new Dictionary();
456         params->Set("host", host->GetName());
457         if (service)
458                 params->Set("service", service->GetShortName());
459         params->Set("author", author);
460         params->Set("comment", comment);
461         params->Set("acktype", type);
462         params->Set("notify", notify);
463         params->Set("expiry", expiry);
464
465         Dictionary::Ptr message = new Dictionary();
466         message->Set("jsonrpc", "2.0");
467         message->Set("method", "event::SetAcknowledgement");
468         message->Set("params", params);
469
470         listener->RelayMessage(origin, checkable, message, true);
471 }
472
473 Value ClusterEvents::AcknowledgementSetAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
474 {
475         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
476
477         if (!endpoint) {
478                 Log(LogNotice, "ClusterEvents")
479                     << "Discarding 'acknowledgement set' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
480                 return Empty;
481         }
482
483         if (!params)
484                 return Empty;
485
486         Host::Ptr host = Host::GetByName(params->Get("host"));
487
488         if (!host)
489                 return Empty;
490
491         Checkable::Ptr checkable;
492
493         if (params->Contains("service"))
494                 checkable = host->GetServiceByShortName(params->Get("service"));
495         else
496                 checkable = host;
497
498         if (!checkable)
499                 return Empty;
500
501         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
502                 Log(LogNotice, "ClusterEvents")
503                     << "Discarding 'acknowledgement set' message for checkable '" << checkable->GetName()
504                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
505                 return Empty;
506         }
507
508         checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"),
509             static_cast<AcknowledgementType>(static_cast<int>(params->Get("acktype"))),
510             params->Get("notify"), params->Get("persistent"), params->Get("expiry"), origin);
511
512         return Empty;
513 }
514
515 void ClusterEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
516 {
517         ApiListener::Ptr listener = ApiListener::GetInstance();
518
519         if (!listener)
520                 return;
521
522         Host::Ptr host;
523         Service::Ptr service;
524         tie(host, service) = GetHostService(checkable);
525
526         Dictionary::Ptr params = new Dictionary();
527         params->Set("host", host->GetName());
528         if (service)
529                 params->Set("service", service->GetShortName());
530
531         Dictionary::Ptr message = new Dictionary();
532         message->Set("jsonrpc", "2.0");
533         message->Set("method", "event::ClearAcknowledgement");
534         message->Set("params", params);
535
536         listener->RelayMessage(origin, checkable, message, true);
537 }
538
539 Value ClusterEvents::AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
540 {
541         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
542
543         if (!endpoint) {
544                 Log(LogNotice, "ClusterEvents")
545                     << "Discarding 'acknowledgement cleared' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
546                 return Empty;
547         }
548
549         if (!params)
550                 return Empty;
551
552         Host::Ptr host = Host::GetByName(params->Get("host"));
553
554         if (!host)
555                 return Empty;
556
557         Checkable::Ptr checkable;
558
559         if (params->Contains("service"))
560                 checkable = host->GetServiceByShortName(params->Get("service"));
561         else
562                 checkable = host;
563
564         if (!checkable)
565                 return Empty;
566
567         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
568                 Log(LogNotice, "ClusterEvents")
569                     << "Discarding 'acknowledgement cleared' message for checkable '" << checkable->GetName()
570                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
571                 return Empty;
572         }
573
574         checkable->ClearAcknowledgement(origin);
575
576         return Empty;
577 }
578
579 Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
580 {
581         Endpoint::Ptr sourceEndpoint = origin->FromClient->GetEndpoint();
582
583         if (!sourceEndpoint || (origin->FromZone && !Zone::GetLocalZone()->IsChildOf(origin->FromZone))) {
584                 Log(LogNotice, "ClusterEvents")
585                     << "Discarding 'execute command' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
586                 return Empty;
587         }
588
589         ApiListener::Ptr listener = ApiListener::GetInstance();
590
591         if (!listener) {
592                 Log(LogCritical, "ApiListener", "No instance available.");
593                 return Empty;
594         }
595
596         if (!listener->GetAcceptCommands()) {
597                 Log(LogWarning, "ApiListener")
598                     << "Ignoring command. '" << listener->GetName() << "' does not accept commands.";
599
600                 Host::Ptr host = new Host();
601                 Dictionary::Ptr attrs = new Dictionary();
602
603                 attrs->Set("__name", params->Get("host"));
604                 attrs->Set("type", "Host");
605                 attrs->Set("enable_active_checks", false);
606
607                 Deserialize(host, attrs, false, FAConfig);
608
609                 if (params->Contains("service"))
610                         host->SetExtension("agent_service_name", params->Get("service"));
611
612                 CheckResult::Ptr cr = new CheckResult();
613                 cr->SetState(ServiceUnknown);
614                 cr->SetOutput("Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands.");
615                 Dictionary::Ptr message = MakeCheckResultMessage(host, cr);
616                 listener->SyncSendMessage(sourceEndpoint, message);
617
618                 return Empty;
619         }
620
621         /* use a virtual host object for executing the command */
622         Host::Ptr host = new Host();
623         Dictionary::Ptr attrs = new Dictionary();
624
625         attrs->Set("__name", params->Get("host"));
626         attrs->Set("type", "Host");
627
628         Deserialize(host, attrs, false, FAConfig);
629
630         if (params->Contains("service"))
631                 host->SetExtension("agent_service_name", params->Get("service"));
632
633         String command = params->Get("command");
634         String command_type = params->Get("command_type");
635
636         if (command_type == "check_command") {
637                 if (!CheckCommand::GetByName(command)) {
638                         CheckResult::Ptr cr = new CheckResult();
639                         cr->SetState(ServiceUnknown);
640                         cr->SetOutput("Check command '" + command + "' does not exist.");
641                         Dictionary::Ptr message = MakeCheckResultMessage(host, cr);
642                         listener->SyncSendMessage(sourceEndpoint, message);
643                         return Empty;
644                 }
645         } else if (command_type == "event_command") {
646                 if (!EventCommand::GetByName(command)) {
647                         Log(LogWarning, "ClusterEvents")
648                             << "Event command '" << command << "' does not exist.";
649                         return Empty;
650                 }
651         } else
652                 return Empty;
653
654         attrs->Set(command_type, params->Get("command"));
655         attrs->Set("command_endpoint", sourceEndpoint->GetName());
656
657         Deserialize(host, attrs, false, FAConfig);
658
659         host->SetExtension("agent_check", true);
660
661         Dictionary::Ptr macros = params->Get("macros");
662
663         if (command_type == "check_command") {
664                 try {
665                         host->ExecuteRemoteCheck(macros);
666                 } catch (const std::exception& ex) {
667                         CheckResult::Ptr cr = new CheckResult();
668                         cr->SetState(ServiceUnknown);
669
670                         String output = "Exception occured while checking '" + host->GetName() + "': " + DiagnosticInformation(ex);
671                         cr->SetOutput(output);
672
673                         double now = Utility::GetTime();
674                         cr->SetScheduleStart(now);
675                         cr->SetScheduleEnd(now);
676                         cr->SetExecutionStart(now);
677                         cr->SetExecutionEnd(now);
678
679                         Dictionary::Ptr message = MakeCheckResultMessage(host, cr);
680                         listener->SyncSendMessage(sourceEndpoint, message);
681
682                         Log(LogCritical, "checker", output);
683                 }
684         } else if (command_type == "event_command") {
685                 host->ExecuteEventHandler(macros, true);
686         }
687
688         return Empty;
689 }
690
691 void ClusterEvents::SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type,
692     const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin)
693 {
694         ApiListener::Ptr listener = ApiListener::GetInstance();
695
696         if (!listener)
697                 return;
698
699         Dictionary::Ptr message = MakeCheckResultMessage(checkable, cr);
700         message->Set("method", "event::SendNotifications");
701
702         Dictionary::Ptr params = message->Get("params");
703         params->Set("type", type);
704         params->Set("author", author);
705         params->Set("text", text);
706
707         listener->RelayMessage(origin, nullptr, message, true);
708 }
709
710 Value ClusterEvents::SendNotificationsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
711 {
712         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
713
714         if (!endpoint) {
715                 Log(LogNotice, "ClusterEvents")
716                     << "Discarding 'send notification' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
717                 return Empty;
718         }
719
720         if (!params)
721                 return Empty;
722
723         Host::Ptr host = Host::GetByName(params->Get("host"));
724
725         if (!host)
726                 return Empty;
727
728         Checkable::Ptr checkable;
729
730         if (params->Contains("service"))
731                 checkable = host->GetServiceByShortName(params->Get("service"));
732         else
733                 checkable = host;
734
735         if (!checkable)
736                 return Empty;
737
738         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
739                 Log(LogNotice, "ClusterEvents")
740                     << "Discarding 'send custom notification' message for checkable '" << checkable->GetName()
741                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
742                 return Empty;
743         }
744
745         CheckResult::Ptr cr;
746         Array::Ptr vperf;
747
748         if (params->Contains("cr")) {
749                 cr = new CheckResult();
750                 Dictionary::Ptr vcr = params->Get("cr");
751
752                 if (vcr && vcr->Contains("performance_data")) {
753                         vperf = vcr->Get("performance_data");
754
755                         if (vperf)
756                                 vcr->Remove("performance_data");
757
758                         Deserialize(cr, vcr, true);
759                 }
760         }
761
762         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
763         String author = params->Get("author");
764         String text = params->Get("text");
765
766         Checkable::OnNotificationsRequested(checkable, type, cr, author, text, origin);
767
768         return Empty;
769 }
770
771 void ClusterEvents::NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
772     NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command,
773     const MessageOrigin::Ptr& origin)
774 {
775         ApiListener::Ptr listener = ApiListener::GetInstance();
776
777         if (!listener)
778                 return;
779
780         Host::Ptr host;
781         Service::Ptr service;
782         tie(host, service) = GetHostService(checkable);
783
784         Dictionary::Ptr params = new Dictionary();
785         params->Set("host", host->GetName());
786         if (service)
787                 params->Set("service", service->GetShortName());
788         params->Set("notification", notification->GetName());
789         params->Set("user", user->GetName());
790         params->Set("type", notificationType);
791         params->Set("cr", Serialize(cr));
792         params->Set("author", author);
793         params->Set("text", commentText);
794         params->Set("command", command);
795
796         Dictionary::Ptr message = new Dictionary();
797         message->Set("jsonrpc", "2.0");
798         message->Set("method", "event::NotificationSentUser");
799         message->Set("params", params);
800
801         listener->RelayMessage(origin, nullptr, message, true);
802 }
803
804 Value ClusterEvents::NotificationSentUserAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
805 {
806         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
807
808         if (!endpoint) {
809                 Log(LogNotice, "ClusterEvents")
810                     << "Discarding 'sent notification to user' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
811                 return Empty;
812         }
813
814         if (!params)
815                 return Empty;
816
817         Host::Ptr host = Host::GetByName(params->Get("host"));
818
819         if (!host)
820                 return Empty;
821
822         Checkable::Ptr checkable;
823
824         if (params->Contains("service"))
825                 checkable = host->GetServiceByShortName(params->Get("service"));
826         else
827                 checkable = host;
828
829         if (!checkable)
830                 return Empty;
831
832         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
833                 Log(LogNotice, "ClusterEvents")
834                     << "Discarding 'send notification to user' message for checkable '" << checkable->GetName()
835                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
836                 return Empty;
837         }
838
839         CheckResult::Ptr cr;
840         Array::Ptr vperf;
841
842         if (params->Contains("cr")) {
843                 cr = new CheckResult();
844                 Dictionary::Ptr vcr = params->Get("cr");
845
846                 if (vcr && vcr->Contains("performance_data")) {
847                         vperf = vcr->Get("performance_data");
848
849                         if (vperf)
850                                 vcr->Remove("performance_data");
851
852                         Deserialize(cr, vcr, true);
853                 }
854         }
855
856         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
857         String author = params->Get("author");
858         String text = params->Get("text");
859
860         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
861
862         if (!notification)
863                 return Empty;
864
865         User::Ptr user = User::GetByName(params->Get("user"));
866
867         if (!user)
868                 return Empty;
869
870         String command = params->Get("command");
871
872         Checkable::OnNotificationSentToUser(notification, checkable, user, type, cr, author, text, command, origin);
873
874         return Empty;
875 }
876
877 void ClusterEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users,
878     NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin)
879 {
880         ApiListener::Ptr listener = ApiListener::GetInstance();
881
882         if (!listener)
883                 return;
884
885         Host::Ptr host;
886         Service::Ptr service;
887         tie(host, service) = GetHostService(checkable);
888
889         Dictionary::Ptr params = new Dictionary();
890         params->Set("host", host->GetName());
891         if (service)
892                 params->Set("service", service->GetShortName());
893         params->Set("notification", notification->GetName());
894
895         Array::Ptr ausers = new Array();
896         for (const User::Ptr& user : users) {
897                 ausers->Add(user->GetName());
898         }
899         params->Set("users", ausers);
900
901         params->Set("type", notificationType);
902         params->Set("cr", Serialize(cr));
903         params->Set("author", author);
904         params->Set("text", commentText);
905
906         params->Set("last_notification", notification->GetLastNotification());
907         params->Set("next_notification", notification->GetNextNotification());
908         params->Set("notification_number", notification->GetNotificationNumber());
909         params->Set("last_problem_notification", notification->GetLastProblemNotification());
910         params->Set("no_more_notifications", notification->GetNoMoreNotifications());
911
912         Dictionary::Ptr message = new Dictionary();
913         message->Set("jsonrpc", "2.0");
914         message->Set("method", "event::NotificationSentToAllUsers");
915         message->Set("params", params);
916
917         listener->RelayMessage(origin, nullptr, message, true);
918 }
919
920 Value ClusterEvents::NotificationSentToAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
921 {
922         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
923
924         if (!endpoint) {
925                 Log(LogNotice, "ClusterEvents")
926                     << "Discarding 'sent notification to all users' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
927                 return Empty;
928         }
929
930         if (!params)
931                 return Empty;
932
933         Host::Ptr host = Host::GetByName(params->Get("host"));
934
935         if (!host)
936                 return Empty;
937
938         Checkable::Ptr checkable;
939
940         if (params->Contains("service"))
941                 checkable = host->GetServiceByShortName(params->Get("service"));
942         else
943                 checkable = host;
944
945         if (!checkable)
946                 return Empty;
947
948         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
949                 Log(LogNotice, "ClusterEvents")
950                     << "Discarding 'sent notification to all users' message for checkable '" << checkable->GetName()
951                     << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
952                 return Empty;
953         }
954
955         CheckResult::Ptr cr;
956         Array::Ptr vperf;
957
958         if (params->Contains("cr")) {
959                 cr = new CheckResult();
960                 Dictionary::Ptr vcr = params->Get("cr");
961
962                 if (vcr && vcr->Contains("performance_data")) {
963                         vperf = vcr->Get("performance_data");
964
965                         if (vperf)
966                                 vcr->Remove("performance_data");
967
968                         Deserialize(cr, vcr, true);
969                 }
970         }
971
972         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
973         String author = params->Get("author");
974         String text = params->Get("text");
975
976         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
977
978         if (!notification)
979                 return Empty;
980
981         Array::Ptr ausers = params->Get("users");
982
983         if (!ausers)
984                 return Empty;
985
986         std::set<User::Ptr> users;
987
988         {
989                 ObjectLock olock(ausers);
990                 for (const String& auser : ausers) {
991                         User::Ptr user = User::GetByName(auser);
992
993                         if (!user)
994                                 continue;
995
996                         users.insert(user);
997                 }
998         }
999
1000         notification->SetLastNotification(params->Get("last_notification"));
1001         notification->SetNextNotification(params->Get("next_notification"));
1002         notification->SetNotificationNumber(params->Get("notification_number"));
1003         notification->SetLastProblemNotification(params->Get("last_problem_notification"));
1004         notification->SetNoMoreNotifications(params->Get("no_more_notifications"));
1005
1006         Array::Ptr notifiedProblemUsers = new Array();
1007         for (const User::Ptr& user : users) {
1008                 notifiedProblemUsers->Add(user->GetName());
1009         }
1010
1011         notification->SetNotifiedProblemUsers(notifiedProblemUsers);
1012
1013         Checkable::OnNotificationSentToAllUsers(notification, checkable, users, type, cr, author, text, origin);
1014
1015         return Empty;
1016 }