1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "compat/statusdatawriter.hpp"
4 #include "compat/statusdatawriter-ti.cpp"
5 #include "icinga/icingaapplication.hpp"
6 #include "icinga/cib.hpp"
7 #include "icinga/hostgroup.hpp"
8 #include "icinga/servicegroup.hpp"
9 #include "icinga/checkcommand.hpp"
10 #include "icinga/eventcommand.hpp"
11 #include "icinga/timeperiod.hpp"
12 #include "icinga/notificationcommand.hpp"
13 #include "icinga/compatutility.hpp"
14 #include "icinga/pluginutility.hpp"
15 #include "icinga/dependency.hpp"
16 #include "base/configtype.hpp"
17 #include "base/objectlock.hpp"
18 #include "base/json.hpp"
19 #include "base/convert.hpp"
20 #include "base/logger.hpp"
21 #include "base/exception.hpp"
22 #include "base/application.hpp"
23 #include "base/context.hpp"
24 #include "base/statsfunction.hpp"
25 #include <boost/algorithm/string.hpp>
26 #include <boost/algorithm/string/replace.hpp>
27 #include <fstream>
28 
29 using namespace icinga;
30 
31 REGISTER_TYPE(StatusDataWriter);
32 
33 REGISTER_STATSFUNCTION(StatusDataWriter, &StatusDataWriter::StatsFunc);
34 
StatsFunc(const Dictionary::Ptr & status,const Array::Ptr &)35 void StatusDataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
36 {
37 	DictionaryData nodes;
38 
39 	for (const StatusDataWriter::Ptr& statusdatawriter : ConfigType::GetObjectsByType<StatusDataWriter>()) {
40 		nodes.emplace_back(statusdatawriter->GetName(), 1); //add more stats
41 	}
42 
43 	status->Set("statusdatawriter", new Dictionary(std::move(nodes)));
44 }
45 
46 /**
47  * Hint: The reason why we're using "\n" rather than std::endl is because
48  * std::endl also _flushes_ the output stream which severely degrades
49  * performance (see https://stackoverflow.com/questions/213907/c-stdendl-vs-n).
50  */
51 
52 /**
53  * Starts the component.
54  */
Start(bool runtimeCreated)55 void StatusDataWriter::Start(bool runtimeCreated)
56 {
57 	ObjectImpl<StatusDataWriter>::Start(runtimeCreated);
58 
59 	Log(LogInformation, "StatusDataWriter")
60 		<< "'" << GetName() << "' started.";
61 
62 	Log(LogWarning, "StatusDataWriter")
63 		<< "This feature is DEPRECATED and may be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones";
64 
65 	m_ObjectsCacheOutdated = true;
66 
67 	m_StatusTimer = new Timer();
68 	m_StatusTimer->SetInterval(GetUpdateInterval());
69 	m_StatusTimer->OnTimerExpired.connect([this](const Timer * const&){ StatusTimerHandler(); });
70 	m_StatusTimer->Start();
71 	m_StatusTimer->Reschedule(0);
72 
73 	ConfigObject::OnVersionChanged.connect([this](const ConfigObject::Ptr&, const Value&) { ObjectHandler(); });
74 	ConfigObject::OnActiveChanged.connect([this](const ConfigObject::Ptr&, const Value&) { ObjectHandler(); });
75 }
76 
77 /**
78  * Stops the component.
79  */
Stop(bool runtimeRemoved)80 void StatusDataWriter::Stop(bool runtimeRemoved)
81 {
82 	Log(LogInformation, "StatusDataWriter")
83 		<< "'" << GetName() << "' stopped.";
84 
85 	ObjectImpl<StatusDataWriter>::Stop(runtimeRemoved);
86 }
87 
DumpComments(std::ostream & fp,const Checkable::Ptr & checkable)88 void StatusDataWriter::DumpComments(std::ostream& fp, const Checkable::Ptr& checkable)
89 {
90 	Host::Ptr host;
91 	Service::Ptr service;
92 	tie(host, service) = GetHostService(checkable);
93 
94 	for (const Comment::Ptr& comment : checkable->GetComments()) {
95 		if (comment->IsExpired())
96 			continue;
97 
98 		if (service)
99 			fp << "servicecomment {" << "\n"
100 				<< "\t" << "service_description=" << service->GetShortName() << "\n";
101 		else
102 			fp << "hostcomment {" << "\n";
103 
104 		fp << "\t" "host_name=" << host->GetName() << "\n"
105 			"\t" "comment_id=" << comment->GetLegacyId() << "\n"
106 			"\t" "entry_time=" << comment->GetEntryTime() << "\n"
107 			"\t" "entry_type=" << comment->GetEntryType() << "\n"
108 			"\t" "persistent=" "1" "\n"
109 			"\t" "author=" << comment->GetAuthor() << "\n"
110 			"\t" "comment_data=" << comment->GetText() << "\n"
111 			"\t" "expires=" << (comment->GetExpireTime() != 0 ? 1 : 0) << "\n"
112 			"\t" "expire_time=" << comment->GetExpireTime() << "\n"
113 			"\t" "}" "\n"
114 			"\n";
115 	}
116 }
117 
DumpTimePeriod(std::ostream & fp,const TimePeriod::Ptr & tp)118 void StatusDataWriter::DumpTimePeriod(std::ostream& fp, const TimePeriod::Ptr& tp)
119 {
120 	fp << "define timeperiod {" "\n"
121 		"\t" "timeperiod_name" "\t" << tp->GetName() << "\n"
122 		"\t" "alias" "\t" << tp->GetName() << "\n";
123 
124 	Dictionary::Ptr ranges = tp->GetRanges();
125 
126 	if (ranges) {
127 		ObjectLock olock(ranges);
128 		for (const Dictionary::Pair& kv : ranges) {
129 			fp << "\t" << kv.first << "\t" << kv.second << "\n";
130 		}
131 	}
132 
133 	fp << "\t" "}" "\n" "\n";
134 }
135 
DumpCommand(std::ostream & fp,const Command::Ptr & command)136 void StatusDataWriter::DumpCommand(std::ostream& fp, const Command::Ptr& command)
137 {
138 	fp << "define command {" "\n"
139 		"\t" "command_name\t";
140 
141 	fp << CompatUtility::GetCommandName(command) << "\n";
142 
143 	fp << "\t" "command_line" "\t" << CompatUtility::GetCommandLine(command);
144 
145 	fp << "\n";
146 
147 	DumpCustomAttributes(fp, command);
148 
149 	fp << "\n" "\t" "}" "\n" "\n";
150 }
151 
DumpDowntimes(std::ostream & fp,const Checkable::Ptr & checkable)152 void StatusDataWriter::DumpDowntimes(std::ostream& fp, const Checkable::Ptr& checkable)
153 {
154 	Host::Ptr host;
155 	Service::Ptr service;
156 	tie(host, service) = GetHostService(checkable);
157 
158 	for (const Downtime::Ptr& downtime : checkable->GetDowntimes()) {
159 		if (downtime->IsExpired())
160 			continue;
161 
162 		if (service)
163 			fp << "servicedowntime {" << "\n"
164 				"\t" "service_description=" << service->GetShortName() << "\n";
165 		else
166 			fp << "hostdowntime {" "\n";
167 
168 		Downtime::Ptr triggeredByObj = Downtime::GetByName(downtime->GetTriggeredBy());
169 		int triggeredByLegacy = 0;
170 		if (triggeredByObj)
171 			triggeredByLegacy = triggeredByObj->GetLegacyId();
172 
173 		fp << "\t" << "host_name=" << host->GetName() << "\n"
174 			"\t" "downtime_id=" << downtime->GetLegacyId() << "\n"
175 			"\t" "entry_time=" << downtime->GetEntryTime() << "\n"
176 			"\t" "start_time=" << downtime->GetStartTime() << "\n"
177 			"\t" "end_time=" << downtime->GetEndTime() << "\n"
178 			"\t" "triggered_by=" << triggeredByLegacy << "\n"
179 			"\t" "fixed=" << static_cast<long>(downtime->GetFixed()) << "\n"
180 			"\t" "duration=" << static_cast<long>(downtime->GetDuration()) << "\n"
181 			"\t" "is_in_effect=" << (downtime->IsInEffect() ? 1 : 0) << "\n"
182 			"\t" "author=" << downtime->GetAuthor() << "\n"
183 			"\t" "comment=" << downtime->GetComment() << "\n"
184 			"\t" "trigger_time=" << downtime->GetTriggerTime() << "\n"
185 			"\t" "}" "\n"
186 			"\n";
187 	}
188 }
189 
DumpHostStatus(std::ostream & fp,const Host::Ptr & host)190 void StatusDataWriter::DumpHostStatus(std::ostream& fp, const Host::Ptr& host)
191 {
192 	fp << "hoststatus {" "\n" "\t" "host_name=" << host->GetName() << "\n";
193 
194 	{
195 		ObjectLock olock(host);
196 		DumpCheckableStatusAttrs(fp, host);
197 	}
198 
199 	/* ugly but cgis parse only that */
200 	fp << "\t" "last_time_up=" << host->GetLastStateUp() << "\n"
201 		"\t" "last_time_down=" << host->GetLastStateDown() << "\n"
202 		"\t" "last_time_unreachable=" << host->GetLastStateUnreachable() << "\n";
203 
204 	fp << "\t" "}" "\n" "\n";
205 
206 	DumpDowntimes(fp, host);
207 	DumpComments(fp, host);
208 }
209 
DumpHostObject(std::ostream & fp,const Host::Ptr & host)210 void StatusDataWriter::DumpHostObject(std::ostream& fp, const Host::Ptr& host)
211 {
212 	String notes = host->GetNotes();
213 	String notes_url = host->GetNotesUrl();
214 	String action_url = host->GetActionUrl();
215 	String icon_image = host->GetIconImage();
216 	String icon_image_alt = host->GetIconImageAlt();
217 	String display_name = host->GetDisplayName();
218 	String address = host->GetAddress();
219 	String address6 = host->GetAddress6();
220 
221 	fp << "define host {" "\n"
222 		"\t" "host_name" "\t" << host->GetName() << "\n";
223 	if (!display_name.IsEmpty()) {
224 		fp << "\t" "display_name" "\t" << host->GetDisplayName() << "\n"
225 			"\t" "alias" "\t" << host->GetDisplayName() << "\n";
226 	}
227 	if (!address.IsEmpty())
228 		fp << "\t" "address" "\t" << address << "\n";
229 	if (!address6.IsEmpty())
230 		fp << "\t" "address6" "\t" << address6 << "\n";
231 	if (!notes.IsEmpty())
232 		fp << "\t" "notes" "\t" << notes << "\n";
233 	if (!notes_url.IsEmpty())
234 		fp << "\t" "notes_url" "\t" << notes_url << "\n";
235 	if (!action_url.IsEmpty())
236 		fp << "\t" "action_url" "\t" << action_url << "\n";
237 	if (!icon_image.IsEmpty())
238 		fp << "\t" "icon_image" "\t" << icon_image << "\n";
239 	if (!icon_image_alt.IsEmpty())
240 		fp << "\t" "icon_image_alt" "\t" << icon_image_alt << "\n";
241 
242 	std::set<Checkable::Ptr> parents = host->GetParents();
243 
244 	if (!parents.empty()) {
245 		fp << "\t" "parents" "\t";
246 		DumpNameList(fp, parents);
247 		fp << "\n";
248 	}
249 
250 	ObjectLock olock(host);
251 
252 	fp << "\t" "check_interval" "\t" << (host->GetCheckInterval() / 60.0) << "\n"
253 		"\t" "retry_interval" "\t" << (host->GetRetryInterval() / 60.0) << "\n"
254 		"\t" "max_check_attempts" "\t" << host->GetMaxCheckAttempts() << "\n"
255 		"\t" "active_checks_enabled" "\t" << Convert::ToLong(host->GetEnableActiveChecks()) << "\n"
256 		"\t" "passive_checks_enabled" "\t" << Convert::ToLong(host->GetEnablePassiveChecks()) << "\n"
257 		"\t" "notifications_enabled" "\t" << Convert::ToLong(host->GetEnableNotifications()) << "\n"
258 		"\t" "notification_options" "\t" << GetNotificationOptions(host) << "\n"
259 		"\t" "notification_interval" "\t" << CompatUtility::GetCheckableNotificationNotificationInterval(host) << "\n"
260 		"\t" "event_handler_enabled" "\t" << Convert::ToLong(host->GetEnableEventHandler()) << "\n";
261 
262 	CheckCommand::Ptr checkcommand = host->GetCheckCommand();
263 	if (checkcommand)
264 		fp << "\t" "check_command" "\t" << CompatUtility::GetCommandName(checkcommand) << "!" << CompatUtility::GetCheckableCommandArgs(host) << "\n";
265 
266 	EventCommand::Ptr eventcommand = host->GetEventCommand();
267 	if (eventcommand && host->GetEnableEventHandler())
268 		fp << "\t" "event_handler" "\t" << CompatUtility::GetCommandName(eventcommand) << "\n";
269 
270 	TimePeriod::Ptr checkPeriod = host->GetCheckPeriod();
271 	if (checkPeriod)
272 		fp << "\t" "check_period" "\t" << checkPeriod->GetName() << "\n";
273 
274 	fp << "\t" "contacts" "\t";
275 	DumpNameList(fp, CompatUtility::GetCheckableNotificationUsers(host));
276 	fp << "\n";
277 
278 	fp << "\t" "contact_groups" "\t";
279 	DumpNameList(fp, CompatUtility::GetCheckableNotificationUserGroups(host));
280 	fp << "\n";
281 
282 	fp << "\t" << "initial_state" "\t" "o" "\n"
283 		"\t" "low_flap_threshold" "\t" << host->GetFlappingThresholdLow() << "\n"
284 		"\t" "high_flap_threshold" "\t" << host->GetFlappingThresholdHigh() << "\n"
285 		"\t" "process_perf_data" "\t" << Convert::ToLong(host->GetEnablePerfdata()) << "\n"
286 		"\t" "check_freshness" "\t" "1" "\n";
287 
288 	fp << "\t" "host_groups" "\t";
289 	bool first = true;
290 
291 	Array::Ptr groups = host->GetGroups();
292 
293 	if (groups) {
294 		ObjectLock olock(groups);
295 
296 		for (const String& name : groups) {
297 			HostGroup::Ptr hg = HostGroup::GetByName(name);
298 
299 			if (hg) {
300 				if (!first)
301 					fp << ",";
302 				else
303 					first = false;
304 
305 				fp << hg->GetName();
306 			}
307 		}
308 	}
309 
310 	fp << "\n";
311 
312 	DumpCustomAttributes(fp, host);
313 
314 	fp << "\t" "}" "\n" "\n";
315 }
316 
DumpCheckableStatusAttrs(std::ostream & fp,const Checkable::Ptr & checkable)317 void StatusDataWriter::DumpCheckableStatusAttrs(std::ostream& fp, const Checkable::Ptr& checkable)
318 {
319 	CheckResult::Ptr cr = checkable->GetLastCheckResult();
320 
321 	EventCommand::Ptr eventcommand = checkable->GetEventCommand();
322 	CheckCommand::Ptr checkcommand = checkable->GetCheckCommand();
323 
324 	fp << "\t" << "check_command=" << CompatUtility::GetCommandName(checkcommand) << "!" << CompatUtility::GetCheckableCommandArgs(checkable) << "\n"
325 		"\t" "event_handler=" << CompatUtility::GetCommandName(eventcommand) << "\n"
326 		"\t" "check_interval=" << (checkable->GetCheckInterval() / 60.0) << "\n"
327 		"\t" "retry_interval=" << (checkable->GetRetryInterval() / 60.0) << "\n"
328 		"\t" "has_been_checked=" << Convert::ToLong(checkable->HasBeenChecked()) << "\n"
329 		"\t" "should_be_scheduled=" << checkable->GetEnableActiveChecks() << "\n"
330 		"\t" "event_handler_enabled=" << Convert::ToLong(checkable->GetEnableEventHandler()) << "\n";
331 
332 	TimePeriod::Ptr checkPeriod = checkable->GetCheckPeriod();
333 	if (checkPeriod)
334 		fp << "\t" "check_period" "\t" << checkPeriod->GetName() << "\n";
335 
336 	if (cr) {
337 		fp << "\t" << "check_execution_time=" << Convert::ToString(cr->CalculateExecutionTime()) << "\n"
338 			"\t" "check_latency=" << Convert::ToString(cr->CalculateLatency()) << "\n";
339 	}
340 
341 	Host::Ptr host;
342 	Service::Ptr service;
343 	tie(host, service) = GetHostService(checkable);
344 
345 	if (service) {
346 		fp << "\t" "current_state=" << service->GetState() << "\n"
347 			"\t" "last_hard_state=" << service->GetLastHardState() << "\n"
348 			"\t" "last_time_ok=" << static_cast<int>(service->GetLastStateOK()) << "\n"
349 			"\t" "last_time_warn=" << static_cast<int>(service->GetLastStateWarning()) << "\n"
350 			"\t" "last_time_critical=" << static_cast<int>(service->GetLastStateCritical()) << "\n"
351 			"\t" "last_time_unknown=" << static_cast<int>(service->GetLastStateUnknown()) << "\n";
352 	} else {
353 		int currentState = host->GetState();
354 
355 		if (currentState != HostUp && !host->IsReachable())
356 			currentState = 2; /* hardcoded compat state */
357 
358 		fp << "\t" "current_state=" << currentState << "\n"
359 			"\t" "last_hard_state=" << host->GetLastHardState() << "\n"
360 			"\t" "last_time_up=" << static_cast<int>(host->GetLastStateUp()) << "\n"
361 			"\t" "last_time_down=" << static_cast<int>(host->GetLastStateDown()) << "\n";
362 	}
363 
364 	fp << "\t" "state_type=" << checkable->GetStateType() << "\n"
365 		"\t" "last_check=" << static_cast<long>(host->GetLastCheck()) << "\n";
366 
367 	if (cr) {
368 		fp << "\t" "plugin_output=" << CompatUtility::GetCheckResultOutput(cr) << "\n"
369 			"\t" "long_plugin_output=" << CompatUtility::GetCheckResultLongOutput(cr) << "\n"
370 			"\t" "performance_data=" << PluginUtility::FormatPerfdata(cr->GetPerformanceData()) << "\n";
371 	}
372 
373 	fp << "\t" << "next_check=" << static_cast<long>(checkable->GetNextCheck()) << "\n"
374 		"\t" "current_attempt=" << checkable->GetCheckAttempt() << "\n"
375 		"\t" "max_attempts=" << checkable->GetMaxCheckAttempts() << "\n"
376 		"\t" "last_state_change=" << static_cast<long>(checkable->GetLastStateChange()) << "\n"
377 		"\t" "last_hard_state_change=" << static_cast<long>(checkable->GetLastHardStateChange()) << "\n"
378 		"\t" "last_update=" << static_cast<long>(Utility::GetTime()) << "\n"
379 		"\t" "notifications_enabled=" << Convert::ToLong(checkable->GetEnableNotifications()) << "\n"
380 		"\t" "active_checks_enabled=" << Convert::ToLong(checkable->GetEnableActiveChecks()) << "\n"
381 		"\t" "passive_checks_enabled=" << Convert::ToLong(checkable->GetEnablePassiveChecks()) << "\n"
382 		"\t" "flap_detection_enabled=" << Convert::ToLong(checkable->GetEnableFlapping()) << "\n"
383 		"\t" "is_flapping=" << Convert::ToLong(checkable->IsFlapping()) << "\n"
384 		"\t" "percent_state_change=" << checkable->GetFlappingCurrent() << "\n"
385 		"\t" "problem_has_been_acknowledged=" << (checkable->GetAcknowledgement() != AcknowledgementNone ? 1 : 0) << "\n"
386 		"\t" "acknowledgement_type=" << checkable->GetAcknowledgement() << "\n"
387 		"\t" "acknowledgement_end_time=" << checkable->GetAcknowledgementExpiry() << "\n"
388 		"\t" "scheduled_downtime_depth=" << checkable->GetDowntimeDepth() << "\n"
389 		"\t" "last_notification=" << CompatUtility::GetCheckableNotificationLastNotification(checkable) << "\n"
390 		"\t" "next_notification=" << CompatUtility::GetCheckableNotificationNextNotification(checkable) << "\n"
391 		"\t" "current_notification_number=" << CompatUtility::GetCheckableNotificationNotificationNumber(checkable) << "\n"
392 		"\t" "is_reachable=" << Convert::ToLong(checkable->IsReachable()) << "\n";
393 }
394 
DumpServiceStatus(std::ostream & fp,const Service::Ptr & service)395 void StatusDataWriter::DumpServiceStatus(std::ostream& fp, const Service::Ptr& service)
396 {
397 	Host::Ptr host = service->GetHost();
398 
399 	fp << "servicestatus {" "\n"
400 		"\t" "host_name=" << host->GetName() << "\n"
401 		"\t" "service_description=" << service->GetShortName() << "\n";
402 
403 	{
404 		ObjectLock olock(service);
405 		DumpCheckableStatusAttrs(fp, service);
406 	}
407 
408 	fp << "\t" "}" "\n" "\n";
409 
410 	DumpDowntimes(fp, service);
411 	DumpComments(fp, service);
412 }
413 
DumpServiceObject(std::ostream & fp,const Service::Ptr & service)414 void StatusDataWriter::DumpServiceObject(std::ostream& fp, const Service::Ptr& service)
415 {
416 	Host::Ptr host = service->GetHost();
417 
418 	{
419 		ObjectLock olock(service);
420 
421 		fp << "define service {" "\n"
422 			"\t" "host_name" "\t" << host->GetName() << "\n"
423 			"\t" "service_description" "\t" << service->GetShortName() << "\n"
424 			"\t" "display_name" "\t" << service->GetDisplayName() << "\n"
425 			"\t" "check_interval" "\t" << (service->GetCheckInterval() / 60.0) << "\n"
426 			"\t" "retry_interval" "\t" << (service->GetRetryInterval() / 60.0) << "\n"
427 			"\t" "max_check_attempts" "\t" << service->GetMaxCheckAttempts() << "\n"
428 			"\t" "active_checks_enabled" "\t" << Convert::ToLong(service->GetEnableActiveChecks()) << "\n"
429 			"\t" "passive_checks_enabled" "\t" << Convert::ToLong(service->GetEnablePassiveChecks()) << "\n"
430 			"\t" "flap_detection_enabled" "\t" << Convert::ToLong(service->GetEnableFlapping()) << "\n"
431 			"\t" "is_volatile" "\t" << Convert::ToLong(service->GetVolatile()) << "\n"
432 			"\t" "notifications_enabled" "\t" << Convert::ToLong(service->GetEnableNotifications()) << "\n"
433 			"\t" "notification_options" "\t" << GetNotificationOptions(service) << "\n"
434 			"\t" "notification_interval" "\t" << CompatUtility::GetCheckableNotificationNotificationInterval(service) << "\n"
435 			"\t" "notification_period" "\t" << "" << "\n"
436 			"\t" "event_handler_enabled" "\t" << Convert::ToLong(service->GetEnableEventHandler()) << "\n";
437 
438 		CheckCommand::Ptr checkcommand = service->GetCheckCommand();
439 		if (checkcommand)
440 			fp << "\t" "check_command" "\t" << CompatUtility::GetCommandName(checkcommand) << "!" << CompatUtility::GetCheckableCommandArgs(service)<< "\n";
441 
442 		EventCommand::Ptr eventcommand = service->GetEventCommand();
443 		if (eventcommand && service->GetEnableEventHandler())
444 			fp << "\t" "event_handler" "\t" << CompatUtility::GetCommandName(eventcommand) << "\n";
445 
446 		TimePeriod::Ptr checkPeriod = service->GetCheckPeriod();
447 		if (checkPeriod)
448 			fp << "\t" "check_period" "\t" << checkPeriod->GetName() << "\n";
449 
450 		fp << "\t" "contacts" "\t";
451 		DumpNameList(fp, CompatUtility::GetCheckableNotificationUsers(service));
452 		fp << "\n";
453 
454 		fp << "\t" "contact_groups" "\t";
455 		DumpNameList(fp, CompatUtility::GetCheckableNotificationUserGroups(service));
456 		fp << "\n";
457 
458 		String notes = service->GetNotes();
459 		String notes_url = service->GetNotesUrl();
460 		String action_url = service->GetActionUrl();
461 		String icon_image = service->GetIconImage();
462 		String icon_image_alt = service->GetIconImageAlt();
463 
464 		fp << "\t" "initial_state" "\t" "o" "\n"
465 			"\t" "low_flap_threshold" "\t" << service->GetFlappingThresholdLow() << "\n"
466 			"\t" "high_flap_threshold" "\t" << service->GetFlappingThresholdHigh() << "\n"
467 			"\t" "process_perf_data" "\t" << Convert::ToLong(service->GetEnablePerfdata()) << "\n"
468 			"\t" "check_freshness" << "\t" "1" "\n";
469 
470 		if (!notes.IsEmpty())
471 			fp << "\t" "notes" "\t" << notes << "\n";
472 		if (!notes_url.IsEmpty())
473 			fp << "\t" "notes_url" "\t" << notes_url << "\n";
474 		if (!action_url.IsEmpty())
475 			fp << "\t" "action_url" "\t" << action_url << "\n";
476 		if (!icon_image.IsEmpty())
477 			fp << "\t" "icon_image" "\t" << icon_image << "\n";
478 		if (!icon_image_alt.IsEmpty())
479 			fp << "\t" "icon_image_alt" "\t" << icon_image_alt << "\n";
480 	}
481 
482 	fp << "\t" "service_groups" "\t";
483 	bool first = true;
484 
485 	Array::Ptr groups = service->GetGroups();
486 
487 	if (groups) {
488 		ObjectLock olock(groups);
489 
490 		for (const String& name : groups) {
491 			ServiceGroup::Ptr sg = ServiceGroup::GetByName(name);
492 
493 			if (sg) {
494 				if (!first)
495 					fp << ",";
496 				else
497 					first = false;
498 
499 				fp << sg->GetName();
500 			}
501 		}
502 	}
503 
504 	fp << "\n";
505 
506 	DumpCustomAttributes(fp, service);
507 
508 	fp << "\t" "}" "\n" "\n";
509 }
510 
DumpCustomAttributes(std::ostream & fp,const CustomVarObject::Ptr & object)511 void StatusDataWriter::DumpCustomAttributes(std::ostream& fp, const CustomVarObject::Ptr& object)
512 {
513 	Dictionary::Ptr vars = object->GetVars();
514 
515 	if (!vars)
516 		return;
517 
518 	bool is_json = false;
519 
520 	ObjectLock olock(vars);
521 	for (const Dictionary::Pair& kv : vars) {
522 		if (kv.first.IsEmpty())
523 			continue;
524 
525 		Value value;
526 
527 		if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) {
528 			value = JsonEncode(kv.second);
529 			is_json = true;
530 		} else
531 			value = CompatUtility::EscapeString(kv.second);
532 
533 		fp << "\t" "_" << kv.first << "\t" << value << "\n";
534 	}
535 
536 	if (is_json)
537 		fp << "\t" "_is_json" "\t" "1" "\n";
538 }
539 
UpdateObjectsCache()540 void StatusDataWriter::UpdateObjectsCache()
541 {
542 	CONTEXT("Writing objects.cache file");
543 
544 	/* Use the compat path here from the .ti generated class. */
545 	String objectsPath = GetObjectsPath();
546 
547 	std::fstream objectfp;
548 	String tempObjectsPath = Utility::CreateTempFile(objectsPath + ".XXXXXX", 0644, objectfp);
549 
550 	objectfp << std::fixed;
551 
552 	objectfp << "# Icinga objects cache file" "\n"
553 			"# This file is auto-generated. Do not modify this file." "\n"
554 			"\n";
555 
556 	for (const Host::Ptr& host : ConfigType::GetObjectsByType<Host>()) {
557 		std::ostringstream tempobjectfp;
558 		tempobjectfp << std::fixed;
559 		DumpHostObject(tempobjectfp, host);
560 		objectfp << tempobjectfp.str();
561 
562 		for (const Service::Ptr& service : host->GetServices()) {
563 			std::ostringstream tempobjectfp;
564 			tempobjectfp << std::fixed;
565 			DumpServiceObject(tempobjectfp, service);
566 			objectfp << tempobjectfp.str();
567 		}
568 	}
569 
570 	for (const HostGroup::Ptr& hg : ConfigType::GetObjectsByType<HostGroup>()) {
571 		std::ostringstream tempobjectfp;
572 		tempobjectfp << std::fixed;
573 
574 		String display_name = hg->GetDisplayName();
575 		String notes = hg->GetNotes();
576 		String notes_url = hg->GetNotesUrl();
577 		String action_url = hg->GetActionUrl();
578 
579 		tempobjectfp << "define hostgroup {" "\n"
580 				"\t" "hostgroup_name" "\t" << hg->GetName() << "\n";
581 
582 		if (!display_name.IsEmpty())
583 			tempobjectfp << "\t" "alias" "\t" << display_name << "\n";
584 		if (!notes.IsEmpty())
585 			tempobjectfp << "\t" "notes" "\t" << notes << "\n";
586 		if (!notes_url.IsEmpty())
587 			tempobjectfp << "\t" "notes_url" "\t" << notes_url << "\n";
588 		if (!action_url.IsEmpty())
589 			tempobjectfp << "\t" "action_url" "\t" << action_url << "\n";
590 
591 		DumpCustomAttributes(tempobjectfp, hg);
592 
593 		tempobjectfp << "\t" "members" "\t";
594 		DumpNameList(tempobjectfp, hg->GetMembers());
595 		tempobjectfp << "\n" "\t" "}" "\n";
596 
597 		objectfp << tempobjectfp.str();
598 	}
599 
600 	for (const ServiceGroup::Ptr& sg : ConfigType::GetObjectsByType<ServiceGroup>()) {
601 		std::ostringstream tempobjectfp;
602 		tempobjectfp << std::fixed;
603 
604 		String display_name = sg->GetDisplayName();
605 		String notes = sg->GetNotes();
606 		String notes_url = sg->GetNotesUrl();
607 		String action_url = sg->GetActionUrl();
608 
609 		tempobjectfp << "define servicegroup {" "\n"
610 			"\t" "servicegroup_name" "\t" << sg->GetName() << "\n";
611 
612 		if (!display_name.IsEmpty())
613 			tempobjectfp << "\t" "alias" "\t" << display_name << "\n";
614 		if (!notes.IsEmpty())
615 			tempobjectfp << "\t" "notes" "\t" << notes << "\n";
616 		if (!notes_url.IsEmpty())
617 			tempobjectfp << "\t" "notes_url" "\t" << notes_url << "\n";
618 		if (!action_url.IsEmpty())
619 			tempobjectfp << "\t" "action_url" "\t" << action_url << "\n";
620 
621 		DumpCustomAttributes(tempobjectfp, sg);
622 
623 		tempobjectfp << "\t" "members" "\t";
624 
625 		std::vector<String> sglist;
626 		for (const Service::Ptr& service : sg->GetMembers()) {
627 			Host::Ptr host = service->GetHost();
628 
629 			sglist.emplace_back(host->GetName());
630 			sglist.emplace_back(service->GetShortName());
631 		}
632 
633 		DumpStringList(tempobjectfp, sglist);
634 
635 		tempobjectfp << "\n" "}" "\n";
636 
637 		objectfp << tempobjectfp.str();
638 	}
639 
640 	for (const User::Ptr& user : ConfigType::GetObjectsByType<User>()) {
641 		std::ostringstream tempobjectfp;
642 		tempobjectfp << std::fixed;
643 
644 		String email = user->GetEmail();
645 		String pager = user->GetPager();
646 		String alias = user->GetDisplayName();
647 
648 		tempobjectfp << "define contact {" "\n"
649 				"\t" "contact_name" "\t" << user->GetName() << "\n";
650 
651 		if (!alias.IsEmpty())
652 			tempobjectfp << "\t" "alias" "\t" << alias << "\n";
653 		if (!email.IsEmpty())
654 			tempobjectfp << "\t" "email" "\t" << email << "\n";
655 		if (!pager.IsEmpty())
656 			tempobjectfp << "\t" "pager" "\t" << pager << "\n";
657 
658 		tempobjectfp << "\t" "service_notification_options" "\t" "w,u,c,r,f,s" "\n"
659 			"\t" "host_notification_options""\t" "d,u,r,f,s" "\n"
660 			"\t" "host_notifications_enabled" "\t" "1" "\n"
661 			"\t" "service_notifications_enabled" "\t" "1" "\n"
662 			"\t" "}" "\n"
663 			"\n";
664 
665 		objectfp << tempobjectfp.str();
666 	}
667 
668 	for (const UserGroup::Ptr& ug : ConfigType::GetObjectsByType<UserGroup>()) {
669 		std::ostringstream tempobjectfp;
670 		tempobjectfp << std::fixed;
671 
672 		tempobjectfp << "define contactgroup {" "\n"
673 				"\t" "contactgroup_name" "\t" << ug->GetName() << "\n"
674 				"\t" "alias" "\t" << ug->GetDisplayName() << "\n";
675 
676 		tempobjectfp << "\t" "members" "\t";
677 		DumpNameList(tempobjectfp, ug->GetMembers());
678 		tempobjectfp << "\n"
679 				"\t" "}" "\n";
680 
681 		objectfp << tempobjectfp.str();
682 	}
683 
684 	for (const Command::Ptr& command : ConfigType::GetObjectsByType<CheckCommand>()) {
685 		DumpCommand(objectfp, command);
686 	}
687 
688 	for (const Command::Ptr& command : ConfigType::GetObjectsByType<NotificationCommand>()) {
689 		DumpCommand(objectfp, command);
690 	}
691 
692 	for (const Command::Ptr& command : ConfigType::GetObjectsByType<EventCommand>()) {
693 		DumpCommand(objectfp, command);
694 	}
695 
696 	for (const TimePeriod::Ptr& tp : ConfigType::GetObjectsByType<TimePeriod>()) {
697 		DumpTimePeriod(objectfp, tp);
698 	}
699 
700 	for (const Dependency::Ptr& dep : ConfigType::GetObjectsByType<Dependency>()) {
701 		Checkable::Ptr parent = dep->GetParent();
702 
703 		if (!parent) {
704 			Log(LogDebug, "StatusDataWriter")
705 				<< "Missing parent for dependency '" << dep->GetName() << "'.";
706 			continue;
707 		}
708 
709 		Host::Ptr parent_host;
710 		Service::Ptr parent_service;
711 		tie(parent_host, parent_service) = GetHostService(parent);
712 
713 		Checkable::Ptr child = dep->GetChild();
714 
715 		if (!child) {
716 			Log(LogDebug, "StatusDataWriter")
717 				<< "Missing child for dependency '" << dep->GetName() << "'.";
718 			continue;
719 		}
720 
721 		Host::Ptr child_host;
722 		Service::Ptr child_service;
723 		tie(child_host, child_service) = GetHostService(child);
724 
725 		int state_filter = dep->GetStateFilter();
726 		std::vector<String> failure_criteria;
727 		if (state_filter & StateFilterOK || state_filter & StateFilterUp)
728 			failure_criteria.emplace_back("o");
729 		if (state_filter & StateFilterWarning)
730 			failure_criteria.emplace_back("w");
731 		if (state_filter & StateFilterCritical)
732 			failure_criteria.emplace_back("c");
733 		if (state_filter & StateFilterUnknown)
734 			failure_criteria.emplace_back("u");
735 		if (state_filter & StateFilterDown)
736 			failure_criteria.emplace_back("d");
737 
738 		String criteria = boost::algorithm::join(failure_criteria, ",");
739 
740 		/* Icinga 1.x only allows host->host, service->service dependencies */
741 		if (!child_service && !parent_service) {
742 			objectfp << "define hostdependency {" "\n"
743 				"\t" "dependent_host_name" "\t" << child_host->GetName() << "\n"
744 				"\t" "host_name" "\t" << parent_host->GetName() << "\n"
745 				"\t" "execution_failure_criteria" "\t" << criteria << "\n"
746 				"\t" "notification_failure_criteria" "\t" << criteria << "\n"
747 				"\t" "}" "\n"
748 				"\n";
749 		} else if (child_service && parent_service){
750 
751 			objectfp << "define servicedependency {" "\n"
752 				"\t" "dependent_host_name" "\t" << child_service->GetHost()->GetName() << "\n"
753 				"\t" "dependent_service_description" "\t" << child_service->GetShortName() << "\n"
754 				"\t" "host_name" "\t" << parent_service->GetHost()->GetName() << "\n"
755 				"\t" "service_description" "\t" << parent_service->GetShortName() << "\n"
756 				"\t" "execution_failure_criteria" "\t" << criteria << "\n"
757 				"\t" "notification_failure_criteria" "\t" << criteria << "\n"
758 				"\t" "}" "\n"
759 				"\n";
760 		}
761 	}
762 
763 	objectfp.close();
764 
765 	Utility::RenameFile(tempObjectsPath, objectsPath);
766 }
767 
768 /**
769  * Periodically writes the status.dat and objects.cache files.
770  */
StatusTimerHandler()771 void StatusDataWriter::StatusTimerHandler()
772 {
773 	if (m_ObjectsCacheOutdated) {
774 		UpdateObjectsCache();
775 		m_ObjectsCacheOutdated = false;
776 	}
777 
778 	double start = Utility::GetTime();
779 
780 	String statusPath = GetStatusPath();
781 
782 	std::fstream statusfp;
783 	String tempStatusPath = Utility::CreateTempFile(statusPath + ".XXXXXX", 0644, statusfp);
784 
785 	statusfp << std::fixed;
786 
787 	statusfp << "# Icinga status file" "\n"
788 			"# This file is auto-generated. Do not modify this file." "\n"
789 			"\n";
790 
791 	statusfp << "info {" "\n"
792 		"\t" "created=" << Utility::GetTime() << "\n"
793 		"\t" "version=" << Application::GetAppVersion() << "\n"
794 		"\t" "}" "\n"
795 		"\n";
796 
797 	statusfp << "programstatus {" "\n"
798 		"\t" "icinga_pid=" << Utility::GetPid() << "\n"
799 		"\t" "daemon_mode=1" "\n"
800 		"\t" "program_start=" << static_cast<long>(Application::GetStartTime()) << "\n"
801 		"\t" "active_host_checks_enabled=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnableHostChecks()) << "\n"
802 		"\t" "passive_host_checks_enabled=1" "\n"
803 		"\t" "active_service_checks_enabled=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnableServiceChecks()) << "\n"
804 		"\t" "passive_service_checks_enabled=1" "\n"
805 		"\t" "check_service_freshness=1" "\n"
806 		"\t" "check_host_freshness=1" "\n"
807 		"\t" "enable_notifications=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnableNotifications()) << "\n"
808 		"\t" "enable_event_handlers=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnableEventHandlers()) << "\n"
809 		"\t" "enable_flap_detection=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnableFlapping()) << "\n"
810 		"\t" "enable_failure_prediction=0" "\n"
811 		"\t" "process_performance_data=" << Convert::ToLong(IcingaApplication::GetInstance()->GetEnablePerfdata()) << "\n"
812 		"\t" "active_scheduled_host_check_stats=" << CIB::GetActiveHostChecksStatistics(60) << "," << CIB::GetActiveHostChecksStatistics(5 * 60) << "," << CIB::GetActiveHostChecksStatistics(15 * 60) << "\n"
813 		"\t" "passive_host_check_stats=" << CIB::GetPassiveHostChecksStatistics(60) << "," << CIB::GetPassiveHostChecksStatistics(5 * 60) << "," << CIB::GetPassiveHostChecksStatistics(15 * 60) << "\n"
814 		"\t" "active_scheduled_service_check_stats=" << CIB::GetActiveServiceChecksStatistics(60) << "," << CIB::GetActiveServiceChecksStatistics(5 * 60) << "," << CIB::GetActiveServiceChecksStatistics(15 * 60) << "\n"
815 		"\t" "passive_service_check_stats=" << CIB::GetPassiveServiceChecksStatistics(60) << "," << CIB::GetPassiveServiceChecksStatistics(5 * 60) << "," << CIB::GetPassiveServiceChecksStatistics(15 * 60) << "\n"
816 		"\t" "next_downtime_id=" << Downtime::GetNextDowntimeID() << "\n"
817 		"\t" "next_comment_id=" << Comment::GetNextCommentID() << "\n";
818 
819 	statusfp << "\t" "}" "\n"
820 			"\n";
821 
822 	for (const Host::Ptr& host : ConfigType::GetObjectsByType<Host>()) {
823 		std::ostringstream tempstatusfp;
824 		tempstatusfp << std::fixed;
825 		DumpHostStatus(tempstatusfp, host);
826 		statusfp << tempstatusfp.str();
827 
828 		for (const Service::Ptr& service : host->GetServices()) {
829 			std::ostringstream tempstatusfp;
830 			tempstatusfp << std::fixed;
831 			DumpServiceStatus(tempstatusfp, service);
832 			statusfp << tempstatusfp.str();
833 		}
834 	}
835 
836 	statusfp.close();
837 
838 	Utility::RenameFile(tempStatusPath, statusPath);
839 
840 	Log(LogNotice, "StatusDataWriter")
841 		<< "Writing status.dat file took " << Utility::FormatDuration(Utility::GetTime() - start);
842 }
843 
ObjectHandler()844 void StatusDataWriter::ObjectHandler()
845 {
846 	m_ObjectsCacheOutdated = true;
847 }
848 
GetNotificationOptions(const Checkable::Ptr & checkable)849 String StatusDataWriter::GetNotificationOptions(const Checkable::Ptr& checkable)
850 {
851 	Host::Ptr host;
852 	Service::Ptr service;
853 	tie(host, service) = GetHostService(checkable);
854 
855 	unsigned long notification_type_filter = 0;
856 	unsigned long notification_state_filter = 0;
857 
858 	for (const Notification::Ptr& notification : checkable->GetNotifications()) {
859 		notification_type_filter |= notification->GetTypeFilter();
860 		notification_state_filter |= notification->GetStateFilter();
861 	}
862 
863 	std::vector<String> notification_options;
864 
865 	/* notification state filters */
866 	if (service) {
867 		if (notification_state_filter & ServiceWarning) {
868 			notification_options.push_back("w");
869 		}
870 		if (notification_state_filter & ServiceUnknown) {
871 			notification_options.push_back("u");
872 		}
873 		if (notification_state_filter & ServiceCritical) {
874 			notification_options.push_back("c");
875 		}
876 	} else {
877 		if (notification_state_filter & HostDown) {
878 			notification_options.push_back("d");
879 		}
880 	}
881 
882 	/* notification type filters */
883 	if (notification_type_filter & NotificationRecovery) {
884 		notification_options.push_back("r");
885 	}
886 	if ((notification_type_filter & NotificationFlappingStart) ||
887 	    (notification_type_filter & NotificationFlappingEnd)) {
888 		notification_options.push_back("f");
889 	}
890 	if ((notification_type_filter & NotificationDowntimeStart) ||
891 	    (notification_type_filter & NotificationDowntimeEnd) ||
892 	    (notification_type_filter & NotificationDowntimeRemoved)) {
893 		notification_options.push_back("s");
894 	}
895 
896 	return boost::algorithm::join(notification_options, ",");
897 }
898