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