- Implemented interface for SMS drivers
[public/netxms.git] / src / server / core / main.cpp
CommitLineData
9a19737f 1/*
f4c99d5a 2** NetXMS - Network Management System
1c8b8363 3** Copyright (C) 2003, 2004, 2005 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
a551fe4d 23#include "nxcore.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
0fca1cc3 36# include <signal.h>
c56962eb 37# include <sys/wait.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);
4e839863 49THREAD_RESULT THREAD_CALL NodePollManager(void *pArg);
ccdbbb52
VK
50THREAD_RESULT THREAD_CALL EventProcessor(void *pArg);
51THREAD_RESULT THREAD_CALL WatchdogThread(void *pArg);
52THREAD_RESULT THREAD_CALL ClientListener(void *pArg);
53THREAD_RESULT THREAD_CALL LocalAdminListener(void *pArg);
37c4d6aa 54THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *pArg);
9a19737f
VK
55
56
57//
58// Global variables
59//
60
a551fe4d
VK
61DWORD NXCORE_EXPORTABLE g_dwFlags = AF_USE_EVENT_LOG;
62char NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
63char NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
9e9d381c 64#ifndef _WIN32
a551fe4d 65char NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = "/var/run/netxmsd.pid";
9e9d381c 66#endif
3a2f672c 67DB_HANDLE g_hCoreDB = 0;
48b1c0ac 68DWORD g_dwDiscoveryPollingInterval;
f00307b7 69DWORD g_dwStatusPollingInterval;
364aa19a 70DWORD g_dwConfigurationPollingInterval;
06e7be2f 71char g_szDataDir[MAX_PATH];
3a2f672c
VK
72
73
74//
75// Static data
76//
77
d16cf8a5 78static CONDITION m_hEventShutdown;
9a19737f
VK
79
80
d627ae40
VK
81//
82// Sleep for specified number of seconds or until system shutdown arrives
83// Function will return TRUE if shutdown event occurs
84//
85
a551fe4d 86BOOL NXCORE_EXPORTABLE SleepAndCheckForShutdown(int iSeconds)
d627ae40 87{
d16cf8a5 88 return ConditionWait(m_hEventShutdown, iSeconds * 1000);
d627ae40
VK
89}
90
91
a551fe4d
VK
92//
93// Disconnect from database (exportable function for startup module)
94//
95
96void NXCORE_EXPORTABLE ShutdownDB(void)
97{
98 if (g_hCoreDB != NULL)
99 DBDisconnect(g_hCoreDB);
100 DBUnloadDriver();
101}
102
103
06e7be2f
VK
104//
105// Check data directory for existence
106//
107
108static 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
c9ccd685
AK
118#ifdef _WIN32
119# define MKDIR(name) mkdir(name)
120#else
121# define MKDIR(name) mkdir(name, 0700)
122#endif
123
e641b7d0
VK
124 // Create directory for mib files if it doesn't exist
125 strcpy(szBuffer, g_szDataDir);
126 strcat(szBuffer, DDIR_MIBS);
c9ccd685 127 if (MKDIR(szBuffer) == -1)
06e7be2f
VK
128 if (errno != EEXIST)
129 {
130 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
131 return FALSE;
132 }
e641b7d0
VK
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
1c8b8363
VK
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
c9ccd685 154#undef MKDIR
06e7be2f
VK
155
156 return TRUE;
157}
158
159
48b1c0ac
VK
160//
161// Load global configuration parameters
162//
163
d627ae40 164static void LoadGlobalConfig()
48b1c0ac 165{
364aa19a 166 g_dwDiscoveryPollingInterval = ConfigReadInt("DiscoveryPollingInterval", 900);
f00307b7 167 g_dwStatusPollingInterval = ConfigReadInt("StatusPollingInterval", 60);
364aa19a 168 g_dwConfigurationPollingInterval = ConfigReadInt("ConfigurationPollingInterval", 3600);
3e4e127f
VK
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;
3ea35b38
VK
173 if (ConfigReadInt("DeleteEmptySubnets", 1))
174 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
5add75d8
VK
175 if (ConfigReadInt("EnableSNMPTraps", 1))
176 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
06e7be2f 177 ConfigReadStr("DataDirectory", g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
48b1c0ac
VK
178}
179
180
9a19737f
VK
181//
182// Server initialization
183//
184
a551fe4d 185BOOL NXCORE_EXPORTABLE Initialize(void)
9a19737f 186{
2260ffe5 187 int i, iNumThreads, iDBVersion;
c7ca9142
VK
188 DWORD dwAddr;
189 char szInfo[256];
347b5bc6 190
9cc9ea72 191 InitLog(g_dwFlags & AF_USE_EVENT_LOG, g_szLogFile, g_dwFlags & AF_STANDALONE);
0ee0c807
VK
192
193#ifdef _WIN32
194 WSADATA wsaData;
195 WSAStartup(0x0002, &wsaData);
196#endif
197
b688074e
VK
198 InitLocalNetInfo();
199
067f2689
VK
200 // Create queue for delayed SQL queries
201 g_pLazyRequestQueue = new Queue(64, 16);
202
d9c2934a 203 // Initialize database driver and connect to database
9cc9ea72 204 if (!DBInit(TRUE, g_dwFlags & AF_LOG_SQL_ERRORS))
3a2f672c
VK
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 }
b1dd534d 213 DbgPrintf(AF_DEBUG_MISC, "Successfully connected to database %s@%s", g_szDbName, g_szDbServer);
3a2f672c 214
2260ffe5
VK
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
c7ca9142
VK
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 }
2260ffe5 232 g_dwFlags |= AF_DB_LOCKED;
c7ca9142 233
48b1c0ac
VK
234 // Load global configuration parameters
235 LoadGlobalConfig();
b1dd534d 236 DbgPrintf(AF_DEBUG_MISC, "Global configuration loaded");
48b1c0ac 237
18e26ff8
VK
238 // Initialize SNMP stuff
239 SnmpInit();
240
06e7be2f
VK
241 // Check data directory
242 if (!CheckDataDir())
243 return FALSE;
244
e641b7d0
VK
245 // Update hashes for image files
246 UpdateImageHashes();
247
3a2f672c 248 // Create synchronization stuff
d16cf8a5 249 m_hEventShutdown = ConditionCreate(TRUE);
3a2f672c 250
3ea35b38
VK
251 // Setup unique identifiers table
252 if (!InitIdTable())
253 return FALSE;
254
a3f23550 255 // Initialize mailer and SMS sender
b86ba1c0 256 InitMailer();
a3f23550 257 InitSMSSender();
b86ba1c0 258
da54bd8d
VK
259 // Load users from database
260 if (!LoadUsers())
347b5bc6
VK
261 {
262 WriteLog(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
da54bd8d 263 return FALSE;
347b5bc6 264 }
da54bd8d 265
2da1357c 266 // Initialize objects infrastructure and load objects from database
74b83361 267 ObjectsInit();
2da1357c
VK
268 if (!LoadObjects())
269 return FALSE;
b1dd534d 270 DbgPrintf(AF_DEBUG_MISC, "Objects loaded and initialized");
74b83361 271
c9363772
VK
272 // Initialize and load event actions
273 if (!InitActions())
274 {
275 WriteLog(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
7e495d97 276 return FALSE;
c9363772 277 }
7e495d97 278
c7ca9142
VK
279 // Initialize event handling subsystem
280 if (!InitEventSubsystem())
281 return FALSE;
282
2260ffe5
VK
283 // Initialize alarms
284 if (!g_alarmMgr.Init())
285 return FALSE;
286
1275c750
VK
287 // Initialize data collection subsystem
288 if (!InitDataCollector())
289 return FALSE;
290
f91fa4c2
VK
291 // Initialize watchdog
292 WatchdogInit();
293
3a2f672c 294 // Start threads
f91fa4c2 295 ThreadCreate(WatchdogThread, 0, NULL);
3a2f672c 296 ThreadCreate(HouseKeeper, 0, NULL);
74b83361 297 ThreadCreate(Syncer, 0, NULL);
a68e217b 298 ThreadCreate(NodePoller, 0, NULL);
4e839863 299 ThreadCreate(NodePollManager, 0, NULL);
5d0ff6bd 300 ThreadCreate(ClientListener, 0, NULL);
3e4e127f
VK
301
302 // Start network discovery thread if required
b688074e 303 CheckForMgmtNode();
3e4e127f
VK
304 if (ConfigReadInt("RunNetworkDiscovery", 1))
305 ThreadCreate(DiscoveryThread, 0, NULL);
3e4e127f 306
f9b7e653
VK
307 // Start event processors
308 iNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
309 for(i = 0; i < iNumThreads; i++)
d09b1909 310 ThreadCreate(EventProcessor, 0, (void *)(i + 1));
4385fa12 311
37c4d6aa 312 // Start SNMP trapper
106e7ae3 313 InitTraps();
37c4d6aa
VK
314 if (ConfigReadInt("EnableSNMPTraps", 1))
315 ThreadCreate(SNMPTrapReceiver, 0, NULL);
316
067f2689 317 // Start database "lazy" write thread
ccdbbb52 318 StartDBWriter();
067f2689 319
3e4e127f
VK
320 // Start local administartive interface listener if required
321 if (ConfigReadInt("EnableAdminInterface", 1))
322 ThreadCreate(LocalAdminListener, 0, NULL);
323
d21a14fc
VK
324 // Load modules
325 LoadNetXMSModules();
326
b1dd534d 327 DbgPrintf(AF_DEBUG_MISC, "Server initialization completed");
9a19737f
VK
328 return TRUE;
329}
330
331
83f0529c
VK
332//
333// Handler for client notification
334//
335
3421c063 336void NotifyClient(ClientSession *pSession, void *pArg)
83f0529c 337{
3421c063 338 pSession->Notify((DWORD)pArg);
83f0529c
VK
339}
340
341
9a19737f
VK
342//
343// Server shutdown
344//
345
a551fe4d 346void NXCORE_EXPORTABLE Shutdown(void)
9a19737f 347{
d09b1909 348 DWORD i, dwNumThreads;
e0d3b217 349
83f0529c 350 // Notify clients
3421c063 351 EnumerateClientSessions(NotifyClient, (void *)NX_NOTIFY_SHUTDOWN);
83f0529c 352
3a2f672c 353 WriteLog(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
c7ca9142 354 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
d16cf8a5 355 ConditionSet(m_hEventShutdown);
c7ca9142 356
d09b1909
VK
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
b86ba1c0 363 ShutdownMailer();
a3f23550 364 ShutdownSMSSender();
b86ba1c0
VK
365
366 ThreadSleep(3); // Give other threads a chance to terminate in a safe way
b1dd534d 367 DbgPrintf(AF_DEBUG_MISC, "All threads was notified, continue with shutdown");
d09b1909 368
d627ae40 369 SaveObjects();
b1dd534d 370 DbgPrintf(AF_DEBUG_MISC, "All objects saved to database");
ff550544 371 SaveUsers();
b1dd534d 372 DbgPrintf(AF_DEBUG_MISC, "All users saved to database");
83f0529c 373 StopDBWriter();
b1dd534d 374 DbgPrintf(AF_DEBUG_MISC, "Database writer stopped");
c7ca9142
VK
375
376 // Remove database lock
b4895bbe 377 UnlockDB();
c7ca9142
VK
378
379 // Disconnect from database and unload driver
a551fe4d 380 if (g_hCoreDB != NULL)
3a2f672c 381 DBDisconnect(g_hCoreDB);
cc140cce 382 DBUnloadDriver();
2b9c1c41 383 DbgPrintf(AF_DEBUG_MISC, "Database driver unloaded");
c7ca9142 384
c9363772 385 CleanupActions();
e0d3b217 386 ShutdownEventSubsystem();
2b9c1c41 387 DbgPrintf(AF_DEBUG_MISC, "Event processing stopped");
e0d3b217
VK
388
389 // Delete all objects
390 for(i = 0; i < g_dwIdIndexSize; i++)
391 delete g_pIndexById[i].pObject;
7e495d97 392
4385fa12 393 CloseLog();
9e9d381c
VK
394
395 // Remove PID file
396#ifndef _WIN32
397 remove(g_szPIDFile);
398#endif
9a19737f
VK
399}
400
401
c868331f
VK
402//
403// Compare given string to command template with abbreviation possibility
404//
405
406static BOOL IsCommand(char *pszTemplate, char *pszString, int iMinChars)
407{
408 int i;
409
410 // Convert given string to uppercase
411 strupr(pszString);
412
413 for(i = 0; pszString[i] != 0; i++)
414 if (pszString[i] != pszTemplate[i])
415 return FALSE;
416 if (i < iMinChars)
417 return FALSE;
418 return TRUE;
419}
420
421
422//
423// Process command entered from command line in standalone mode
424// Return TRUE if command was "down"
425//
426
427static BOOL ProcessCommand(char *pszCmdLine)
428{
429 char *pArg;
430 char szBuffer[256];
431 BOOL bExitCode = FALSE;
432
433 // Get command
434 pArg = ExtractWord(pszCmdLine, szBuffer);
435
436 if (IsCommand("DOWN", szBuffer, 4))
437 {
438 bExitCode = TRUE;
439 }
440 else if (IsCommand("DUMP", szBuffer, 2))
441 {
442 // Get argument
443 pArg = ExtractWord(pArg, szBuffer);
444
445 if (IsCommand("OBJECTS", szBuffer, 1))
446 {
04738fd8 447 DumpObjects();
c868331f
VK
448 }
449 else if (IsCommand("SESSIONS", szBuffer, 1))
450 {
451 DumpSessions();
452 }
453 else if (IsCommand("USERS", szBuffer, 1))
454 {
455 DumpUsers();
456 }
457 else
458 {
459 printf("ERROR: Invalid command argument\n\n");
460 }
461 }
462 else if (IsCommand("HELP", szBuffer, 2))
463 {
464 printf("Valid commands are:\n"
465 " down - Down NetXMS server\n"
466 " dump objects - Dump network objects to screen\n"
467 " dump sessions - Dump active client sessions to screen\n"
468 " dump users - Dump users to screen\n"
469 " mutex - Display mutex status\n"
470 " wd - Display watchdog information\n"
471 "\nAlmost all commands can be abbreviated to 2 or 3 characters\n"
472 "\n");
473 }
474 else if (IsCommand("MUTEX", szBuffer, 2))
475 {
476 printf("Mutex status:\n");
dbe67493
VK
477 DbgTestRWLock(g_rwlockIdIndex, "g_hMutexIdIndex");
478 DbgTestRWLock(g_rwlockNodeIndex, "g_hMutexNodeIndex");
479 DbgTestRWLock(g_rwlockSubnetIndex, "g_hMutexSubnetIndex");
480 DbgTestRWLock(g_rwlockInterfaceIndex, "g_hMutexInterfaceIndex");
c868331f
VK
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
0fca1cc3
VK
497//
498// Signal handler for UNIX platforms
499//
500
501#ifndef _WIN32
502
a551fe4d 503void NXCORE_EXPORTABLE OnSignal(int iSignal)
0fca1cc3 504{
c56962eb 505 //WriteLog(MSG_SIGNAL_RECEIVED, EVENTLOG_WARNING_TYPE, "d", iSignal);
35e1d81f
VK
506 switch(iSignal)
507 {
508 case SIGTERM:
509 if (!IsStandalone())
510 ConditionSet(m_hEventShutdown);
511 break;
512 case SIGSEGV:
c56962eb 513 abort();
35e1d81f 514 break;
c56962eb
AK
515 case SIGCHLD:
516 while (waitpid(-1, NULL, WNOHANG) > 0)
517 ;
35e1d81f
VK
518 default:
519 break;
520 }
0fca1cc3
VK
521}
522
523#endif
524
525
9a19737f
VK
526//
527// Common main()
528//
529
a551fe4d 530void NXCORE_EXPORTABLE Main(void)
9a19737f 531{
3a2f672c
VK
532 WriteLog(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
533
3a2f672c
VK
534 if (IsStandalone())
535 {
c868331f 536 char *ptr, szCommand[256];
3a2f672c 537
c868331f
VK
538 printf("\nNetXMS Server V" NETXMS_VERSION_STRING " Ready\n"
539 "Enter \"help\" for command list or \"down\" for server shutdown\n"
540 "System Console\n\n");
fb1a5953
VK
541
542#if USE_READLINE
543 // Initialize readline library if we use it
d9da26ec
AK
544# if RL_READLINE_VERSION && RL_VERSION_MAJOR >= 5
545 rl_bind_key('\t', (rl_command_func_t *)rl_insert);
546# else
434af69f 547 rl_bind_key('\t', (Function *)rl_insert);
d9da26ec 548# endif
fb1a5953 549#endif
c868331f 550
3a2f672c
VK
551 while(1)
552 {
fb1a5953
VK
553#if USE_READLINE
554 ptr = readline("netxmsd: ");
555#else
c868331f
VK
556 printf("netxmsd: ");
557 fflush(stdout);
558 fgets(szCommand, 255, stdin);
559 ptr = strchr(szCommand, '\n');
560 if (ptr != NULL)
561 *ptr = 0;
fb1a5953
VK
562 ptr = szCommand;
563#endif
c868331f 564
fb1a5953
VK
565 if (ptr != NULL)
566 {
567 StrStrip(ptr);
568 if (*ptr != 0)
569 {
570 if (ProcessCommand(ptr))
571 break;
572#if USE_READLINE
573 add_history(ptr);
574#endif
575 }
576#if USE_READLINE
577 free(ptr);
578#endif
579 }
580 else
581 {
582 printf("\n");
583 }
3a2f672c
VK
584 }
585
fb1a5953
VK
586#if USE_READLINE
587 free(ptr);
588#endif
3a2f672c
VK
589 Shutdown();
590 }
591 else
592 {
d16cf8a5 593 ConditionWait(m_hEventShutdown, INFINITE);
0fca1cc3
VK
594 // On Win32, Shutdown() will be called by service control handler
595#ifndef _WIN32
596 Shutdown();
597#endif
3a2f672c 598 }
9a19737f 599}
ded831ac
VK
600
601
602//
603// DLL Entry point
604//
605
606#ifdef _WIN32
607
608BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
609{
610 return TRUE;
611}
612
613#endif