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