6c69b1fffd25ae9d1bcfbbcd86c31d18a5718c8b
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004, 2005 NetXMS Team
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (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
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** $module: main.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24 #include <netxmsdb.h>
25
26 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H
27 #include <readline/readline.h>
28 #include <readline/history.h>
29 #define USE_READLINE 1
30 #endif
31
32 #ifdef _WIN32
33 # include <direct.h>
34 # include <errno.h>
35 #else
36 # include <signal.h>
37 #endif
38
39
40 //
41 // Thread functions
42 //
43
44 THREAD_RESULT THREAD_CALL HouseKeeper(void *pArg);
45 THREAD_RESULT THREAD_CALL DiscoveryThread(void *pArg);
46 THREAD_RESULT THREAD_CALL Syncer(void *pArg);
47 THREAD_RESULT THREAD_CALL NodePoller(void *pArg);
48 THREAD_RESULT THREAD_CALL StatusPoller(void *pArg);
49 THREAD_RESULT THREAD_CALL ConfigurationPoller(void *pArg);
50 THREAD_RESULT THREAD_CALL EventProcessor(void *pArg);
51 THREAD_RESULT THREAD_CALL WatchdogThread(void *pArg);
52 THREAD_RESULT THREAD_CALL ClientListener(void *pArg);
53 THREAD_RESULT THREAD_CALL LocalAdminListener(void *pArg);
54 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *pArg);
55
56
57 //
58 // Global variables
59 //
60
61 DWORD NXCORE_EXPORTABLE g_dwFlags = AF_USE_EVENT_LOG;
62 char NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
63 char NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
64 #ifndef _WIN32
65 char NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = "/var/run/netxmsd.pid";
66 #endif
67 DB_HANDLE g_hCoreDB = 0;
68 DWORD g_dwDiscoveryPollingInterval;
69 DWORD g_dwStatusPollingInterval;
70 DWORD g_dwConfigurationPollingInterval;
71 char g_szDataDir[MAX_PATH];
72
73
74 //
75 // Static data
76 //
77
78 static CONDITION m_hEventShutdown;
79
80
81 //
82 // Sleep for specified number of seconds or until system shutdown arrives
83 // Function will return TRUE if shutdown event occurs
84 //
85
86 BOOL NXCORE_EXPORTABLE SleepAndCheckForShutdown(int iSeconds)
87 {
88 return ConditionWait(m_hEventShutdown, iSeconds * 1000);
89 }
90
91
92 //
93 // Disconnect from database (exportable function for startup module)
94 //
95
96 void NXCORE_EXPORTABLE ShutdownDB(void)
97 {
98 if (g_hCoreDB != NULL)
99 DBDisconnect(g_hCoreDB);
100 DBUnloadDriver();
101 }
102
103
104 //
105 // Check data directory for existence
106 //
107
108 static BOOL CheckDataDir(void)
109 {
110 char szBuffer[MAX_PATH];
111
112 if (chdir(g_szDataDir) == -1)
113 {
114 WriteLog(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_szDataDir);
115 return FALSE;
116 }
117
118 #ifdef _WIN32
119 # define MKDIR(name) mkdir(name)
120 #else
121 # define MKDIR(name) mkdir(name, 0700)
122 #endif
123
124 // Create directory for mib files if it doesn't exist
125 strcpy(szBuffer, g_szDataDir);
126 strcat(szBuffer, DDIR_MIBS);
127 if (MKDIR(szBuffer) == -1)
128 if (errno != EEXIST)
129 {
130 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
131 return FALSE;
132 }
133
134 // Create directory for image files if it doesn't exist
135 strcpy(szBuffer, g_szDataDir);
136 strcat(szBuffer, DDIR_IMAGES);
137 if (MKDIR(szBuffer) == -1)
138 if (errno != EEXIST)
139 {
140 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
141 return FALSE;
142 }
143
144 // Create directory for package files if it doesn't exist
145 strcpy(szBuffer, g_szDataDir);
146 strcat(szBuffer, DDIR_PACKAGES);
147 if (MKDIR(szBuffer) == -1)
148 if (errno != EEXIST)
149 {
150 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
151 return FALSE;
152 }
153
154 #undef MKDIR
155
156 return TRUE;
157 }
158
159
160 //
161 // Load global configuration parameters
162 //
163
164 static void LoadGlobalConfig()
165 {
166 g_dwDiscoveryPollingInterval = ConfigReadInt("DiscoveryPollingInterval", 900);
167 g_dwStatusPollingInterval = ConfigReadInt("StatusPollingInterval", 60);
168 g_dwConfigurationPollingInterval = ConfigReadInt("ConfigurationPollingInterval", 3600);
169 if (ConfigReadInt("EnableAccessControl", 1))
170 g_dwFlags |= AF_ENABLE_ACCESS_CONTROL;
171 if (ConfigReadInt("EnableEventsAccessControl", 1))
172 g_dwFlags |= AF_ENABLE_EVENTS_ACCESS_CONTROL;
173 if (ConfigReadInt("DeleteEmptySubnets", 1))
174 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
175 if (ConfigReadInt("EnableSNMPTraps", 1))
176 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
177 ConfigReadStr("DataDirectory", g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
178 }
179
180
181 //
182 // Server initialization
183 //
184
185 BOOL NXCORE_EXPORTABLE Initialize(void)
186 {
187 int i, iNumThreads, iDBVersion;
188 DWORD dwAddr;
189 char szInfo[256];
190
191 InitLog(g_dwFlags & AF_USE_EVENT_LOG, g_szLogFile, g_dwFlags & AF_STANDALONE);
192
193 #ifdef _WIN32
194 WSADATA wsaData;
195 WSAStartup(0x0002, &wsaData);
196 #endif
197
198 InitLocalNetInfo();
199
200 // Create queue for delayed SQL queries
201 g_pLazyRequestQueue = new Queue(64, 16);
202
203 // Initialize database driver and connect to database
204 if (!DBInit(TRUE, g_dwFlags & AF_LOG_SQL_ERRORS))
205 return FALSE;
206
207 g_hCoreDB = DBConnect();
208 if (g_hCoreDB == 0)
209 {
210 WriteLog(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, NULL);
211 return FALSE;
212 }
213 DbgPrintf(AF_DEBUG_MISC, "Successfully connected to database %s@%s", g_szDbName, g_szDbServer);
214
215 // Check database version
216 iDBVersion = ConfigReadInt("DBFormatVersion", 0);
217 if (iDBVersion != DB_FORMAT_VERSION)
218 {
219 WriteLog(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
220 return FALSE;
221 }
222
223 // Initialize locks
224 if (!InitLocks(&dwAddr, szInfo))
225 {
226 if (dwAddr == UNLOCKED) // Some SQL problems
227 WriteLog(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
228 else // Database already locked by another server instance
229 WriteLog(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
230 return FALSE;
231 }
232 g_dwFlags |= AF_DB_LOCKED;
233
234 // Load global configuration parameters
235 LoadGlobalConfig();
236 DbgPrintf(AF_DEBUG_MISC, "Global configuration loaded");
237
238 // Initialize SNMP stuff
239 SnmpInit();
240
241 // Check data directory
242 if (!CheckDataDir())
243 return FALSE;
244
245 // Update hashes for image files
246 UpdateImageHashes();
247
248 // Create synchronization stuff
249 m_hEventShutdown = ConditionCreate(TRUE);
250
251 // Setup unique identifiers table
252 if (!InitIdTable())
253 return FALSE;
254
255 // Initialize mailer
256 InitMailer();
257
258 // Load users from database
259 if (!LoadUsers())
260 {
261 WriteLog(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
262 return FALSE;
263 }
264
265 // Initialize objects infrastructure and load objects from database
266 ObjectsInit();
267 if (!LoadObjects())
268 return FALSE;
269 DbgPrintf(AF_DEBUG_MISC, "Objects loaded and initialized");
270
271 // Initialize and load event actions
272 if (!InitActions())
273 {
274 WriteLog(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
275 return FALSE;
276 }
277
278 // Initialize event handling subsystem
279 if (!InitEventSubsystem())
280 return FALSE;
281
282 // Initialize alarms
283 if (!g_alarmMgr.Init())
284 return FALSE;
285
286 // Initialize data collection subsystem
287 if (!InitDataCollector())
288 return FALSE;
289
290 // Initialize watchdog
291 WatchdogInit();
292
293 // Start threads
294 ThreadCreate(WatchdogThread, 0, NULL);
295 ThreadCreate(HouseKeeper, 0, NULL);
296 ThreadCreate(Syncer, 0, NULL);
297 ThreadCreate(NodePoller, 0, NULL);
298 ThreadCreate(StatusPoller, 0, NULL);
299 ThreadCreate(ConfigurationPoller, 0, NULL);
300 ThreadCreate(ClientListener, 0, NULL);
301
302 // Start network discovery thread if required
303 CheckForMgmtNode();
304 if (ConfigReadInt("RunNetworkDiscovery", 1))
305 ThreadCreate(DiscoveryThread, 0, NULL);
306
307 // Start event processors
308 iNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
309 for(i = 0; i < iNumThreads; i++)
310 ThreadCreate(EventProcessor, 0, (void *)(i + 1));
311
312 // Start SNMP trapper
313 InitTraps();
314 if (ConfigReadInt("EnableSNMPTraps", 1))
315 ThreadCreate(SNMPTrapReceiver, 0, NULL);
316
317 // Start database "lazy" write thread
318 StartDBWriter();
319
320 // Start local administartive interface listener if required
321 if (ConfigReadInt("EnableAdminInterface", 1))
322 ThreadCreate(LocalAdminListener, 0, NULL);
323
324 // Load modules
325 LoadNetXMSModules();
326
327 DbgPrintf(AF_DEBUG_MISC, "Server initialization completed");
328 return TRUE;
329 }
330
331
332 //
333 // Handler for client notification
334 //
335
336 void NotifyClient(ClientSession *pSession, void *pArg)
337 {
338 pSession->Notify((DWORD)pArg);
339 }
340
341
342 //
343 // Server shutdown
344 //
345
346 void NXCORE_EXPORTABLE Shutdown(void)
347 {
348 DWORD i, dwNumThreads;
349
350 // Notify clients
351 EnumerateClientSessions(NotifyClient, (void *)NX_NOTIFY_SHUTDOWN);
352
353 WriteLog(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
354 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
355 ConditionSet(m_hEventShutdown);
356
357 // Stop event processor(s)
358 g_pEventQueue->Clear();
359 dwNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
360 for(i = 0; i < dwNumThreads; i++)
361 g_pEventQueue->Put(INVALID_POINTER_VALUE);
362
363 ShutdownMailer();
364
365 ThreadSleep(3); // Give other threads a chance to terminate in a safe way
366 DbgPrintf(AF_DEBUG_MISC, "All threads was notified, continue with shutdown");
367
368 SaveObjects();
369 DbgPrintf(AF_DEBUG_MISC, "All objects saved to database");
370 SaveUsers();
371 DbgPrintf(AF_DEBUG_MISC, "All users saved to database");
372 StopDBWriter();
373 DbgPrintf(AF_DEBUG_MISC, "Database writer stopped");
374
375 // Remove database lock
376 UnlockDB();
377
378 // Disconnect from database and unload driver
379 if (g_hCoreDB != NULL)
380 DBDisconnect(g_hCoreDB);
381 DBUnloadDriver();
382 DbgPrintf(AF_DEBUG_MISC, "Database driver unloaded");
383
384 CleanupActions();
385 ShutdownEventSubsystem();
386 DbgPrintf(AF_DEBUG_MISC, "Event processing stopped");
387
388 // Delete all objects
389 for(i = 0; i < g_dwIdIndexSize; i++)
390 delete g_pIndexById[i].pObject;
391
392 CloseLog();
393
394 // Remove PID file
395 #ifndef _WIN32
396 remove(g_szPIDFile);
397 #endif
398 }
399
400
401 //
402 // Compare given string to command template with abbreviation possibility
403 //
404
405 static BOOL IsCommand(char *pszTemplate, char *pszString, int iMinChars)
406 {
407 int i;
408
409 // Convert given string to uppercase
410 strupr(pszString);
411
412 for(i = 0; pszString[i] != 0; i++)
413 if (pszString[i] != pszTemplate[i])
414 return FALSE;
415 if (i < iMinChars)
416 return FALSE;
417 return TRUE;
418 }
419
420
421 //
422 // Process command entered from command line in standalone mode
423 // Return TRUE if command was "down"
424 //
425
426 static BOOL ProcessCommand(char *pszCmdLine)
427 {
428 char *pArg;
429 char szBuffer[256];
430 BOOL bExitCode = FALSE;
431
432 // Get command
433 pArg = ExtractWord(pszCmdLine, szBuffer);
434
435 if (IsCommand("DOWN", szBuffer, 4))
436 {
437 bExitCode = TRUE;
438 }
439 else if (IsCommand("DUMP", szBuffer, 2))
440 {
441 // Get argument
442 pArg = ExtractWord(pArg, szBuffer);
443
444 if (IsCommand("OBJECTS", szBuffer, 1))
445 {
446 DumpObjects();
447 }
448 else if (IsCommand("SESSIONS", szBuffer, 1))
449 {
450 DumpSessions();
451 }
452 else if (IsCommand("USERS", szBuffer, 1))
453 {
454 DumpUsers();
455 }
456 else
457 {
458 printf("ERROR: Invalid command argument\n\n");
459 }
460 }
461 else if (IsCommand("HELP", szBuffer, 2))
462 {
463 printf("Valid commands are:\n"
464 " down - Down NetXMS server\n"
465 " dump objects - Dump network objects to screen\n"
466 " dump sessions - Dump active client sessions to screen\n"
467 " dump users - Dump users to screen\n"
468 " mutex - Display mutex status\n"
469 " wd - Display watchdog information\n"
470 "\nAlmost all commands can be abbreviated to 2 or 3 characters\n"
471 "\n");
472 }
473 else if (IsCommand("MUTEX", szBuffer, 2))
474 {
475 printf("Mutex status:\n");
476 DbgTestRWLock(g_rwlockIdIndex, "g_hMutexIdIndex");
477 DbgTestRWLock(g_rwlockNodeIndex, "g_hMutexNodeIndex");
478 DbgTestRWLock(g_rwlockSubnetIndex, "g_hMutexSubnetIndex");
479 DbgTestRWLock(g_rwlockInterfaceIndex, "g_hMutexInterfaceIndex");
480 DbgTestMutex(g_hMutexObjectAccess, "g_hMutexObjectAccess");
481 printf("\n");
482 }
483 else if (IsCommand("WD", szBuffer, 2))
484 {
485 WatchdogPrintStatus();
486 printf("\n");
487 }
488 else
489 {
490 printf("INVALID COMMAND\n\n");
491 }
492
493 return bExitCode;
494 }
495
496
497 //
498 // Signal handler for UNIX platforms
499 //
500
501 #ifndef _WIN32
502
503 void NXCORE_EXPORTABLE OnSignal(int iSignal)
504 {
505 WriteLog(MSG_SIGNAL_RECEIVED, EVENTLOG_WARNING_TYPE, "d", iSignal);
506 switch(iSignal)
507 {
508 case SIGTERM:
509 if (!IsStandalone())
510 ConditionSet(m_hEventShutdown);
511 break;
512 case SIGSEGV:
513 exit(5);
514 break;
515 default:
516 break;
517 }
518 }
519
520 #endif
521
522
523 //
524 // Common main()
525 //
526
527 void NXCORE_EXPORTABLE Main(void)
528 {
529 WriteLog(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
530
531 if (IsStandalone())
532 {
533 char *ptr, szCommand[256];
534
535 printf("\nNetXMS Server V" NETXMS_VERSION_STRING " Ready\n"
536 "Enter \"help\" for command list or \"down\" for server shutdown\n"
537 "System Console\n\n");
538
539 #if USE_READLINE
540 // Initialize readline library if we use it
541 rl_bind_key('\t', (Function *)rl_insert);
542 #endif
543
544 while(1)
545 {
546 #if USE_READLINE
547 ptr = readline("netxmsd: ");
548 #else
549 printf("netxmsd: ");
550 fflush(stdout);
551 fgets(szCommand, 255, stdin);
552 ptr = strchr(szCommand, '\n');
553 if (ptr != NULL)
554 *ptr = 0;
555 ptr = szCommand;
556 #endif
557
558 if (ptr != NULL)
559 {
560 StrStrip(ptr);
561 if (*ptr != 0)
562 {
563 if (ProcessCommand(ptr))
564 break;
565 #if USE_READLINE
566 add_history(ptr);
567 #endif
568 }
569 #if USE_READLINE
570 free(ptr);
571 #endif
572 }
573 else
574 {
575 printf("\n");
576 }
577 }
578
579 #if USE_READLINE
580 free(ptr);
581 #endif
582 Shutdown();
583 }
584 else
585 {
586 ConditionWait(m_hEventShutdown, INFINITE);
587 // On Win32, Shutdown() will be called by service control handler
588 #ifndef _WIN32
589 Shutdown();
590 #endif
591 }
592 }
593
594
595 //
596 // DLL Entry point
597 //
598
599 #ifdef _WIN32
600
601 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
602 {
603 return TRUE;
604 }
605
606 #endif