1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "config/configitem.hpp"
4 #include "config/configcompilercontext.hpp"
5 #include "config/applyrule.hpp"
6 #include "config/objectrule.hpp"
7 #include "config/configcompiler.hpp"
8 #include "base/application.hpp"
9 #include "base/configtype.hpp"
10 #include "base/objectlock.hpp"
11 #include "base/convert.hpp"
12 #include "base/logger.hpp"
13 #include "base/debug.hpp"
14 #include "base/workqueue.hpp"
15 #include "base/exception.hpp"
16 #include "base/stdiostream.hpp"
17 #include "base/netstring.hpp"
18 #include "base/serializer.hpp"
19 #include "base/json.hpp"
20 #include "base/exception.hpp"
21 #include "base/function.hpp"
22 #include "base/utility.hpp"
23 #include <boost/algorithm/string/join.hpp>
24 #include <sstream>
25 #include <fstream>
26 #include <algorithm>
27 #include <random>
28 
29 using namespace icinga;
30 
31 std::mutex ConfigItem::m_Mutex;
32 ConfigItem::TypeMap ConfigItem::m_Items;
33 ConfigItem::TypeMap ConfigItem::m_DefaultTemplates;
34 ConfigItem::ItemList ConfigItem::m_UnnamedItems;
35 ConfigItem::IgnoredItemList ConfigItem::m_IgnoredItems;
36 
37 REGISTER_FUNCTION(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext, "func");
38 
39 /**
40  * Constructor for the ConfigItem class.
41  *
42  * @param type The object type.
43  * @param name The name of the item.
44  * @param unit The unit of the item.
45  * @param abstract Whether the item is a template.
46  * @param exprl Expression list for the item.
47  * @param debuginfo Debug information.
48  */
ConfigItem(Type::Ptr type,String name,bool abstract,Expression::Ptr exprl,Expression::Ptr filter,bool defaultTmpl,bool ignoreOnError,DebugInfo debuginfo,Dictionary::Ptr scope,String zone,String package)49 ConfigItem::ConfigItem(Type::Ptr type, String name,
50 	bool abstract, Expression::Ptr exprl,
51 	Expression::Ptr filter, bool defaultTmpl, bool ignoreOnError,
52 	DebugInfo debuginfo, Dictionary::Ptr scope,
53 	String zone, String package)
54 	: m_Type(std::move(type)), m_Name(std::move(name)), m_Abstract(abstract),
55 	m_Expression(std::move(exprl)), m_Filter(std::move(filter)),
56 	m_DefaultTmpl(defaultTmpl), m_IgnoreOnError(ignoreOnError),
57 	m_DebugInfo(std::move(debuginfo)), m_Scope(std::move(scope)), m_Zone(std::move(zone)),
58 	m_Package(std::move(package))
59 {
60 }
61 
62 /**
63  * Retrieves the type of the configuration item.
64  *
65  * @returns The type.
66  */
GetType() const67 Type::Ptr ConfigItem::GetType() const
68 {
69 	return m_Type;
70 }
71 
72 /**
73  * Retrieves the name of the configuration item.
74  *
75  * @returns The name.
76  */
GetName() const77 String ConfigItem::GetName() const
78 {
79 	return m_Name;
80 }
81 
82 /**
83  * Checks whether the item is abstract.
84  *
85  * @returns true if the item is abstract, false otherwise.
86  */
IsAbstract() const87 bool ConfigItem::IsAbstract() const
88 {
89 	return m_Abstract;
90 }
91 
IsDefaultTemplate() const92 bool ConfigItem::IsDefaultTemplate() const
93 {
94 	return m_DefaultTmpl;
95 }
96 
IsIgnoreOnError() const97 bool ConfigItem::IsIgnoreOnError() const
98 {
99 	return m_IgnoreOnError;
100 }
101 
102 /**
103  * Retrieves the debug information for the configuration item.
104  *
105  * @returns The debug information.
106  */
GetDebugInfo() const107 DebugInfo ConfigItem::GetDebugInfo() const
108 {
109 	return m_DebugInfo;
110 }
111 
GetScope() const112 Dictionary::Ptr ConfigItem::GetScope() const
113 {
114 	return m_Scope;
115 }
116 
GetObject() const117 ConfigObject::Ptr ConfigItem::GetObject() const
118 {
119 	return m_Object;
120 }
121 
122 /**
123  * Retrieves the expression list for the configuration item.
124  *
125  * @returns The expression list.
126  */
GetExpression() const127 Expression::Ptr ConfigItem::GetExpression() const
128 {
129 	return m_Expression;
130 }
131 
132 /**
133 * Retrieves the object filter for the configuration item.
134 *
135 * @returns The filter expression.
136 */
GetFilter() const137 Expression::Ptr ConfigItem::GetFilter() const
138 {
139 	return m_Filter;
140 }
141 
142 class DefaultValidationUtils final : public ValidationUtils
143 {
144 public:
ValidateName(const String & type,const String & name) const145 	bool ValidateName(const String& type, const String& name) const override
146 	{
147 		ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
148 
149 		if (!item || (item && item->IsAbstract()))
150 			return false;
151 
152 		return true;
153 	}
154 };
155 
156 /**
157  * Commits the configuration item by creating a ConfigObject
158  * object.
159  *
160  * @returns The ConfigObject that was created/updated.
161  */
Commit(bool discard)162 ConfigObject::Ptr ConfigItem::Commit(bool discard)
163 {
164 	Type::Ptr type = GetType();
165 
166 #ifdef I2_DEBUG
167 	Log(LogDebug, "ConfigItem")
168 		<< "Commit called for ConfigItem Type=" << type->GetName() << ", Name=" << GetName();
169 #endif /* I2_DEBUG */
170 
171 	/* Make sure the type is valid. */
172 	if (!type || !ConfigObject::TypeInstance->IsAssignableFrom(type))
173 		BOOST_THROW_EXCEPTION(ScriptError("Type '" + type->GetName() + "' does not exist.", m_DebugInfo));
174 
175 	if (IsAbstract())
176 		return nullptr;
177 
178 	ConfigObject::Ptr dobj = static_pointer_cast<ConfigObject>(type->Instantiate(std::vector<Value>()));
179 
180 	dobj->SetDebugInfo(m_DebugInfo);
181 	dobj->SetZoneName(m_Zone);
182 	dobj->SetPackage(m_Package);
183 	dobj->SetName(m_Name);
184 
185 	DebugHint debugHints;
186 
187 	ScriptFrame frame(true, dobj);
188 	if (m_Scope)
189 		m_Scope->CopyTo(frame.Locals);
190 	try {
191 		m_Expression->Evaluate(frame, &debugHints);
192 	} catch (const std::exception& ex) {
193 		if (m_IgnoreOnError) {
194 			Log(LogNotice, "ConfigObject")
195 				<< "Ignoring config object '" << m_Name << "' of type '" << type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
196 
197 			{
198 				std::unique_lock<std::mutex> lock(m_Mutex);
199 				m_IgnoredItems.push_back(m_DebugInfo.Path);
200 			}
201 
202 			return nullptr;
203 		}
204 
205 		throw;
206 	}
207 
208 	if (discard)
209 		m_Expression.reset();
210 
211 	String item_name;
212 	String short_name = dobj->GetShortName();
213 
214 	if (!short_name.IsEmpty()) {
215 		item_name = short_name;
216 		dobj->SetName(short_name);
217 	} else
218 		item_name = m_Name;
219 
220 	String name = item_name;
221 
222 	auto *nc = dynamic_cast<NameComposer *>(type.get());
223 
224 	if (nc) {
225 		if (name.IsEmpty())
226 			BOOST_THROW_EXCEPTION(ScriptError("Object name must not be empty.", m_DebugInfo));
227 
228 		name = nc->MakeName(name, dobj);
229 
230 		if (name.IsEmpty())
231 			BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine name for object"));
232 	}
233 
234 	if (name != item_name)
235 		dobj->SetShortName(item_name);
236 
237 	dobj->SetName(name);
238 
239 	Dictionary::Ptr dhint = debugHints.ToDictionary();
240 
241 	try {
242 		DefaultValidationUtils utils;
243 		dobj->Validate(FAConfig, utils);
244 	} catch (ValidationError& ex) {
245 		if (m_IgnoreOnError) {
246 			Log(LogNotice, "ConfigObject")
247 				<< "Ignoring config object '" << m_Name << "' of type '" << type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
248 
249 			{
250 				std::unique_lock<std::mutex> lock(m_Mutex);
251 				m_IgnoredItems.push_back(m_DebugInfo.Path);
252 			}
253 
254 			return nullptr;
255 		}
256 
257 		ex.SetDebugHint(dhint);
258 		throw;
259 	}
260 
261 	try {
262 		dobj->OnConfigLoaded();
263 	} catch (const std::exception& ex) {
264 		if (m_IgnoreOnError) {
265 			Log(LogNotice, "ConfigObject")
266 				<< "Ignoring config object '" << m_Name << "' of type '" << m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
267 
268 			{
269 				std::unique_lock<std::mutex> lock(m_Mutex);
270 				m_IgnoredItems.push_back(m_DebugInfo.Path);
271 			}
272 
273 			return nullptr;
274 		}
275 
276 		throw;
277 	}
278 
279 	Value serializedObject;
280 
281 	try {
282 		serializedObject = Serialize(dobj, FAConfig);
283 	} catch (const CircularReferenceError& ex) {
284 		BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed"));
285 	}
286 
287 	Dictionary::Ptr persistentItem = new Dictionary({
288 		{ "type", type->GetName() },
289 		{ "name", GetName() },
290 		{ "properties", Serialize(dobj, FAConfig) },
291 		{ "debug_hints", dhint },
292 		{ "debug_info", new Array({
293 			m_DebugInfo.Path,
294 			m_DebugInfo.FirstLine,
295 			m_DebugInfo.FirstColumn,
296 			m_DebugInfo.LastLine,
297 			m_DebugInfo.LastColumn,
298 		}) }
299 	});
300 
301 	dhint.reset();
302 
303 	ConfigCompilerContext::GetInstance()->WriteObject(persistentItem);
304 	persistentItem.reset();
305 
306 	dobj->Register();
307 
308 	m_Object = dobj;
309 
310 	return dobj;
311 }
312 
313 /**
314  * Registers the configuration item.
315  */
Register()316 void ConfigItem::Register()
317 {
318 	m_ActivationContext = ActivationContext::GetCurrentContext();
319 
320 	std::unique_lock<std::mutex> lock(m_Mutex);
321 
322 	/* If this is a non-abstract object with a composite name
323 	 * we register it in m_UnnamedItems instead of m_Items. */
324 	if (!m_Abstract && dynamic_cast<NameComposer *>(m_Type.get()))
325 		m_UnnamedItems.emplace_back(this);
326 	else {
327 		auto& items = m_Items[m_Type];
328 
329 		auto it = items.find(m_Name);
330 
331 		if (it != items.end()) {
332 			std::ostringstream msgbuf;
333 			msgbuf << "A configuration item of type '" << m_Type->GetName()
334 					<< "' and name '" << GetName() << "' already exists ("
335 					<< it->second->GetDebugInfo() << "), new declaration: " << GetDebugInfo();
336 			BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str()));
337 		}
338 
339 		m_Items[m_Type][m_Name] = this;
340 
341 		if (m_DefaultTmpl)
342 			m_DefaultTemplates[m_Type][m_Name] = this;
343 	}
344 }
345 
346 /**
347  * Unregisters the configuration item.
348  */
Unregister()349 void ConfigItem::Unregister()
350 {
351 	if (m_Object) {
352 		m_Object->Unregister();
353 		m_Object.reset();
354 	}
355 
356 	std::unique_lock<std::mutex> lock(m_Mutex);
357 	m_UnnamedItems.erase(std::remove(m_UnnamedItems.begin(), m_UnnamedItems.end(), this), m_UnnamedItems.end());
358 	m_Items[m_Type].erase(m_Name);
359 	m_DefaultTemplates[m_Type].erase(m_Name);
360 }
361 
362 /**
363  * Retrieves a configuration item by type and name.
364  *
365  * @param type The type of the ConfigItem that is to be looked up.
366  * @param name The name of the ConfigItem that is to be looked up.
367  * @returns The configuration item.
368  */
GetByTypeAndName(const Type::Ptr & type,const String & name)369 ConfigItem::Ptr ConfigItem::GetByTypeAndName(const Type::Ptr& type, const String& name)
370 {
371 	std::unique_lock<std::mutex> lock(m_Mutex);
372 
373 	auto it = m_Items.find(type);
374 
375 	if (it == m_Items.end())
376 		return nullptr;
377 
378 	auto it2 = it->second.find(name);
379 
380 	if (it2 == it->second.end())
381 		return nullptr;
382 
383 	return it2->second;
384 }
385 
CommitNewItems(const ActivationContext::Ptr & context,WorkQueue & upq,std::vector<ConfigItem::Ptr> & newItems)386 bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems)
387 {
388 	typedef std::pair<ConfigItem::Ptr, bool> ItemPair;
389 	std::vector<ItemPair> items;
390 
391 	{
392 		std::unique_lock<std::mutex> lock(m_Mutex);
393 
394 		for (const TypeMap::value_type& kv : m_Items) {
395 			for (const ItemMap::value_type& kv2 : kv.second) {
396 				if (kv2.second->m_Abstract || kv2.second->m_Object)
397 					continue;
398 
399 				if (kv2.second->m_ActivationContext != context)
400 					continue;
401 
402 				items.emplace_back(kv2.second, false);
403 			}
404 		}
405 
406 		ItemList newUnnamedItems;
407 
408 		for (const ConfigItem::Ptr& item : m_UnnamedItems) {
409 			if (item->m_ActivationContext != context) {
410 				newUnnamedItems.push_back(item);
411 				continue;
412 			}
413 
414 			if (item->m_Abstract || item->m_Object)
415 				continue;
416 
417 			items.emplace_back(item, true);
418 		}
419 
420 		m_UnnamedItems.swap(newUnnamedItems);
421 	}
422 
423 	if (items.empty())
424 		return true;
425 
426 	// Shuffle all items to evenly distribute them over the threads of the workqueue. This increases perfomance
427 	// noticably in environments with lots of objects and available threads.
428 	std::shuffle(std::begin(items), std::end(items), std::default_random_engine {});
429 
430 #ifdef I2_DEBUG
431 	Log(LogDebug, "configitem")
432 		<< "Committing " << items.size() << " new items.";
433 #endif /* I2_DEBUG */
434 
435 	for (const auto& ip : items)
436 		newItems.push_back(ip.first);
437 
438 	std::set<Type::Ptr> types;
439 	std::set<Type::Ptr> completed_types;
440 
441 	for (const Type::Ptr& type : Type::GetAllTypes()) {
442 		if (ConfigObject::TypeInstance->IsAssignableFrom(type))
443 			types.insert(type);
444 	}
445 
446 	while (types.size() != completed_types.size()) {
447 		for (const Type::Ptr& type : types) {
448 			if (completed_types.find(type) != completed_types.end())
449 				continue;
450 
451 			bool unresolved_dep = false;
452 
453 			/* skip this type (for now) if there are unresolved load dependencies */
454 			for (const String& loadDep : type->GetLoadDependencies()) {
455 				Type::Ptr pLoadDep = Type::GetByName(loadDep);
456 				if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) {
457 					unresolved_dep = true;
458 					break;
459 				}
460 			}
461 
462 			if (unresolved_dep)
463 				continue;
464 
465 			int committed_items = 0;
466 			upq.ParallelFor(items, [&type, &committed_items](const ItemPair& ip) {
467 				const ConfigItem::Ptr& item = ip.first;
468 
469 				if (item->m_Type != type)
470 					return;
471 
472 				ip.first->Commit(ip.second);
473 				committed_items++;
474 			});
475 
476 			upq.Join();
477 
478 			completed_types.insert(type);
479 
480 #ifdef I2_DEBUG
481 			if (committed_items > 0)
482 				Log(LogDebug, "configitem")
483 					<< "Committed " << committed_items << " items of type '" << type->GetName() << "'.";
484 #endif /* I2_DEBUG */
485 
486 			if (upq.HasExceptions())
487 				return false;
488 		}
489 	}
490 
491 #ifdef I2_DEBUG
492 	Log(LogDebug, "configitem")
493 		<< "Committed " << items.size() << " items.";
494 #endif /* I2_DEBUG */
495 
496 	completed_types.clear();
497 
498 	while (types.size() != completed_types.size()) {
499 		for (const Type::Ptr& type : types) {
500 			if (completed_types.find(type) != completed_types.end())
501 				continue;
502 
503 			bool unresolved_dep = false;
504 
505 			/* skip this type (for now) if there are unresolved load dependencies */
506 			for (const String& loadDep : type->GetLoadDependencies()) {
507 				Type::Ptr pLoadDep = Type::GetByName(loadDep);
508 				if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) {
509 					unresolved_dep = true;
510 					break;
511 				}
512 			}
513 
514 			if (unresolved_dep)
515 				continue;
516 
517 			int notified_items = 0;
518 			upq.ParallelFor(items, [&type, &notified_items](const ItemPair& ip) {
519 				const ConfigItem::Ptr& item = ip.first;
520 
521 				if (!item->m_Object || item->m_Type != type)
522 					return;
523 
524 				try {
525 					item->m_Object->OnAllConfigLoaded();
526 					notified_items++;
527 				} catch (const std::exception& ex) {
528 					if (!item->m_IgnoreOnError)
529 						throw;
530 
531 					Log(LogNotice, "ConfigObject")
532 						<< "Ignoring config object '" << item->m_Name << "' of type '" << item->m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex);
533 
534 					item->Unregister();
535 
536 					{
537 						std::unique_lock<std::mutex> lock(item->m_Mutex);
538 						item->m_IgnoredItems.push_back(item->m_DebugInfo.Path);
539 					}
540 				}
541 			});
542 
543 			completed_types.insert(type);
544 
545 			upq.Join();
546 
547 #ifdef I2_DEBUG
548 			if (notified_items > 0)
549 				Log(LogDebug, "configitem")
550 					<< "Sent OnAllConfigLoaded to " << notified_items << " items of type '" << type->GetName() << "'.";
551 #endif /* I2_DEBUG */
552 
553 			if (upq.HasExceptions())
554 				return false;
555 
556 			notified_items = 0;
557 			for (const String& loadDep : type->GetLoadDependencies()) {
558 				upq.ParallelFor(items, [loadDep, &type, &notified_items](const ItemPair& ip) {
559 					const ConfigItem::Ptr& item = ip.first;
560 
561 					if (!item->m_Object || item->m_Type->GetName() != loadDep)
562 						return;
563 
564 					ActivationScope ascope(item->m_ActivationContext);
565 					item->m_Object->CreateChildObjects(type);
566 					notified_items++;
567 				});
568 			}
569 
570 			upq.Join();
571 
572 #ifdef I2_DEBUG
573 			if (notified_items > 0)
574 				Log(LogDebug, "configitem")
575 					<< "Sent CreateChildObjects to " << notified_items << " items of type '" << type->GetName() << "'.";
576 #endif /* I2_DEBUG */
577 
578 			if (upq.HasExceptions())
579 				return false;
580 
581 			// Make sure to activate any additionally generated items
582 			if (!CommitNewItems(context, upq, newItems))
583 				return false;
584 		}
585 	}
586 
587 	return true;
588 }
589 
CommitItems(const ActivationContext::Ptr & context,WorkQueue & upq,std::vector<ConfigItem::Ptr> & newItems,bool silent)590 bool ConfigItem::CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems, bool silent)
591 {
592 	if (!silent)
593 		Log(LogInformation, "ConfigItem", "Committing config item(s).");
594 
595 	if (!CommitNewItems(context, upq, newItems)) {
596 		upq.ReportExceptions("config");
597 
598 		for (const ConfigItem::Ptr& item : newItems) {
599 			item->Unregister();
600 		}
601 
602 		return false;
603 	}
604 
605 	ApplyRule::CheckMatches(silent);
606 
607 	if (!silent) {
608 		/* log stats for external parsers */
609 		typedef std::map<Type::Ptr, int> ItemCountMap;
610 		ItemCountMap itemCounts;
611 		for (const ConfigItem::Ptr& item : newItems) {
612 			if (!item->m_Object)
613 				continue;
614 
615 			itemCounts[item->m_Object->GetReflectionType()]++;
616 		}
617 
618 		for (const ItemCountMap::value_type& kv : itemCounts) {
619 			Log(LogInformation, "ConfigItem")
620 				<< "Instantiated " << kv.second << " " << (kv.second != 1 ? kv.first->GetPluralName() : kv.first->GetName()) << ".";
621 		}
622 	}
623 
624 	return true;
625 }
626 
627 /**
628  * ActivateItems activates new config items.
629  *
630  * @param newItems Vector of items to be activated
631  * @param runtimeCreated Whether the objects were created by a runtime object
632  * @param mainConfigActivation Whether this is the call for activating the main configuration during startup
633  * @param withModAttrs Whether this call shall read the modified attributes file
634  * @param cookie Cookie for preventing message loops
635  * @return Whether the config activation was successful (in case of errors, exceptions are thrown)
636  */
ActivateItems(const std::vector<ConfigItem::Ptr> & newItems,bool runtimeCreated,bool mainConfigActivation,bool withModAttrs,const Value & cookie)637 bool ConfigItem::ActivateItems(const std::vector<ConfigItem::Ptr>& newItems, bool runtimeCreated,
638 	bool mainConfigActivation, bool withModAttrs, const Value& cookie)
639 {
640 	static std::mutex mtx;
641 	std::unique_lock<std::mutex> lock(mtx);
642 
643 	if (withModAttrs) {
644 		/* restore modified attributes */
645 		if (Utility::PathExists(Configuration::ModAttrPath)) {
646 			std::unique_ptr<Expression> expression = ConfigCompiler::CompileFile(Configuration::ModAttrPath);
647 
648 			if (expression) {
649 				try {
650 					ScriptFrame frame(true);
651 					expression->Evaluate(frame);
652 				} catch (const std::exception& ex) {
653 					Log(LogCritical, "config", DiagnosticInformation(ex));
654 				}
655 			}
656 		}
657 	}
658 
659 	for (const ConfigItem::Ptr& item : newItems) {
660 		if (!item->m_Object)
661 			continue;
662 
663 		ConfigObject::Ptr object = item->m_Object;
664 
665 		if (object->IsActive())
666 			continue;
667 
668 #ifdef I2_DEBUG
669 		Log(LogDebug, "ConfigItem")
670 			<< "Setting 'active' to true for object '" << object->GetName() << "' of type '" << object->GetReflectionType()->GetName() << "'";
671 #endif /* I2_DEBUG */
672 
673 		object->PreActivate();
674 	}
675 
676 	if (mainConfigActivation)
677 		Log(LogInformation, "ConfigItem", "Triggering Start signal for config items");
678 
679 	/* Activate objects in priority order. */
680 	std::vector<Type::Ptr> types = Type::GetAllTypes();
681 
682 	std::sort(types.begin(), types.end(), [](const Type::Ptr& a, const Type::Ptr& b) {
683 		if (a->GetActivationPriority() < b->GetActivationPriority())
684 			return true;
685 		return false;
686 	});
687 
688 	/* Find the last logger type to be activated. */
689 	Type::Ptr lastLoggerType = nullptr;
690 	for (const Type::Ptr& type : types) {
691 		if (Logger::TypeInstance->IsAssignableFrom(type)) {
692 			lastLoggerType = type;
693 		}
694 	}
695 
696 	for (const Type::Ptr& type : types) {
697 		for (const ConfigItem::Ptr& item : newItems) {
698 			if (!item->m_Object)
699 				continue;
700 
701 			ConfigObject::Ptr object = item->m_Object;
702 			Type::Ptr objectType = object->GetReflectionType();
703 
704 			if (objectType != type)
705 				continue;
706 
707 #ifdef I2_DEBUG
708 			Log(LogDebug, "ConfigItem")
709 				<< "Activating object '" << object->GetName() << "' of type '"
710 				<< objectType->GetName() << "' with priority "
711 				<< objectType->GetActivationPriority();
712 #endif /* I2_DEBUG */
713 
714 			object->Activate(runtimeCreated, cookie);
715 		}
716 
717 		if (mainConfigActivation && type == lastLoggerType) {
718 			/* Disable early logging configuration once the last logger type was activated. */
719 			Logger::DisableEarlyLogging();
720 		}
721 	}
722 
723 	if (mainConfigActivation)
724 		Log(LogInformation, "ConfigItem", "Activated all objects.");
725 
726 	return true;
727 }
728 
RunWithActivationContext(const Function::Ptr & function)729 bool ConfigItem::RunWithActivationContext(const Function::Ptr& function)
730 {
731 	ActivationScope scope;
732 
733 	if (!function)
734 		BOOST_THROW_EXCEPTION(ScriptError("'function' argument must not be null."));
735 
736 	function->Invoke();
737 
738 	WorkQueue upq(25000, Configuration::Concurrency);
739 	upq.SetName("ConfigItem::RunWithActivationContext");
740 
741 	std::vector<ConfigItem::Ptr> newItems;
742 
743 	if (!CommitItems(scope.GetContext(), upq, newItems, true))
744 		return false;
745 
746 	if (!ActivateItems(newItems, false, false))
747 		return false;
748 
749 	return true;
750 }
751 
GetItems(const Type::Ptr & type)752 std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const Type::Ptr& type)
753 {
754 	std::vector<ConfigItem::Ptr> items;
755 
756 	std::unique_lock<std::mutex> lock(m_Mutex);
757 
758 	auto it = m_Items.find(type);
759 
760 	if (it == m_Items.end())
761 		return items;
762 
763 	items.reserve(it->second.size());
764 
765 	for (const ItemMap::value_type& kv : it->second) {
766 		items.push_back(kv.second);
767 	}
768 
769 	return items;
770 }
771 
GetDefaultTemplates(const Type::Ptr & type)772 std::vector<ConfigItem::Ptr> ConfigItem::GetDefaultTemplates(const Type::Ptr& type)
773 {
774 	std::vector<ConfigItem::Ptr> items;
775 
776 	std::unique_lock<std::mutex> lock(m_Mutex);
777 
778 	auto it = m_DefaultTemplates.find(type);
779 
780 	if (it == m_DefaultTemplates.end())
781 		return items;
782 
783 	items.reserve(it->second.size());
784 
785 	for (const ItemMap::value_type& kv : it->second) {
786 		items.push_back(kv.second);
787 	}
788 
789 	return items;
790 }
791 
RemoveIgnoredItems(const String & allowedConfigPath)792 void ConfigItem::RemoveIgnoredItems(const String& allowedConfigPath)
793 {
794 	std::unique_lock<std::mutex> lock(m_Mutex);
795 
796 	for (const String& path : m_IgnoredItems) {
797 		if (path.Find(allowedConfigPath) == String::NPos)
798 			continue;
799 
800 		Log(LogNotice, "ConfigItem")
801 			<< "Removing ignored item path '" << path << "'.";
802 
803 		(void) unlink(path.CStr());
804 	}
805 
806 	m_IgnoredItems.clear();
807 }
808