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