]> granicus.if.org Git - icinga2/blob - base/application.cpp
Replaced custom event code with Boost.Signals.
[icinga2] / base / application.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012 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 "i2-base.h"
21
22 #ifndef _WIN32
23 #       include <ltdl.h>
24 #endif
25
26 using namespace icinga;
27
28 Application::Ptr I2_EXPORT Application::m_Instance;
29 bool I2_EXPORT Application::m_ShuttingDown = false;
30 bool I2_EXPORT Application::m_Debugging = false;
31
32 /**
33  * Constructor for the Application class.
34  */
35 Application::Application(void)
36 {
37 #ifdef _WIN32
38         WSADATA wsaData;
39         if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
40                 throw Win32Exception("WSAStartup failed", WSAGetLastError());
41 #else /* _WIN32 */
42         lt_dlinit();
43 #endif /* _WIN32 */
44
45         char *debugging = getenv("_DEBUG");
46         m_Debugging = (debugging && strtol(debugging, NULL, 10) != 0);
47
48 #ifdef _WIN32
49         if (IsDebuggerPresent())
50                 m_Debugging = true;
51 #endif /* _WIN32 */
52 }
53
54 /**
55  * Destructor for the application class.
56  */
57 Application::~Application(void)
58 {
59         m_ShuttingDown = true;
60
61         /* stop all components */
62         for (map<string, Component::Ptr>::iterator i = m_Components.begin();
63             i != m_Components.end(); i++) {
64                 i->second->Stop();
65         }
66
67         m_Components.clear();
68
69 #ifdef _WIN32
70         WSACleanup();
71 #else /* _WIN32 */
72         //lt_dlexit();
73 #endif /* _WIN32 */
74 }
75
76 /**
77  * Retrieves a pointer to the application singleton object.
78  *
79  * @returns The application object.
80  */
81 Application::Ptr Application::GetInstance(void)
82 {
83         if (m_ShuttingDown)
84                 return Application::Ptr();
85         else
86                 return m_Instance;
87 }
88
89 /**
90  * Processes events for registered sockets and timers and calls whatever
91  * handlers have been set up for these events.
92  */
93 void Application::RunEventLoop(void)
94 {
95         while (!m_ShuttingDown) {
96                 fd_set readfds, writefds, exceptfds;
97                 int nfds = -1;
98
99                 Object::ClearHeldObjects();
100
101                 Timer::CallExpiredTimers();
102
103                 FD_ZERO(&readfds);
104                 FD_ZERO(&writefds);
105                 FD_ZERO(&exceptfds);
106
107                 Socket::CollectionType::iterator prev, i;
108                 for (i = Socket::Sockets.begin();
109                     i != Socket::Sockets.end(); ) {
110                         Socket::Ptr socket = i->lock();
111
112                         prev = i;
113                         i++;
114
115                         if (!socket) {
116                                 Socket::Sockets.erase(prev);
117                                 continue;
118                         }
119
120                         int fd = socket->GetFD();
121
122                         if (socket->WantsToWrite())
123                                 FD_SET(fd, &writefds);
124
125                         if (socket->WantsToRead())
126                                 FD_SET(fd, &readfds);
127
128                         FD_SET(fd, &exceptfds);
129
130                         if (fd > nfds)
131                                 nfds = fd;
132                 }
133
134                 time_t now = time(NULL);
135                 time_t next = Timer::GetNextCall();
136                 time_t sleep = (next < now) ? 0 : (next - now);
137
138                 if (m_ShuttingDown)
139                         break;
140
141                 timeval tv;
142                 tv.tv_sec = (sleep < 0) ? 0 : (long)sleep;
143                 tv.tv_usec = 0;
144
145                 int ready;
146
147                 if (nfds == -1) {
148                         Sleep(tv.tv_sec * 1000 + tv.tv_usec);
149                         ready = 0;
150                 } else
151                         ready = select(nfds + 1, &readfds, &writefds,
152                             &exceptfds, &tv);
153
154                 if (ready < 0)
155                         break;
156                 else if (ready == 0)
157                         continue;
158
159                 EventArgs ea;
160                 ea.Source = shared_from_this();
161
162                 for (i = Socket::Sockets.begin();
163                     i != Socket::Sockets.end(); ) {
164                         Socket::Ptr socket = i->lock();
165
166                         prev = i;
167                         i++;
168
169                         if (!socket) {
170                                 Socket::Sockets.erase(prev);
171                                 continue;
172                         }
173
174                         int fd;
175
176                         fd = socket->GetFD();
177                         if (fd != INVALID_SOCKET && FD_ISSET(fd, &writefds))
178                                 socket->OnWritable(ea);
179
180                         fd = socket->GetFD();
181                         if (fd != INVALID_SOCKET && FD_ISSET(fd, &readfds))
182                                 socket->OnReadable(ea);
183
184                         fd = socket->GetFD();
185                         if (fd != INVALID_SOCKET && FD_ISSET(fd, &exceptfds))
186                                 socket->OnException(ea);
187                 }
188         }
189 }
190
191 /**
192  * Signals the application to shut down during the next
193  * execution of the event loop.
194  */
195 void Application::Shutdown(void)
196 {
197         m_ShuttingDown = true;
198 }
199
200 /**
201  * Loads a component from a shared library.
202  *
203  * @param path The path of the component library.
204  * @param componentConfig The configuration for the component.
205  * @returns The component.
206  */
207 Component::Ptr Application::LoadComponent(const string& path,
208     const ConfigObject::Ptr& componentConfig)
209 {
210         Component::Ptr component;
211         Component *(*pCreateComponent)();
212
213         Log("Loading component '" + path + "'");
214
215 #ifdef _WIN32
216         HMODULE hModule = LoadLibrary(path.c_str());
217 #else /* _WIN32 */
218         lt_dlhandle hModule = lt_dlopen(path.c_str());
219 #endif /* _WIN32 */
220
221         if (hModule == NULL)
222                 throw runtime_error("Could not load module");
223
224 #ifdef _WIN32
225         pCreateComponent = (CreateComponentFunction)GetProcAddress(hModule,
226             "CreateComponent");
227 #else /* _WIN32 */
228 #       ifdef __GNUC__
229         /* suppress compiler warning for void * cast */
230         __extension__
231 #       endif
232         pCreateComponent = (CreateComponentFunction)lt_dlsym(hModule,
233             "CreateComponent");
234 #endif /* _WIN32 */
235
236         if (pCreateComponent == NULL)
237                 throw runtime_error("Loadable module does not contain "
238                     "CreateComponent function");
239
240         component = Component::Ptr(pCreateComponent());
241         component->SetConfig(componentConfig);
242         RegisterComponent(component);
243         return component;
244 }
245
246 /**
247  * Registers a component object and starts it.
248  *
249  * @param component The component.
250  */
251 void Application::RegisterComponent(Component::Ptr component)
252 {
253         m_Components[component->GetName()] = component;
254
255         component->Start();
256 }
257
258 /**
259  * Unregisters a component object and stops it.
260  *
261  * @param component The component.
262  */
263 void Application::UnregisterComponent(Component::Ptr component)
264 {
265         string name = component->GetName();
266
267         Log("Unloading component '" + name + "'");
268         map<string, Component::Ptr>::iterator i = m_Components.find(name);
269         if (i != m_Components.end())
270                 m_Components.erase(i);
271                 
272         component->Stop();
273 }
274
275 /**
276  * Finds a loaded component by name.
277  *
278  * @param name The name of the component.
279  * @returns The component or a null pointer if the component could not be found.
280  */
281 Component::Ptr Application::GetComponent(const string& name) const
282 {
283         map<string, Component::Ptr>::const_iterator i = m_Components.find(name);
284
285         if (i == m_Components.end())
286                 return Component::Ptr();
287
288         return i->second;
289 }
290
291 /**
292  * Writes a message to the application's log.
293  *
294  * @param message The message.
295  */
296 void Application::Log(string message)
297 {
298         char timestamp[100];
299
300         time_t now;
301         time(&now);
302         tm tmnow = *localtime(&now);
303
304         strftime(timestamp, sizeof(timestamp), "%a %B %d %Y %H:%M:%S", &tmnow);
305
306         cout << "[" << timestamp << "]: " << message << endl;
307 }
308
309 /**
310  * Retrieves the directory the application's binary is contained in.
311  *
312  * @returns The directory.
313  */
314 string Application::GetExeDirectory(void) const
315 {
316         static string ExePath;
317
318         if (ExePath.length() != 0)
319                 return ExePath;
320
321 #ifndef _WIN32
322         char Cwd[MAXPATHLEN];
323         char *PathEnv, *Directory, PathTest[MAXPATHLEN], FullExePath[MAXPATHLEN];
324         bool FoundPath;
325
326         const char *argv0 = m_Arguments[0].c_str();
327
328         if (getcwd(Cwd, sizeof(Cwd)) == NULL)
329                 throw PosixException("getcwd failed", errno);
330
331         if (argv0[0] != '/')
332                 snprintf(FullExePath, sizeof(FullExePath), "%s/%s", Cwd, argv0);
333         else
334                 strncpy(FullExePath, argv0, sizeof(FullExePath));
335
336         if (strchr(argv0, '/') == NULL) {
337                 PathEnv = getenv("PATH");
338
339                 if (PathEnv != NULL) {
340                         PathEnv = strdup(PathEnv);
341
342                         if (PathEnv == NULL)
343                                 throw bad_alloc();
344
345                         FoundPath = false;
346
347                         for (Directory = strtok(PathEnv, ":"); Directory != NULL; Directory = strtok(NULL, ":")) {
348                                 if (snprintf(PathTest, sizeof(PathTest), "%s/%s", Directory, argv0) < 0)
349                                         throw PosixException("snprintf failed", errno);
350
351                                 if (access(PathTest, X_OK) == 0) {
352                                         strncpy(FullExePath, PathTest, sizeof(FullExePath));
353
354                                         FoundPath = true;
355
356                                         break;
357                                 }
358                         }
359
360                         free(PathEnv);
361
362                         if (!FoundPath)
363                                 throw runtime_error("Could not determine executable path.");
364                 }
365         }
366
367         char Buf[PATH_MAX];
368         if (realpath(FullExePath, Buf) == NULL)
369                 throw PosixException("realpath failed", errno);
370
371         // remove filename
372         char *LastSlash = strrchr(Buf, '/');
373
374         if (LastSlash != NULL)
375                 *LastSlash = '\0';
376
377         ExePath = string(Buf);
378 #else /* _WIN32 */
379         char FullExePath[MAXPATHLEN];
380
381         GetModuleFileName(NULL, FullExePath, sizeof(FullExePath));
382
383         PathRemoveFileSpec(FullExePath);
384
385         ExePath = string(FullExePath);
386 #endif /* _WIN32 */
387
388         return ExePath;
389 }
390
391 /**
392  * Adds a directory to the component search path.
393  *
394  * @param componentDirectory The directory.
395  */
396 void Application::AddComponentSearchDir(const string& componentDirectory)
397 {
398 #ifdef _WIN32
399         SetDllDirectory(componentDirectory.c_str());
400 #else /* _WIN32 */
401         lt_dladdsearchdir(componentDirectory.c_str());
402 #endif /* _WIN32 */
403 }
404
405 /**
406  * Retrieves the debugging mode of the application.
407  *
408  * @returns true if the application is being debugged, false otherwise
409  */
410 bool Application::IsDebugging(void)
411 {
412         return m_Debugging;
413 }
414
415 #ifndef _WIN32
416 /**
417  * Signal handler for SIGINT. Prepares the application for cleanly
418  * shutting down during the next execution of the event loop.
419  *
420  * @param signum The signal number.
421  */
422 void Application::SigIntHandler(int signum)
423 {
424         assert(signum == SIGINT);
425
426         Application::GetInstance()->Shutdown();
427
428         struct sigaction sa;
429         memset(&sa, 0, sizeof(sa));
430         sa.sa_handler = SIG_DFL;
431         sigaction(SIGINT, &sa, NULL);
432 }
433 #else /* _WIN32 */
434 /**
435  * Console control handler. Prepares the application for cleanly
436  * shutting down during the next execution of the event loop.
437  */
438 BOOL WINAPI Application::CtrlHandler(DWORD type)
439 {
440         Application::GetInstance()->Shutdown();
441         SetConsoleCtrlHandler(NULL, FALSE);
442         return TRUE;
443 }
444 #endif /* _WIN32 */
445
446 /**
447  * Runs the application.
448  *
449  * @param argc The number of arguments.
450  * @param argv The arguments that should be passed to the application.
451  * @returns The application's exit code.
452  */
453 int Application::Run(int argc, char **argv)
454 {
455         int result;
456
457         assert(!Application::m_Instance);
458         Application::m_Instance = static_pointer_cast<Application>(shared_from_this());
459
460 #ifndef _WIN32
461         struct sigaction sa;
462         memset(&sa, 0, sizeof(sa));
463         sa.sa_handler = &Application::SigIntHandler;
464         sigaction(SIGINT, &sa, NULL);
465
466         sa.sa_handler = SIG_IGN;
467         sigaction(SIGPIPE, &sa, NULL);
468 #else
469         SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE);
470 #endif /* _WIN32 */
471
472         m_Arguments.clear();
473         for (int i = 0; i < argc; i++)
474                 m_Arguments.push_back(string(argv[i]));
475
476         if (IsDebugging()) {
477                 result = Main(m_Arguments);
478
479                 Application::m_Instance.reset();
480         } else {
481                 try {
482                         result = Main(m_Arguments);
483                 } catch (const std::exception& ex) {
484                         Application::m_Instance.reset();
485
486                         Application::Log("---");
487                         Application::Log("Exception: " + Utility::GetTypeName(ex));
488                         Application::Log("Message: " + string(ex.what()));
489
490                         return EXIT_FAILURE;
491                 }
492         }
493
494         return result;
495 }