1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "base/configobject.hpp"
4 #include "base/configobject-ti.cpp"
5 #include "base/configtype.hpp"
6 #include "base/serializer.hpp"
7 #include "base/netstring.hpp"
8 #include "base/json.hpp"
9 #include "base/stdiostream.hpp"
10 #include "base/debug.hpp"
11 #include "base/objectlock.hpp"
12 #include "base/logger.hpp"
13 #include "base/exception.hpp"
14 #include "base/function.hpp"
15 #include "base/initialize.hpp"
16 #include "base/workqueue.hpp"
17 #include "base/context.hpp"
18 #include "base/application.hpp"
20 #include <boost/exception/errinfo_api_function.hpp>
21 #include <boost/exception/errinfo_errno.hpp>
22 #include <boost/exception/errinfo_file_name.hpp>
24 using namespace icinga;
26 REGISTER_TYPE_WITH_PROTOTYPE(ConfigObject, ConfigObject::GetPrototype());
28 boost::signals2::signal<void (const ConfigObject::Ptr&)> ConfigObject::OnStateChanged;
30 bool ConfigObject::IsActive() const
35 bool ConfigObject::IsPaused() const
40 void ConfigObject::SetExtension(const String& key, const Value& value)
42 Dictionary::Ptr extensions = GetExtensions();
45 extensions = new Dictionary();
46 SetExtensions(extensions);
49 extensions->Set(key, value);
52 Value ConfigObject::GetExtension(const String& key)
54 Dictionary::Ptr extensions = GetExtensions();
59 return extensions->Get(key);
62 void ConfigObject::ClearExtension(const String& key)
64 Dictionary::Ptr extensions = GetExtensions();
69 extensions->Remove(key);
72 class ModAttrValidationUtils final : public ValidationUtils
75 bool ValidateName(const String& type, const String& name) const override
77 Type::Ptr ptype = Type::GetByName(type);
78 auto *dtype = dynamic_cast<ConfigType *>(ptype.get());
83 if (!dtype->GetObject(name))
90 void ConfigObject::ModifyAttribute(const String& attr, const Value& value, bool updateVersion)
92 Dictionary::Ptr original_attributes = GetOriginalAttributes();
93 bool updated_original_attributes = false;
95 Type::Ptr type = GetReflectionType();
97 std::vector<String> tokens = attr.Split(".");
99 String fieldName = tokens[0];
101 int fid = type->GetFieldId(fieldName);
102 Field field = type->GetFieldInfo(fid);
104 if (field.Attributes & FANoUserModify)
105 BOOST_THROW_EXCEPTION(std::invalid_argument("Attribute cannot be modified."));
107 if (field.Attributes & FAConfig) {
108 if (!original_attributes) {
109 original_attributes = new Dictionary();
110 SetOriginalAttributes(original_attributes, true);
114 Value oldValue = GetField(fid);
117 if (tokens.size() > 1) {
118 newValue = oldValue.Clone();
119 Value current = newValue;
121 if (current.IsEmpty()) {
122 current = new Dictionary();
126 String prefix = tokens[0];
128 for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) {
129 if (!current.IsObjectType<Dictionary>())
130 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
132 Dictionary::Ptr dict = current;
134 const String& key = tokens[i];
137 if (!dict->Get(key, ¤t)) {
138 current = new Dictionary();
139 dict->Set(key, current);
143 if (!current.IsObjectType<Dictionary>())
144 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
146 Dictionary::Ptr dict = current;
148 const String& key = tokens[tokens.size() - 1];
151 /* clone it for original attributes */
152 oldValue = dict->Get(key).Clone();
154 if (field.Attributes & FAConfig) {
155 updated_original_attributes = true;
157 if (oldValue.IsObjectType<Dictionary>()) {
158 Dictionary::Ptr oldDict = oldValue;
159 ObjectLock olock(oldDict);
160 for (const auto& kv : oldDict) {
161 String key = prefix + "." + kv.first;
162 if (!original_attributes->Contains(key))
163 original_attributes->Set(key, kv.second);
166 /* store the new value as null */
167 if (value.IsObjectType<Dictionary>()) {
168 Dictionary::Ptr valueDict = value;
169 ObjectLock olock(valueDict);
170 for (const auto& kv : valueDict) {
171 String key = attr + "." + kv.first;
172 if (!original_attributes->Contains(key))
173 original_attributes->Set(key, Empty);
176 } else if (!original_attributes->Contains(attr))
177 original_attributes->Set(attr, oldValue);
180 dict->Set(key, value);
184 if (field.Attributes & FAConfig) {
185 if (!original_attributes->Contains(attr)) {
186 updated_original_attributes = true;
187 original_attributes->Set(attr, oldValue);
192 ModAttrValidationUtils utils;
193 ValidateField(fid, Lazy<Value>{newValue}, utils);
195 SetField(fid, newValue);
197 if (updateVersion && (field.Attributes & FAConfig))
198 SetVersion(Utility::GetTime());
200 if (updated_original_attributes)
201 NotifyOriginalAttributes();
204 void ConfigObject::RestoreAttribute(const String& attr, bool updateVersion)
206 Type::Ptr type = GetReflectionType();
208 std::vector<String> tokens = attr.Split(".");
210 String fieldName = tokens[0];
212 int fid = type->GetFieldId(fieldName);
214 Value currentValue = GetField(fid);
216 Dictionary::Ptr original_attributes = GetOriginalAttributes();
218 if (!original_attributes)
221 Value oldValue = original_attributes->Get(attr);
224 if (tokens.size() > 1) {
225 newValue = currentValue.Clone();
226 Value current = newValue;
228 if (current.IsEmpty())
229 BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute"));
231 String prefix = tokens[0];
233 for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) {
234 if (!current.IsObjectType<Dictionary>())
235 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
237 Dictionary::Ptr dict = current;
239 const String& key = tokens[i];
242 if (!dict->Contains(key))
243 BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute"));
245 current = dict->Get(key);
248 if (!current.IsObjectType<Dictionary>())
249 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
251 Dictionary::Ptr dict = current;
253 const String& key = tokens[tokens.size() - 1];
256 std::vector<String> restoredAttrs;
259 ObjectLock olock(original_attributes);
260 for (const auto& kv : original_attributes) {
261 std::vector<String> originalTokens = String(kv.first).Split(".");
263 if (tokens.size() > originalTokens.size())
267 for (std::vector<String>::size_type i = 0; i < tokens.size(); i++) {
268 if (tokens[i] != originalTokens[i]) {
277 Dictionary::Ptr dict;
279 if (tokens.size() == originalTokens.size())
282 Value currentSub = current;
284 for (std::vector<String>::size_type i = tokens.size() - 1; i < originalTokens.size() - 1; i++) {
286 currentSub = dict->Get(originalTokens[i]);
288 if (!currentSub.IsObjectType<Dictionary>()) {
289 currentSub = new Dictionary();
290 dict->Set(originalTokens[i], currentSub);
297 dict->Set(originalTokens[originalTokens.size() - 1], kv.second);
298 restoredAttrs.push_back(kv.first);
302 for (const String& attr : restoredAttrs)
303 original_attributes->Remove(attr);
310 original_attributes->Remove(attr);
311 SetField(fid, newValue);
314 SetVersion(Utility::GetTime());
317 bool ConfigObject::IsAttributeModified(const String& attr) const
319 Dictionary::Ptr original_attributes = GetOriginalAttributes();
321 if (!original_attributes)
324 return original_attributes->Contains(attr);
327 void ConfigObject::Register()
331 TypeImpl<ConfigObject>::Ptr type = static_pointer_cast<TypeImpl<ConfigObject> >(GetReflectionType());
332 type->RegisterObject(this);
335 void ConfigObject::Unregister()
339 TypeImpl<ConfigObject>::Ptr type = static_pointer_cast<TypeImpl<ConfigObject> >(GetReflectionType());
340 type->UnregisterObject(this);
343 void ConfigObject::Start(bool runtimeCreated)
345 ObjectImpl<ConfigObject>::Start(runtimeCreated);
347 ObjectLock olock(this);
349 SetStartCalled(true);
352 void ConfigObject::PreActivate()
354 CONTEXT("Setting 'active' to true for object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
357 SetActive(true, true);
360 void ConfigObject::Activate(bool runtimeCreated)
362 CONTEXT("Activating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
365 ObjectLock olock(this);
367 Start(runtimeCreated);
369 ASSERT(GetStartCalled());
371 if (GetHAMode() == HARunEverywhere)
378 void ConfigObject::Stop(bool runtimeRemoved)
380 ObjectImpl<ConfigObject>::Stop(runtimeRemoved);
382 ObjectLock olock(this);
387 void ConfigObject::Deactivate(bool runtimeRemoved)
389 CONTEXT("Deactivating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
392 ObjectLock olock(this);
397 SetActive(false, true);
401 Stop(runtimeRemoved);
404 ASSERT(GetStopCalled());
409 void ConfigObject::OnConfigLoaded()
411 /* Nothing to do here. */
414 void ConfigObject::OnAllConfigLoaded()
416 static ConfigType *ctype;
419 Type::Ptr type = Type::GetByName("Zone");
420 ctype = dynamic_cast<ConfigType *>(type.get());
423 String zoneName = GetZoneName();
425 if (!zoneName.IsEmpty())
426 m_Zone = ctype->GetObject(zoneName);
429 void ConfigObject::CreateChildObjects(const Type::Ptr& childType)
431 /* Nothing to do here. */
434 void ConfigObject::OnStateLoaded()
436 /* Nothing to do here. */
439 void ConfigObject::Pause()
441 SetPauseCalled(true);
444 void ConfigObject::Resume()
446 SetResumeCalled(true);
449 void ConfigObject::SetAuthority(bool authority)
451 ObjectLock olock(this);
453 if (authority && GetPaused()) {
454 SetResumeCalled(false);
456 ASSERT(GetResumeCalled());
458 } else if (!authority && !GetPaused()) {
460 SetPauseCalled(false);
462 ASSERT(GetPauseCalled());
466 void ConfigObject::DumpObjects(const String& filename, int attributeTypes)
468 Log(LogInformation, "ConfigObject")
469 << "Dumping program state to file '" << filename << "'";
472 String tempFilename = Utility::CreateTempFile(filename + ".XXXXXX", 0600, fp);
473 fp.exceptions(std::ofstream::failbit | std::ofstream::badbit);
476 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file"));
478 StdioStream::Ptr sfp = new StdioStream(&fp, false);
480 for (const Type::Ptr& type : Type::GetAllTypes()) {
481 auto *dtype = dynamic_cast<ConfigType *>(type.get());
486 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
487 Dictionary::Ptr update = Serialize(object, attributeTypes);
492 Dictionary::Ptr persistentObject = new Dictionary({
493 { "type", type->GetName() },
494 { "name", object->GetName() },
498 String json = JsonEncode(persistentObject);
500 NetString::WriteStringToStream(sfp, json);
508 Utility::RenameFile(tempFilename, filename);
511 void ConfigObject::RestoreObject(const String& message, int attributeTypes)
513 Dictionary::Ptr persistentObject = JsonDecode(message);
515 String type = persistentObject->Get("type");
516 String name = persistentObject->Get("name");
518 ConfigObject::Ptr object = GetObject(type, name);
524 Log(LogDebug, "ConfigObject")
525 << "Restoring object '" << name << "' of type '" << type << "'.";
526 #endif /* I2_DEBUG */
527 Dictionary::Ptr update = persistentObject->Get("update");
528 Deserialize(object, update, false, attributeTypes);
529 object->OnStateLoaded();
530 object->SetStateLoaded(true);
533 void ConfigObject::RestoreObjects(const String& filename, int attributeTypes)
535 if (!Utility::PathExists(filename))
538 Log(LogInformation, "ConfigObject")
539 << "Restoring program state from file '" << filename << "'";
542 fp.open(filename.CStr(), std::ios_base::in);
544 StdioStream::Ptr sfp = new StdioStream (&fp, false);
546 unsigned long restored = 0;
548 WorkQueue upq(25000, Configuration::Concurrency);
549 upq.SetName("ConfigObject::RestoreObjects");
552 StreamReadContext src;
554 StreamReadStatus srs = NetString::ReadStringFromStream(sfp, &message, src);
556 if (srs == StatusEof)
559 if (srs != StatusNewItem)
562 upq.Enqueue(std::bind(&ConfigObject::RestoreObject, message, attributeTypes));
570 unsigned long no_state = 0;
572 for (const Type::Ptr& type : Type::GetAllTypes()) {
573 auto *dtype = dynamic_cast<ConfigType *>(type.get());
578 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
579 if (!object->GetStateLoaded()) {
580 object->OnStateLoaded();
581 object->SetStateLoaded(true);
588 Log(LogInformation, "ConfigObject")
589 << "Restored " << restored << " objects. Loaded " << no_state << " new objects without state.";
592 void ConfigObject::StopObjects()
594 std::vector<Type::Ptr> types = Type::GetAllTypes();
596 std::sort(types.begin(), types.end(), [](const Type::Ptr& a, const Type::Ptr& b) {
597 if (a->GetActivationPriority() > b->GetActivationPriority())
602 for (const Type::Ptr& type : types) {
603 auto *dtype = dynamic_cast<ConfigType *>(type.get());
608 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
610 Log(LogDebug, "ConfigObject")
611 << "Deactivate() called for config object '" << object->GetName() << "' with type '" << type->GetName() << "'.";
612 #endif /* I2_DEBUG */
613 object->Deactivate();
618 void ConfigObject::DumpModifiedAttributes(const std::function<void(const ConfigObject::Ptr&, const String&, const Value&)>& callback)
620 for (const Type::Ptr& type : Type::GetAllTypes()) {
621 auto *dtype = dynamic_cast<ConfigType *>(type.get());
626 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
627 Dictionary::Ptr originalAttributes = object->GetOriginalAttributes();
629 if (!originalAttributes)
632 ObjectLock olock(originalAttributes);
633 for (const Dictionary::Pair& kv : originalAttributes) {
634 String key = kv.first;
636 Type::Ptr type = object->GetReflectionType();
638 std::vector<String> tokens = key.Split(".");
640 String fieldName = tokens[0];
641 int fid = type->GetFieldId(fieldName);
643 Value currentValue = object->GetField(fid);
646 if (tokens.size() > 1) {
647 Value current = currentValue;
649 for (std::vector<String>::size_type i = 1; i < tokens.size() - 1; i++) {
650 if (!current.IsObjectType<Dictionary>())
651 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
653 Dictionary::Ptr dict = current;
654 const String& key = tokens[i];
656 if (!dict->Contains(key))
659 current = dict->Get(key);
662 if (!current.IsObjectType<Dictionary>())
663 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
665 Dictionary::Ptr dict = current;
666 const String& key = tokens[tokens.size() - 1];
668 modifiedValue = dict->Get(key);
670 modifiedValue = currentValue;
672 callback(object, key, modifiedValue);
679 ConfigObject::Ptr ConfigObject::GetObject(const String& type, const String& name)
681 Type::Ptr ptype = Type::GetByName(type);
682 auto *ctype = dynamic_cast<ConfigType *>(ptype.get());
687 return ctype->GetObject(name);
690 ConfigObject::Ptr ConfigObject::GetZone() const
695 Dictionary::Ptr ConfigObject::GetSourceLocation() const
697 DebugInfo di = GetDebugInfo();
699 return new Dictionary({
701 { "first_line", di.FirstLine },
702 { "first_column", di.FirstColumn },
703 { "last_line", di.LastLine },
704 { "last_column", di.LastColumn }
708 NameComposer::~NameComposer()