- All component locks moved to memory
[public/netxms.git] / src / server / core / main.cpp
CommitLineData
9a19737f 1/*
f4c99d5a
VK
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004 NetXMS Team
9a19737f
VK
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"
2260ffe5 24#include <netxmsdb.h>
78bf9c68 25
fb1a5953
VK
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
06e7be2f 32#ifdef _WIN32
c9ccd685
AK
33# include <direct.h>
34# include <errno.h>
35#else
36# include <sys/stat.h>
0fca1cc3 37# include <signal.h>
06e7be2f
VK
38#endif
39
3a2f672c
VK
40
41//
42// Thread functions
43//
44
ccdbbb52
VK
45THREAD_RESULT THREAD_CALL HouseKeeper(void *pArg);
46THREAD_RESULT THREAD_CALL DiscoveryThread(void *pArg);
47THREAD_RESULT THREAD_CALL Syncer(void *pArg);
48THREAD_RESULT THREAD_CALL NodePoller(void *pArg);
49THREAD_RESULT THREAD_CALL StatusPoller(void *pArg);
50THREAD_RESULT THREAD_CALL ConfigurationPoller(void *pArg);
51THREAD_RESULT THREAD_CALL EventProcessor(void *pArg);
52THREAD_RESULT THREAD_CALL WatchdogThread(void *pArg);
53THREAD_RESULT THREAD_CALL ClientListener(void *pArg);
54THREAD_RESULT THREAD_CALL LocalAdminListener(void *pArg);
37c4d6aa 55THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *pArg);
9a19737f
VK
56
57
58//
59// Global variables
60//
61
3e4e127f 62DWORD g_dwFlags = AF_USE_EVENT_LOG;
4385fa12
VK
63char g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
64char g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
9e9d381c
VK
65#ifndef _WIN32
66char g_szPIDFile[MAX_PATH] = "/var/run/netxmsd.pid";
67#endif
3a2f672c 68DB_HANDLE g_hCoreDB = 0;
48b1c0ac 69DWORD g_dwDiscoveryPollingInterval;
f00307b7 70DWORD g_dwStatusPollingInterval;
364aa19a 71DWORD g_dwConfigurationPollingInterval;
06e7be2f 72char g_szDataDir[MAX_PATH];
3a2f672c
VK
73
74
75//
76// Static data
77//
78
d16cf8a5 79static CONDITION m_hEventShutdown;
9a19737f
VK
80
81
d627ae40
VK
82//
83// Sleep for specified number of seconds or until system shutdown arrives
84// Function will return TRUE if shutdown event occurs
85//
86
87BOOL SleepAndCheckForShutdown(int iSeconds)
88{
d16cf8a5 89 return ConditionWait(m_hEventShutdown, iSeconds * 1000);
d627ae40
VK
90}
91
92
06e7be2f
VK
93//
94// Check data directory for existence
95//
96
97static 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
c9ccd685
AK
107#ifdef _WIN32
108# define MKDIR(name) mkdir(name)
109#else
110# define MKDIR(name) mkdir(name, 0700)
111#endif
112
e641b7d0
VK
113 // Create directory for mib files if it doesn't exist
114 strcpy(szBuffer, g_szDataDir);
115 strcat(szBuffer, DDIR_MIBS);
c9ccd685 116 if (MKDIR(szBuffer) == -1)
06e7be2f
VK
117 if (errno != EEXIST)
118 {
119 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
120 return FALSE;
121 }
e641b7d0
VK
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
c9ccd685 133#undef MKDIR
06e7be2f
VK
134
135 return TRUE;
136}
137
138
48b1c0ac
VK
139//
140// Load global configuration parameters
141//
142
d627ae40 143static void LoadGlobalConfig()
48b1c0ac 144{
364aa19a 145 g_dwDiscoveryPollingInterval = ConfigReadInt("DiscoveryPollingInterval", 900);
f00307b7 146 g_dwStatusPollingInterval = ConfigReadInt("StatusPollingInterval", 60);
364aa19a 147 g_dwConfigurationPollingInterval = ConfigReadInt("ConfigurationPollingInterval", 3600);
3e4e127f
VK
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;
3ea35b38
VK
152 if (ConfigReadInt("DeleteEmptySubnets", 1))
153 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
5add75d8
VK
154 if (ConfigReadInt("EnableSNMPTraps", 1))
155 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
06e7be2f 156 ConfigReadStr("DataDirectory", g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
48b1c0ac
VK
157}
158
159
9a19737f
VK
160//
161// Server initialization
162//
163
164BOOL Initialize(void)
165{
2260ffe5 166 int i, iNumThreads, iDBVersion;
c7ca9142
VK
167 DWORD dwAddr;
168 char szInfo[256];
347b5bc6 169
9cc9ea72 170 InitLog(g_dwFlags & AF_USE_EVENT_LOG, g_szLogFile, g_dwFlags & AF_STANDALONE);
0ee0c807
VK
171
172#ifdef _WIN32
173 WSADATA wsaData;
174 WSAStartup(0x0002, &wsaData);
175#endif
176
b688074e
VK
177 InitLocalNetInfo();
178
067f2689
VK
179 // Create queue for delayed SQL queries
180 g_pLazyRequestQueue = new Queue(64, 16);
181
d9c2934a 182 // Initialize database driver and connect to database
9cc9ea72 183 if (!DBInit(TRUE, g_dwFlags & AF_LOG_SQL_ERRORS))
3a2f672c
VK
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 }
b1dd534d 192 DbgPrintf(AF_DEBUG_MISC, "Successfully connected to database %s@%s", g_szDbName, g_szDbServer);
3a2f672c 193
2260ffe5
VK
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
c7ca9142
VK
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 }
2260ffe5 211 g_dwFlags |= AF_DB_LOCKED;
c7ca9142 212
48b1c0ac
VK
213 // Load global configuration parameters
214 LoadGlobalConfig();
b1dd534d 215 DbgPrintf(AF_DEBUG_MISC, "Global configuration loaded");
48b1c0ac 216
18e26ff8
VK
217 // Initialize SNMP stuff
218 SnmpInit();
219
06e7be2f
VK
220 // Check data directory
221 if (!CheckDataDir())
222 return FALSE;
223
e641b7d0
VK
224 // Update hashes for image files
225 UpdateImageHashes();
226
3a2f672c 227 // Create synchronization stuff
d16cf8a5 228 m_hEventShutdown = ConditionCreate(TRUE);
3a2f672c 229
3ea35b38
VK
230 // Setup unique identifiers table
231 if (!InitIdTable())
232 return FALSE;
233
b86ba1c0
VK
234 // Initialize mailer
235 InitMailer();
236
da54bd8d
VK
237 // Load users from database
238 if (!LoadUsers())
347b5bc6
VK
239 {
240 WriteLog(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
da54bd8d 241 return FALSE;
347b5bc6 242 }
da54bd8d 243
2da1357c 244 // Initialize objects infrastructure and load objects from database
74b83361 245 ObjectsInit();
2da1357c
VK
246 if (!LoadObjects())
247 return FALSE;
b1dd534d 248 DbgPrintf(AF_DEBUG_MISC, "Objects loaded and initialized");
74b83361 249
c9363772
VK
250 // Initialize and load event actions
251 if (!InitActions())
252 {
253 WriteLog(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
7e495d97 254 return FALSE;
c9363772 255 }
7e495d97 256
c7ca9142
VK
257 // Initialize event handling subsystem
258 if (!InitEventSubsystem())
259 return FALSE;
260
2260ffe5
VK
261 // Initialize alarms
262 if (!g_alarmMgr.Init())
263 return FALSE;
264
1275c750
VK
265 // Initialize data collection subsystem
266 if (!InitDataCollector())
267 return FALSE;
268
f91fa4c2
VK
269 // Initialize watchdog
270 WatchdogInit();
271
3a2f672c 272 // Start threads
f91fa4c2 273 ThreadCreate(WatchdogThread, 0, NULL);
3a2f672c 274 ThreadCreate(HouseKeeper, 0, NULL);
74b83361 275 ThreadCreate(Syncer, 0, NULL);
a68e217b 276 ThreadCreate(NodePoller, 0, NULL);
f00307b7 277 ThreadCreate(StatusPoller, 0, NULL);
323f8bec 278 ThreadCreate(ConfigurationPoller, 0, NULL);
5d0ff6bd 279 ThreadCreate(ClientListener, 0, NULL);
3e4e127f
VK
280
281 // Start network discovery thread if required
b688074e 282 CheckForMgmtNode();
3e4e127f
VK
283 if (ConfigReadInt("RunNetworkDiscovery", 1))
284 ThreadCreate(DiscoveryThread, 0, NULL);
3e4e127f 285
f9b7e653
VK
286 // Start event processors
287 iNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
288 for(i = 0; i < iNumThreads; i++)
d09b1909 289 ThreadCreate(EventProcessor, 0, (void *)(i + 1));
4385fa12 290
37c4d6aa
VK
291 // Start SNMP trapper
292 if (ConfigReadInt("EnableSNMPTraps", 1))
293 ThreadCreate(SNMPTrapReceiver, 0, NULL);
294
067f2689 295 // Start database "lazy" write thread
ccdbbb52 296 StartDBWriter();
067f2689 297
3e4e127f
VK
298 // Start local administartive interface listener if required
299 if (ConfigReadInt("EnableAdminInterface", 1))
300 ThreadCreate(LocalAdminListener, 0, NULL);
301
b1dd534d 302 DbgPrintf(AF_DEBUG_MISC, "Server initialization completed");
9a19737f
VK
303 return TRUE;
304}
305
306
83f0529c
VK
307//
308// Handler for client notification
309//
310
3421c063 311void NotifyClient(ClientSession *pSession, void *pArg)
83f0529c 312{
3421c063 313 pSession->Notify((DWORD)pArg);
83f0529c
VK
314}
315
316
9a19737f
VK
317//
318// Server shutdown
319//
320
321void Shutdown(void)
322{
d09b1909 323 DWORD i, dwNumThreads;
e0d3b217 324
83f0529c 325 // Notify clients
3421c063 326 EnumerateClientSessions(NotifyClient, (void *)NX_NOTIFY_SHUTDOWN);
83f0529c 327
3a2f672c 328 WriteLog(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
c7ca9142 329 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
d16cf8a5 330 ConditionSet(m_hEventShutdown);
c7ca9142 331
d09b1909
VK
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
b86ba1c0
VK
338 ShutdownMailer();
339
340 ThreadSleep(3); // Give other threads a chance to terminate in a safe way
b1dd534d 341 DbgPrintf(AF_DEBUG_MISC, "All threads was notified, continue with shutdown");
d09b1909 342
d627ae40 343 SaveObjects();
b1dd534d 344 DbgPrintf(AF_DEBUG_MISC, "All objects saved to database");
ff550544 345 SaveUsers();
b1dd534d 346 DbgPrintf(AF_DEBUG_MISC, "All users saved to database");
83f0529c 347 StopDBWriter();
b1dd534d 348 DbgPrintf(AF_DEBUG_MISC, "Database writer stopped");
c7ca9142
VK
349
350 // Remove database lock
b4895bbe 351 UnlockDB();
c7ca9142
VK
352
353 // Disconnect from database and unload driver
3a2f672c
VK
354 if (g_hCoreDB != 0)
355 DBDisconnect(g_hCoreDB);
cc140cce 356 DBUnloadDriver();
2b9c1c41 357 DbgPrintf(AF_DEBUG_MISC, "Database driver unloaded");
c7ca9142 358
c9363772 359 CleanupActions();
e0d3b217 360 ShutdownEventSubsystem();
2b9c1c41 361 DbgPrintf(AF_DEBUG_MISC, "Event processing stopped");
e0d3b217
VK
362
363 // Delete all objects
364 for(i = 0; i < g_dwIdIndexSize; i++)
365 delete g_pIndexById[i].pObject;
7e495d97 366
4385fa12 367 CloseLog();
9e9d381c
VK
368
369 // Remove PID file
370#ifndef _WIN32
371 remove(g_szPIDFile);
372#endif
9a19737f
VK
373}
374
375
c868331f
VK
376//
377// Compare given string to command template with abbreviation possibility
378//
379
380static 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
401static 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 {
04738fd8 421 DumpObjects();
c868331f
VK
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");
dbe67493
VK
451 DbgTestRWLock(g_rwlockIdIndex, "g_hMutexIdIndex");
452 DbgTestRWLock(g_rwlockNodeIndex, "g_hMutexNodeIndex");
453 DbgTestRWLock(g_rwlockSubnetIndex, "g_hMutexSubnetIndex");
454 DbgTestRWLock(g_rwlockInterfaceIndex, "g_hMutexInterfaceIndex");
c868331f
VK
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
0fca1cc3
VK
472//
473// Signal handler for UNIX platforms
474//
475
476#ifndef _WIN32
477
478void OnSignal(int iSignal)
479{
480 WriteLog(MSG_SIGNAL_RECEIVED, EVENTLOG_WARNING_TYPE, "d", iSignal);
35e1d81f
VK
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 }
0fca1cc3
VK
493}
494
495#endif
496
497
9a19737f
VK
498//
499// Common main()
500//
501
502void Main(void)
503{
3a2f672c
VK
504 WriteLog(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
505
3a2f672c
VK
506 if (IsStandalone())
507 {
c868331f 508 char *ptr, szCommand[256];
3a2f672c 509
c868331f
VK
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");
fb1a5953
VK
513
514#if USE_READLINE
515 // Initialize readline library if we use it
516 rl_bind_key('\t', rl_insert);
517#endif
c868331f 518
3a2f672c
VK
519 while(1)
520 {
fb1a5953
VK
521#if USE_READLINE
522 ptr = readline("netxmsd: ");
523#else
c868331f
VK
524 printf("netxmsd: ");
525 fflush(stdout);
526 fgets(szCommand, 255, stdin);
527 ptr = strchr(szCommand, '\n');
528 if (ptr != NULL)
529 *ptr = 0;
fb1a5953
VK
530 ptr = szCommand;
531#endif
c868331f 532
fb1a5953
VK
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 }
3a2f672c
VK
552 }
553
fb1a5953
VK
554#if USE_READLINE
555 free(ptr);
556#endif
3a2f672c
VK
557 Shutdown();
558 }
559 else
560 {
d16cf8a5 561 ConditionWait(m_hEventShutdown, INFINITE);
0fca1cc3
VK
562 // On Win32, Shutdown() will be called by service control handler
563#ifndef _WIN32
564 Shutdown();
565#endif
3a2f672c 566 }
9a19737f
VK
567}
568
569
570//
571// Startup code
572//
573
574int main(int argc, char *argv[])
575{
0fca1cc3
VK
576#ifndef _WIN32
577 int i;
9e9d381c 578 FILE *fp;
0fca1cc3
VK
579#endif
580
4385fa12 581 if (!ParseCommandLine(argc, argv))
9a19737f
VK
582 return 1;
583
4385fa12 584 if (!LoadConfig())
77312b6f
VK
585 {
586 if (IsStandalone())
587 printf("Error loading configuration file\n");
9a19737f 588 return 1;
77312b6f 589 }
9a19737f 590
b7271ccb 591#ifdef _WIN32
9a19737f
VK
592 if (!IsStandalone())
593 {
594 InitService();
595 }
596 else
597 {
598 if (!Initialize())
599 {
600 printf("NMS Core initialization failed\n");
06e7be2f
VK
601
602 // Remove database lock
2260ffe5 603 if (g_dwFlags & AF_DB_LOCKED)
06e7be2f 604 {
b4895bbe 605 UnlockDB();
06e7be2f
VK
606 DBDisconnect(g_hCoreDB);
607 }
b7271ccb 608 return 3;
9a19737f
VK
609 }
610 Main();
611 }
0fca1cc3 612#else /* not _WIN32 */
b7271ccb
VK
613 if (!IsStandalone())
614 {
615 if (daemon(0, 0) == -1)
616 {
617 perror("Call to daemon() failed");
618 return 2;
619 }
0fca1cc3 620 }
85303064 621
9e9d381c
VK
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
0fca1cc3
VK
630 // Setup signal handlers
631 for(i = 0; i < 32; i++)
632 signal(i, SIG_IGN);
633 signal(SIGTERM, OnSignal);
634 signal(SIGSEGV, OnSignal);
85303064 635
0fca1cc3 636 // Initialize server
b7271ccb 637 if (!Initialize())
06e7be2f
VK
638 {
639 // Remove database lock
2260ffe5 640 if (g_dwFlags & AF_DB_LOCKED)
06e7be2f 641 {
b4895bbe 642 UnlockDB();
06e7be2f
VK
643 DBDisconnect(g_hCoreDB);
644 }
b7271ccb 645 return 3;
06e7be2f 646 }
0fca1cc3
VK
647
648 // Everything is OK, start common main loop
b7271ccb
VK
649 Main();
650#endif /* _WIN32 */
9a19737f
VK
651 return 0;
652}