]> granicus.if.org Git - icinga2/blob - lib/base/dynamicobject.cpp
Don't attempt to restore program state from non-existing state file
[icinga2] / lib / base / dynamicobject.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/dynamicobject.hpp"
21 #include "base/dynamictype.hpp"
22 #include "base/serializer.hpp"
23 #include "base/netstring.hpp"
24 #include "base/json.hpp"
25 #include "base/stdiostream.hpp"
26 #include "base/debug.hpp"
27 #include "base/objectlock.hpp"
28 #include "base/logger.hpp"
29 #include "base/exception.hpp"
30 #include "base/function.hpp"
31 #include "base/initialize.hpp"
32 #include "base/workqueue.hpp"
33 #include "base/context.hpp"
34 #include "base/application.hpp"
35 #include <fstream>
36 #include <boost/foreach.hpp>
37 #include <boost/exception/errinfo_api_function.hpp>
38 #include <boost/exception/errinfo_errno.hpp>
39 #include <boost/exception/errinfo_file_name.hpp>
40
41 using namespace icinga;
42
43 REGISTER_TYPE(DynamicObject);
44
45 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStarted;
46 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStopped;
47 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnPaused;
48 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnResumed;
49 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStateChanged;
50
51 DynamicObject::DynamicObject(void)
52 { }
53
54 DynamicType::Ptr DynamicObject::GetType(void) const
55 {
56         return DynamicType::GetByName(GetTypeNameV());
57 }
58
59 DebugInfo DynamicObject::GetDebugInfo(void) const
60 {
61         return m_DebugInfo;
62 }
63
64 void DynamicObject::SetDebugInfo(const DebugInfo& di)
65 {
66         m_DebugInfo = di;
67 }
68
69 bool DynamicObject::IsActive(void) const
70 {
71         return GetActive();
72 }
73
74 bool DynamicObject::IsPaused(void) const
75 {
76         return GetPaused();
77 }
78
79 void DynamicObject::SetExtension(const String& key, const Value& value)
80 {
81         Dictionary::Ptr extensions = GetExtensions();
82
83         if (!extensions) {
84                 extensions = new Dictionary();
85                 SetExtensions(extensions);
86         }
87
88         extensions->Set(key, value);
89 }
90
91 Value DynamicObject::GetExtension(const String& key)
92 {
93         Dictionary::Ptr extensions = GetExtensions();
94
95         if (!extensions)
96                 return Empty;
97
98         return extensions->Get(key);
99 }
100
101 void DynamicObject::ClearExtension(const String& key)
102 {
103         Dictionary::Ptr extensions = GetExtensions();
104
105         if (!extensions)
106                 return;
107
108         extensions->Remove(key);
109 }
110
111 void DynamicObject::Register(void)
112 {
113         ASSERT(!OwnsLock());
114
115         DynamicType::Ptr dtype = GetType();
116         dtype->RegisterObject(this);
117 }
118
119 void DynamicObject::Start(void)
120 {
121         ASSERT(!OwnsLock());
122         ObjectLock olock(this);
123
124         SetStartCalled(true);
125 }
126
127 void DynamicObject::Activate(void)
128 {
129         CONTEXT("Activating object '" + GetName() + "' of type '" + GetType()->GetName() + "'");
130
131         ASSERT(!OwnsLock());
132
133         Start();
134
135         ASSERT(GetStartCalled());
136
137         {
138                 ObjectLock olock(this);
139                 ASSERT(!IsActive());
140                 SetActive(true);
141         }
142
143         OnStarted(this);
144
145         SetAuthority(true);
146 }
147
148 void DynamicObject::Stop(void)
149 {
150         ASSERT(!OwnsLock());
151         ObjectLock olock(this);
152
153         SetStopCalled(true);
154 }
155
156 void DynamicObject::Deactivate(void)
157 {
158         CONTEXT("Deactivating object '" + GetName() + "' of type '" + GetType()->GetName() + "'");
159
160         ASSERT(!OwnsLock());
161
162         SetAuthority(false);
163
164         {
165                 ObjectLock olock(this);
166
167                 if (!IsActive())
168                         return;
169
170                 SetActive(false);
171         }
172
173         Stop();
174
175         ASSERT(GetStopCalled());
176
177         OnStopped(this);
178 }
179
180 void DynamicObject::OnConfigLoaded(void)
181 {
182         /* Nothing to do here. */
183 }
184
185 void DynamicObject::OnAllConfigLoaded(void)
186 {
187         /* Nothing to do here. */
188 }
189
190 void DynamicObject::OnStateLoaded(void)
191 {
192         /* Nothing to do here. */
193 }
194
195 void DynamicObject::Pause(void)
196 {
197         SetPauseCalled(true);
198 }
199
200 void DynamicObject::Resume(void)
201 {
202         SetResumeCalled(true);
203 }
204
205 void DynamicObject::SetAuthority(bool authority)
206 {
207         if (authority && GetPaused()) {
208                 SetResumeCalled(false);
209                 Resume();
210                 ASSERT(GetResumeCalled());
211                 SetPaused(false);
212                 OnResumed(this);
213         } else if (!authority && !GetPaused()) {
214                 SetPauseCalled(false);
215                 Pause();
216                 ASSERT(GetPauseCalled());
217                 SetPaused(true);
218                 OnPaused(this);
219         }
220 }
221
222 void DynamicObject::DumpObjects(const String& filename, int attributeTypes)
223 {
224         Log(LogInformation, "DynamicObject")
225             << "Dumping program state to file '" << filename << "'";
226
227         String tempFilename = filename + ".tmp";
228
229         std::fstream fp;
230         fp.open(tempFilename.CStr(), std::ios_base::out);
231
232         if (!fp)
233                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file"));
234
235         StdioStream::Ptr sfp = new StdioStream(&fp, false);
236
237         BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
238                 BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
239                         Dictionary::Ptr persistentObject = new Dictionary();
240
241                         persistentObject->Set("type", type->GetName());
242                         persistentObject->Set("name", object->GetName());
243
244                         Dictionary::Ptr update = Serialize(object, attributeTypes);
245
246                         if (!update)
247                                 continue;
248
249                         persistentObject->Set("update", update);
250
251                         String json = JsonEncode(persistentObject);
252
253                         NetString::WriteStringToStream(sfp, json);
254                 }
255         }
256
257         sfp->Close();
258
259         fp.close();
260
261 #ifdef _WIN32
262         _unlink(filename.CStr());
263 #endif /* _WIN32 */
264
265         if (rename(tempFilename.CStr(), filename.CStr()) < 0) {
266                 BOOST_THROW_EXCEPTION(posix_error()
267                     << boost::errinfo_api_function("rename")
268                     << boost::errinfo_errno(errno)
269                     << boost::errinfo_file_name(tempFilename));
270         }
271 }
272
273 void DynamicObject::RestoreObject(const String& message, int attributeTypes)
274 {
275         Dictionary::Ptr persistentObject = JsonDecode(message);
276
277         String type = persistentObject->Get("type");
278
279         DynamicType::Ptr dt = DynamicType::GetByName(type);
280
281         if (!dt)
282                 return;
283
284         String name = persistentObject->Get("name");
285
286         DynamicObject::Ptr object = dt->GetObject(name);
287
288         if (!object)
289                 return;
290
291         ASSERT(!object->IsActive());
292 #ifdef I2_DEBUG
293         Log(LogDebug, "DynamicObject")
294             << "Restoring object '" << name << "' of type '" << type << "'.";
295 #endif /* I2_DEBUG */
296         Dictionary::Ptr update = persistentObject->Get("update");
297         Deserialize(object, update, false, attributeTypes);
298         object->OnStateLoaded();
299         object->SetStateLoaded(true);
300 }
301
302 void DynamicObject::RestoreObjects(const String& filename, int attributeTypes)
303 {
304         if (!Utility::PathExists(filename))
305                 return;
306
307         Log(LogInformation, "DynamicObject")
308             << "Restoring program state from file '" << filename << "'";
309
310         std::fstream fp;
311         fp.open(filename.CStr(), std::ios_base::in);
312
313         StdioStream::Ptr sfp = new StdioStream (&fp, false);
314
315         unsigned long restored = 0;
316
317         WorkQueue upq(25000, Application::GetConcurrency());
318
319         String message;
320         StreamReadContext src;
321         for (;;) {
322                 StreamReadStatus srs = NetString::ReadStringFromStream(sfp, &message, src);
323
324                 if (srs == StatusEof)
325                         break;
326
327                 if (srs != StatusNewItem)
328                         continue;
329
330                 upq.Enqueue(boost::bind(&DynamicObject::RestoreObject, message, attributeTypes));
331                 restored++;
332         }
333
334         sfp->Close();
335
336         upq.Join();
337
338         unsigned long no_state = 0;
339
340         BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
341                 BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
342                         if (!object->GetStateLoaded()) {
343                                 object->OnStateLoaded();
344                                 object->SetStateLoaded(true);
345
346                                 no_state++;
347                         }
348                 }
349         }
350
351         Log(LogInformation, "DynamicObject")
352             << "Restored " << restored << " objects. Loaded " << no_state << " new objects without state.";
353 }
354
355 void DynamicObject::StopObjects(void)
356 {
357         BOOST_FOREACH(const DynamicType::Ptr& dt, DynamicType::GetTypes()) {
358                 BOOST_FOREACH(const DynamicObject::Ptr& object, dt->GetObjects()) {
359                         object->Deactivate();
360                 }
361         }
362 }
363
364 DynamicObject::Ptr DynamicObject::GetObject(const String& type, const String& name)
365 {
366         DynamicType::Ptr dtype = DynamicType::GetByName(type);
367         return dtype->GetObject(name);
368 }