]> granicus.if.org Git - icinga2/blob - lib/base/configobject.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / configobject.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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"
19 #include <fstream>
20 #include <boost/exception/errinfo_api_function.hpp>
21 #include <boost/exception/errinfo_errno.hpp>
22 #include <boost/exception/errinfo_file_name.hpp>
23
24 using namespace icinga;
25
26 REGISTER_TYPE_WITH_PROTOTYPE(ConfigObject, ConfigObject::GetPrototype());
27
28 boost::signals2::signal<void (const ConfigObject::Ptr&)> ConfigObject::OnStateChanged;
29
30 bool ConfigObject::IsActive() const
31 {
32         return GetActive();
33 }
34
35 bool ConfigObject::IsPaused() const
36 {
37         return GetPaused();
38 }
39
40 void ConfigObject::SetExtension(const String& key, const Value& value)
41 {
42         Dictionary::Ptr extensions = GetExtensions();
43
44         if (!extensions) {
45                 extensions = new Dictionary();
46                 SetExtensions(extensions);
47         }
48
49         extensions->Set(key, value);
50 }
51
52 Value ConfigObject::GetExtension(const String& key)
53 {
54         Dictionary::Ptr extensions = GetExtensions();
55
56         if (!extensions)
57                 return Empty;
58
59         return extensions->Get(key);
60 }
61
62 void ConfigObject::ClearExtension(const String& key)
63 {
64         Dictionary::Ptr extensions = GetExtensions();
65
66         if (!extensions)
67                 return;
68
69         extensions->Remove(key);
70 }
71
72 class ModAttrValidationUtils final : public ValidationUtils
73 {
74 public:
75         bool ValidateName(const String& type, const String& name) const override
76         {
77                 Type::Ptr ptype = Type::GetByName(type);
78                 auto *dtype = dynamic_cast<ConfigType *>(ptype.get());
79
80                 if (!dtype)
81                         return false;
82
83                 if (!dtype->GetObject(name))
84                         return false;
85
86                 return true;
87         }
88 };
89
90 void ConfigObject::ModifyAttribute(const String& attr, const Value& value, bool updateVersion)
91 {
92         Dictionary::Ptr original_attributes = GetOriginalAttributes();
93         bool updated_original_attributes = false;
94
95         Type::Ptr type = GetReflectionType();
96
97         std::vector<String> tokens = attr.Split(".");
98
99         String fieldName = tokens[0];
100
101         int fid = type->GetFieldId(fieldName);
102         Field field = type->GetFieldInfo(fid);
103
104         if (field.Attributes & FANoUserModify)
105                 BOOST_THROW_EXCEPTION(std::invalid_argument("Attribute cannot be modified."));
106
107         if (field.Attributes & FAConfig) {
108                 if (!original_attributes) {
109                         original_attributes = new Dictionary();
110                         SetOriginalAttributes(original_attributes, true);
111                 }
112         }
113
114         Value oldValue = GetField(fid);
115         Value newValue;
116
117         if (tokens.size() > 1) {
118                 newValue = oldValue.Clone();
119                 Value current = newValue;
120
121                 if (current.IsEmpty()) {
122                         current = new Dictionary();
123                         newValue = current;
124                 }
125
126                 String prefix = tokens[0];
127
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."));
131
132                         Dictionary::Ptr dict = current;
133
134                         const String& key = tokens[i];
135                         prefix += "." + key;
136
137                         if (!dict->Get(key, &current)) {
138                                 current = new Dictionary();
139                                 dict->Set(key, current);
140                         }
141                 }
142
143                 if (!current.IsObjectType<Dictionary>())
144                         BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
145
146                 Dictionary::Ptr dict = current;
147
148                 const String& key = tokens[tokens.size() - 1];
149                 prefix += "." + key;
150
151                 /* clone it for original attributes */
152                 oldValue = dict->Get(key).Clone();
153
154                 if (field.Attributes & FAConfig) {
155                         updated_original_attributes = true;
156
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);
164                                 }
165
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);
174                                         }
175                                 }
176                         } else if (!original_attributes->Contains(attr))
177                                 original_attributes->Set(attr, oldValue);
178                 }
179
180                 dict->Set(key, value);
181         } else {
182                 newValue = value;
183
184                 if (field.Attributes & FAConfig) {
185                         if (!original_attributes->Contains(attr)) {
186                                 updated_original_attributes = true;
187                                 original_attributes->Set(attr, oldValue);
188                         }
189                 }
190         }
191
192         ModAttrValidationUtils utils;
193         ValidateField(fid, Lazy<Value>{newValue}, utils);
194
195         SetField(fid, newValue);
196
197         if (updateVersion && (field.Attributes & FAConfig))
198                 SetVersion(Utility::GetTime());
199
200         if (updated_original_attributes)
201                 NotifyOriginalAttributes();
202 }
203
204 void ConfigObject::RestoreAttribute(const String& attr, bool updateVersion)
205 {
206         Type::Ptr type = GetReflectionType();
207
208         std::vector<String> tokens = attr.Split(".");
209
210         String fieldName = tokens[0];
211
212         int fid = type->GetFieldId(fieldName);
213
214         Value currentValue = GetField(fid);
215
216         Dictionary::Ptr original_attributes = GetOriginalAttributes();
217
218         if (!original_attributes)
219                 return;
220
221         Value oldValue = original_attributes->Get(attr);
222         Value newValue;
223
224         if (tokens.size() > 1) {
225                 newValue = currentValue.Clone();
226                 Value current = newValue;
227
228                 if (current.IsEmpty())
229                         BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute"));
230
231                 String prefix = tokens[0];
232
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."));
236
237                         Dictionary::Ptr dict = current;
238
239                         const String& key = tokens[i];
240                         prefix += "." + key;
241
242                         if (!dict->Contains(key))
243                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot restore non-existent object attribute"));
244
245                         current = dict->Get(key);
246                 }
247
248                 if (!current.IsObjectType<Dictionary>())
249                         BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
250
251                 Dictionary::Ptr dict = current;
252
253                 const String& key = tokens[tokens.size() - 1];
254                 prefix += "." + key;
255
256                 std::vector<String> restoredAttrs;
257
258                 {
259                         ObjectLock olock(original_attributes);
260                         for (const auto& kv : original_attributes) {
261                                 std::vector<String> originalTokens = String(kv.first).Split(".");
262
263                                 if (tokens.size() > originalTokens.size())
264                                         continue;
265
266                                 bool match = true;
267                                 for (std::vector<String>::size_type i = 0; i < tokens.size(); i++) {
268                                         if (tokens[i] != originalTokens[i]) {
269                                                 match = false;
270                                                 break;
271                                         }
272                                 }
273
274                                 if (!match)
275                                         continue;
276
277                                 Dictionary::Ptr dict;
278
279                                 if (tokens.size() == originalTokens.size())
280                                         dict = current;
281                                 else {
282                                         Value currentSub = current;
283
284                                         for (std::vector<String>::size_type i = tokens.size() - 1; i < originalTokens.size() - 1; i++) {
285                                                 dict = currentSub;
286                                                 currentSub = dict->Get(originalTokens[i]);
287
288                                                 if (!currentSub.IsObjectType<Dictionary>()) {
289                                                         currentSub = new Dictionary();
290                                                         dict->Set(originalTokens[i], currentSub);
291                                                 }
292                                         }
293
294                                         dict = currentSub;
295                                 }
296
297                                 dict->Set(originalTokens[originalTokens.size() - 1], kv.second);
298                                 restoredAttrs.push_back(kv.first);
299                         }
300                 }
301
302                 for (const String& attr : restoredAttrs)
303                         original_attributes->Remove(attr);
304
305
306         } else {
307                 newValue = oldValue;
308         }
309
310         original_attributes->Remove(attr);
311         SetField(fid, newValue);
312
313         if (updateVersion)
314                 SetVersion(Utility::GetTime());
315 }
316
317 bool ConfigObject::IsAttributeModified(const String& attr) const
318 {
319         Dictionary::Ptr original_attributes = GetOriginalAttributes();
320
321         if (!original_attributes)
322                 return false;
323
324         return original_attributes->Contains(attr);
325 }
326
327 void ConfigObject::Register()
328 {
329         ASSERT(!OwnsLock());
330
331         TypeImpl<ConfigObject>::Ptr type = static_pointer_cast<TypeImpl<ConfigObject> >(GetReflectionType());
332         type->RegisterObject(this);
333 }
334
335 void ConfigObject::Unregister()
336 {
337         ASSERT(!OwnsLock());
338
339         TypeImpl<ConfigObject>::Ptr type = static_pointer_cast<TypeImpl<ConfigObject> >(GetReflectionType());
340         type->UnregisterObject(this);
341 }
342
343 void ConfigObject::Start(bool runtimeCreated)
344 {
345         ObjectImpl<ConfigObject>::Start(runtimeCreated);
346
347         ObjectLock olock(this);
348
349         SetStartCalled(true);
350 }
351
352 void ConfigObject::PreActivate()
353 {
354         CONTEXT("Setting 'active' to true for object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
355
356         ASSERT(!IsActive());
357         SetActive(true, true);
358 }
359
360 void ConfigObject::Activate(bool runtimeCreated)
361 {
362         CONTEXT("Activating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
363
364         {
365                 ObjectLock olock(this);
366
367                 Start(runtimeCreated);
368
369                 ASSERT(GetStartCalled());
370
371                 if (GetHAMode() == HARunEverywhere)
372                         SetAuthority(true);
373         }
374
375         NotifyActive();
376 }
377
378 void ConfigObject::Stop(bool runtimeRemoved)
379 {
380         ObjectImpl<ConfigObject>::Stop(runtimeRemoved);
381
382         ObjectLock olock(this);
383
384         SetStopCalled(true);
385 }
386
387 void ConfigObject::Deactivate(bool runtimeRemoved)
388 {
389         CONTEXT("Deactivating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'");
390
391         {
392                 ObjectLock olock(this);
393
394                 if (!IsActive())
395                         return;
396
397                 SetActive(false, true);
398
399                 SetAuthority(false);
400
401                 Stop(runtimeRemoved);
402         }
403
404         ASSERT(GetStopCalled());
405
406         NotifyActive();
407 }
408
409 void ConfigObject::OnConfigLoaded()
410 {
411         /* Nothing to do here. */
412 }
413
414 void ConfigObject::OnAllConfigLoaded()
415 {
416         static ConfigType *ctype;
417
418         if (!ctype) {
419                 Type::Ptr type = Type::GetByName("Zone");
420                 ctype = dynamic_cast<ConfigType *>(type.get());
421         }
422
423         String zoneName = GetZoneName();
424
425         if (!zoneName.IsEmpty())
426                 m_Zone = ctype->GetObject(zoneName);
427 }
428
429 void ConfigObject::CreateChildObjects(const Type::Ptr& childType)
430 {
431         /* Nothing to do here. */
432 }
433
434 void ConfigObject::OnStateLoaded()
435 {
436         /* Nothing to do here. */
437 }
438
439 void ConfigObject::Pause()
440 {
441         SetPauseCalled(true);
442 }
443
444 void ConfigObject::Resume()
445 {
446         SetResumeCalled(true);
447 }
448
449 void ConfigObject::SetAuthority(bool authority)
450 {
451         ObjectLock olock(this);
452
453         if (authority && GetPaused()) {
454                 SetResumeCalled(false);
455                 Resume();
456                 ASSERT(GetResumeCalled());
457                 SetPaused(false);
458         } else if (!authority && !GetPaused()) {
459                 SetPaused(true);
460                 SetPauseCalled(false);
461                 Pause();
462                 ASSERT(GetPauseCalled());
463         }
464 }
465
466 void ConfigObject::DumpObjects(const String& filename, int attributeTypes)
467 {
468         Log(LogInformation, "ConfigObject")
469                 << "Dumping program state to file '" << filename << "'";
470
471         std::fstream fp;
472         String tempFilename = Utility::CreateTempFile(filename + ".XXXXXX", 0600, fp);
473         fp.exceptions(std::ofstream::failbit | std::ofstream::badbit);
474
475         if (!fp)
476                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file"));
477
478         StdioStream::Ptr sfp = new StdioStream(&fp, false);
479
480         for (const Type::Ptr& type : Type::GetAllTypes()) {
481                 auto *dtype = dynamic_cast<ConfigType *>(type.get());
482
483                 if (!dtype)
484                         continue;
485
486                 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
487                         Dictionary::Ptr update = Serialize(object, attributeTypes);
488
489                         if (!update)
490                                 continue;
491
492                         Dictionary::Ptr persistentObject = new Dictionary({
493                                 { "type", type->GetName() },
494                                 { "name", object->GetName() },
495                                 { "update", update }
496                         });
497
498                         String json = JsonEncode(persistentObject);
499
500                         NetString::WriteStringToStream(sfp, json);
501                 }
502         }
503
504         sfp->Close();
505
506         fp.close();
507
508         Utility::RenameFile(tempFilename, filename);
509 }
510
511 void ConfigObject::RestoreObject(const String& message, int attributeTypes)
512 {
513         Dictionary::Ptr persistentObject = JsonDecode(message);
514
515         String type = persistentObject->Get("type");
516         String name = persistentObject->Get("name");
517
518         ConfigObject::Ptr object = GetObject(type, name);
519
520         if (!object)
521                 return;
522
523 #ifdef I2_DEBUG
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);
531 }
532
533 void ConfigObject::RestoreObjects(const String& filename, int attributeTypes)
534 {
535         if (!Utility::PathExists(filename))
536                 return;
537
538         Log(LogInformation, "ConfigObject")
539                 << "Restoring program state from file '" << filename << "'";
540
541         std::fstream fp;
542         fp.open(filename.CStr(), std::ios_base::in);
543
544         StdioStream::Ptr sfp = new StdioStream (&fp, false);
545
546         unsigned long restored = 0;
547
548         WorkQueue upq(25000, Configuration::Concurrency);
549         upq.SetName("ConfigObject::RestoreObjects");
550
551         String message;
552         StreamReadContext src;
553         for (;;) {
554                 StreamReadStatus srs = NetString::ReadStringFromStream(sfp, &message, src);
555
556                 if (srs == StatusEof)
557                         break;
558
559                 if (srs != StatusNewItem)
560                         continue;
561
562                 upq.Enqueue(std::bind(&ConfigObject::RestoreObject, message, attributeTypes));
563                 restored++;
564         }
565
566         sfp->Close();
567
568         upq.Join();
569
570         unsigned long no_state = 0;
571
572         for (const Type::Ptr& type : Type::GetAllTypes()) {
573                 auto *dtype = dynamic_cast<ConfigType *>(type.get());
574
575                 if (!dtype)
576                         continue;
577
578                 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
579                         if (!object->GetStateLoaded()) {
580                                 object->OnStateLoaded();
581                                 object->SetStateLoaded(true);
582
583                                 no_state++;
584                         }
585                 }
586         }
587
588         Log(LogInformation, "ConfigObject")
589                 << "Restored " << restored << " objects. Loaded " << no_state << " new objects without state.";
590 }
591
592 void ConfigObject::StopObjects()
593 {
594         std::vector<Type::Ptr> types = Type::GetAllTypes();
595
596         std::sort(types.begin(), types.end(), [](const Type::Ptr& a, const Type::Ptr& b) {
597                 if (a->GetActivationPriority() > b->GetActivationPriority())
598                         return true;
599                 return false;
600         });
601
602         for (const Type::Ptr& type : types) {
603                 auto *dtype = dynamic_cast<ConfigType *>(type.get());
604
605                 if (!dtype)
606                         continue;
607
608                 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
609 #ifdef I2_DEBUG
610                         Log(LogDebug, "ConfigObject")
611                                 << "Deactivate() called for config object '" << object->GetName() << "' with type '" << type->GetName() << "'.";
612 #endif /* I2_DEBUG */
613                         object->Deactivate();
614                 }
615         }
616 }
617
618 void ConfigObject::DumpModifiedAttributes(const std::function<void(const ConfigObject::Ptr&, const String&, const Value&)>& callback)
619 {
620         for (const Type::Ptr& type : Type::GetAllTypes()) {
621                 auto *dtype = dynamic_cast<ConfigType *>(type.get());
622
623                 if (!dtype)
624                         continue;
625
626                 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
627                         Dictionary::Ptr originalAttributes = object->GetOriginalAttributes();
628
629                         if (!originalAttributes)
630                                 continue;
631
632                         ObjectLock olock(originalAttributes);
633                         for (const Dictionary::Pair& kv : originalAttributes) {
634                                 String key = kv.first;
635
636                                 Type::Ptr type = object->GetReflectionType();
637
638                                 std::vector<String> tokens = key.Split(".");
639
640                                 String fieldName = tokens[0];
641                                 int fid = type->GetFieldId(fieldName);
642
643                                 Value currentValue = object->GetField(fid);
644                                 Value modifiedValue;
645
646                                 if (tokens.size() > 1) {
647                                         Value current = currentValue;
648
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."));
652
653                                                 Dictionary::Ptr dict = current;
654                                                 const String& key = tokens[i];
655
656                                                 if (!dict->Contains(key))
657                                                         break;
658
659                                                 current = dict->Get(key);
660                                         }
661
662                                         if (!current.IsObjectType<Dictionary>())
663                                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Value must be a dictionary."));
664
665                                         Dictionary::Ptr dict = current;
666                                         const String& key = tokens[tokens.size() - 1];
667
668                                         modifiedValue = dict->Get(key);
669                                 } else
670                                         modifiedValue = currentValue;
671
672                                 callback(object, key, modifiedValue);
673                         }
674                 }
675         }
676
677 }
678
679 ConfigObject::Ptr ConfigObject::GetObject(const String& type, const String& name)
680 {
681         Type::Ptr ptype = Type::GetByName(type);
682         auto *ctype = dynamic_cast<ConfigType *>(ptype.get());
683
684         if (!ctype)
685                 return nullptr;
686
687         return ctype->GetObject(name);
688 }
689
690 ConfigObject::Ptr ConfigObject::GetZone() const
691 {
692         return m_Zone;
693 }
694
695 Dictionary::Ptr ConfigObject::GetSourceLocation() const
696 {
697         DebugInfo di = GetDebugInfo();
698
699         return new Dictionary({
700                 { "path", di.Path },
701                 { "first_line", di.FirstLine },
702                 { "first_column", di.FirstColumn },
703                 { "last_line", di.LastLine },
704                 { "last_column", di.LastColumn }
705         });
706 }
707
708 NameComposer::~NameComposer()
709 { }