added function RevNXCPMessageEx which can dynamically increase message buffer
[public/netxms.git] / src / server / core / main.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
48c0079d 3** Copyright (C) 2003-2011 NetXMS Team
5039dede
AK
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** File: main.cpp
20**
21**/
22
23#include "nxcore.h"
24#include <netxmsdb.h>
25
0322eed7
VK
26#if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE
27#include <readline/readline.h>
28#include <readline/history.h>
29#define USE_READLINE 1
5039dede
AK
30#endif
31
32#ifdef _WIN32
a23d8e0d
VK
33#include <errno.h>
34#include <psapi.h>
d717b69c
VK
35#define open _open
36#define write _write
37#define close _close
5039dede 38#else
a23d8e0d
VK
39#include <signal.h>
40#include <sys/wait.h>
5039dede
AK
41#endif
42
43
44//
45// Messages generated by mc.pl (for UNIX version only)
46//
47
48#ifndef _WIN32
49extern unsigned int g_dwNumMessages;
50extern const TCHAR *g_szMessages[];
51#endif
52
53
54//
55// Shutdown reasons
56//
57
58#define SHUTDOWN_DEFAULT 0
59#define SHUTDOWN_FROM_CONSOLE 1
60#define SHUTDOWN_BY_SIGNAL 2
61
62
63//
64// Externals
65//
66
67extern Queue g_statusPollQueue;
68extern Queue g_configPollQueue;
040c45fa 69extern Queue g_topologyPollQueue;
5039dede
AK
70extern Queue g_routePollQueue;
71extern Queue g_discoveryPollQueue;
72extern Queue g_nodePollerQueue;
73extern Queue g_conditionPollerQueue;
74extern Queue *g_pItemQueue;
75
76void InitCertificates(void);
ab185583
VK
77void InitUsers();
78void CleanupUsers();
5039dede
AK
79
80
81//
82// Thread functions
83//
84
85THREAD_RESULT THREAD_CALL HouseKeeper(void *pArg);
86THREAD_RESULT THREAD_CALL Syncer(void *pArg);
87THREAD_RESULT THREAD_CALL NodePoller(void *pArg);
88THREAD_RESULT THREAD_CALL PollManager(void *pArg);
89THREAD_RESULT THREAD_CALL EventProcessor(void *pArg);
90THREAD_RESULT THREAD_CALL WatchdogThread(void *pArg);
91THREAD_RESULT THREAD_CALL ClientListener(void *pArg);
92THREAD_RESULT THREAD_CALL ISCListener(void *pArg);
93THREAD_RESULT THREAD_CALL LocalAdminListener(void *pArg);
94THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *pArg);
95THREAD_RESULT THREAD_CALL SyslogDaemon(void *pArg);
96THREAD_RESULT THREAD_CALL BeaconPoller(void *pArg);
3929b1ca 97THREAD_RESULT THREAD_CALL JobManagerThread(void *arg);
5039dede
AK
98
99
100//
101// Global variables
102//
103
104TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
105TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
106TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
107TCHAR g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
108TCHAR NXCORE_EXPORTABLE g_szListenAddress[MAX_PATH] = _T("0.0.0.0");
109#ifndef _WIN32
35f836fe 110char NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = _T("/var/run/netxmsd.pid");
5039dede
AK
111#endif
112DB_HANDLE g_hCoreDB = 0;
113DWORD g_dwDiscoveryPollingInterval;
114DWORD g_dwStatusPollingInterval;
115DWORD g_dwConfigurationPollingInterval;
116DWORD g_dwRoutingTableUpdateInterval;
040c45fa 117DWORD g_dwTopologyPollingInterval;
5039dede
AK
118DWORD g_dwConditionPollingInterval;
119DWORD g_dwPingSize;
120DWORD g_dwAuditFlags;
d96bd4c7 121TCHAR g_szDataDir[MAX_PATH] = _T("");
9796ce45 122TCHAR g_szLibDir[MAX_PATH] = DEFAULT_LIBDIR;
28f5b9a4 123int g_nDBSyntax = DB_SYNTAX_UNKNOWN;
73869331 124DWORD NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
5039dede
AK
125QWORD g_qwServerId;
126RSA *g_pServerKey = NULL;
127time_t g_tServerStartTime = 0;
128DWORD g_dwLockTimeout = 60000; // Default timeout for acquiring mutex
5039dede
AK
129DWORD g_dwAgentCommandTimeout = 2000; // Default timeout for requests to agent
130DWORD g_dwThresholdRepeatInterval = 0; // Disabled by default
131int g_nRequiredPolls = 1;
b8c1ec69 132DB_DRIVER g_dbDriver = NULL;
5039dede
AK
133
134
135//
136// Static data
137//
138
139static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
140static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
141static THREAD m_thHouseKeeper = INVALID_THREAD_HANDLE;
142static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
143static THREAD m_thSyslogDaemon = INVALID_THREAD_HANDLE;
144static int m_nShutdownReason = SHUTDOWN_DEFAULT;
145
146#ifndef _WIN32
147static pthread_t m_signalHandlerThread;
148#endif
149
150
151//
152// Sleep for specified number of seconds or until system shutdown arrives
153// Function will return TRUE if shutdown event occurs
154//
155
156BOOL NXCORE_EXPORTABLE SleepAndCheckForShutdown(int iSeconds)
157{
158 return ConditionWait(m_condShutdown, iSeconds * 1000);
159}
160
161
162//
163// Disconnect from database (exportable function for startup module)
164//
165
73869331 166void NXCORE_EXPORTABLE ShutdownDB()
5039dede
AK
167{
168 if (g_hCoreDB != NULL)
169 DBDisconnect(g_hCoreDB);
b8c1ec69 170 DBUnloadDriver(g_dbDriver);
5039dede
AK
171}
172
173
174//
175// Check data directory for existence
176//
177
35f836fe 178static BOOL CheckDataDir()
5039dede 179{
35f836fe 180 TCHAR szBuffer[MAX_PATH];
5039dede 181
35f836fe 182 if (_tchdir(g_szDataDir) == -1)
5039dede
AK
183 {
184 nxlog_write(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_szDataDir);
185 return FALSE;
186 }
187
188#ifdef _WIN32
35f836fe 189#define MKDIR(name) _tmkdir(name)
5039dede 190#else
35f836fe 191#define MKDIR(name) _tmkdir(name, 0700)
5039dede
AK
192#endif
193
194 // Create directory for mib files if it doesn't exist
35f836fe
VK
195 _tcscpy(szBuffer, g_szDataDir);
196 _tcscat(szBuffer, DDIR_MIBS);
5039dede
AK
197 if (MKDIR(szBuffer) == -1)
198 if (errno != EEXIST)
199 {
200 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
201 return FALSE;
202 }
203
5039dede 204 // Create directory for package files if it doesn't exist
35f836fe
VK
205 _tcscpy(szBuffer, g_szDataDir);
206 _tcscat(szBuffer, DDIR_PACKAGES);
5039dede
AK
207 if (MKDIR(szBuffer) == -1)
208 if (errno != EEXIST)
209 {
210 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
211 return FALSE;
212 }
213
4d0c32f3 214 // Create directory for map background images if it doesn't exist
35f836fe
VK
215 _tcscpy(szBuffer, g_szDataDir);
216 _tcscat(szBuffer, DDIR_BACKGROUNDS);
4d0c32f3
VK
217 if (MKDIR(szBuffer) == -1)
218 if (errno != EEXIST)
219 {
220 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
221 return FALSE;
222 }
223
e6b9439a
AK
224 // Create directory for image library is if does't exists
225 _tcscpy(szBuffer, g_szDataDir);
226 _tcscat(szBuffer, DDIR_IMAGES);
227 if (MKDIR(szBuffer) == -1)
228 {
229 if (errno != EEXIST)
230 {
231 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
232 return FALSE;
233 }
234 }
235
619e5c9b
VK
236 // Create directory for file store if does't exists
237 _tcscpy(szBuffer, g_szDataDir);
238 _tcscat(szBuffer, DDIR_FILES);
239 if (MKDIR(szBuffer) == -1)
240 {
241 if (errno != EEXIST)
242 {
243 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
244 return FALSE;
245 }
246 }
247
5039dede
AK
248#undef MKDIR
249
250 return TRUE;
251}
252
253
254//
255// Load global configuration parameters
256//
257
258static void LoadGlobalConfig()
259{
35f836fe
VK
260 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
261 g_dwStatusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
262 g_dwConfigurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
263 g_dwRoutingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
040c45fa 264 g_dwTopologyPollingInterval = ConfigReadInt(_T("TopologyPollingInterval"), 1800);
35f836fe
VK
265 g_dwConditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
266 if (ConfigReadInt(_T("DeleteEmptySubnets"), 1))
5039dede 267 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
35f836fe 268 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
5039dede 269 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
35f836fe 270 if (ConfigReadInt(_T("EnableZoning"), 0))
5039dede 271 g_dwFlags |= AF_ENABLE_ZONING;
35f836fe 272 if (ConfigReadInt(_T("EnableMultipleDBConnections"), 1))
2a8e6a7b
VK
273 {
274 // SQLite has troubles with multiple connections to the same database
275 // from different threads, and it does not speed up database access
276 // anyway, so we will not enable multiple connections for SQLite
277 if (g_nDBSyntax != DB_SYNTAX_SQLITE)
278 {
279 g_dwFlags |= AF_ENABLE_MULTIPLE_DB_CONN;
280 }
281 else
282 {
283 DbgPrintf(1, _T("Configuration parameter EnableMultipleDBConnections ignored because database engine is SQLite"));
284 }
285 }
35f836fe 286 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
5039dede 287 g_dwFlags |= AF_ENABLE_NETWORK_DISCOVERY;
35f836fe 288 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
5039dede 289 g_dwFlags |= AF_ACTIVE_NETWORK_DISCOVERY;
35f836fe 290 if (ConfigReadInt(_T("ResolveNodeNames"), 1))
5039dede 291 g_dwFlags |= AF_RESOLVE_NODE_NAMES;
35f836fe 292 if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
5039dede 293 g_dwFlags |= AF_SYNC_NODE_NAMES_WITH_DNS;
35f836fe 294 if (ConfigReadInt(_T("InternalCA"), 0))
5039dede 295 g_dwFlags |= AF_INTERNAL_CA;
35f836fe 296 if (ConfigReadInt(_T("CheckTrustedNodes"), 1))
5039dede 297 g_dwFlags |= AF_CHECK_TRUSTED_NODES;
d96bd4c7
VK
298
299 if (g_szDataDir[0] == 0)
300 {
35f836fe 301 ConfigReadStr(_T("DataDirectory"), g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
d96bd4c7
VK
302 DbgPrintf(1, _T("Data directory set to %s from server configuration variable"), g_szDataDir);
303 }
304 else
305 {
306 DbgPrintf(1, _T("Using data directory %s"), g_szDataDir);
307 }
308
35f836fe
VK
309 g_dwPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
310 g_dwLockTimeout = ConfigReadInt(_T("LockTimeout"), 60000);
311 g_dwSNMPTimeout = ConfigReadInt(_T("SNMPRequestTimeout"), 2000);
312 g_dwAgentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 2000);
313 g_dwThresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
314 g_nRequiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
5039dede
AK
315}
316
317
318//
319// Initialize cryptografic functions
320//
321
73869331 322static BOOL InitCryptografy()
5039dede
AK
323{
324#ifdef _WITH_ENCRYPTION
35f836fe 325 TCHAR szKeyFile[MAX_PATH];
5039dede
AK
326 BOOL bResult = FALSE;
327 int fd, iPolicy;
328 DWORD dwLen;
329 BYTE *pBufPos, *pKeyBuffer, hash[SHA1_DIGEST_SIZE];
330
35f836fe 331 if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 15)))
5039dede
AK
332 return FALSE;
333
35f836fe
VK
334 _tcscpy(szKeyFile, g_szDataDir);
335 _tcscat(szKeyFile, DFILE_KEYS);
336 fd = _topen(szKeyFile, O_RDONLY | O_BINARY);
5039dede
AK
337 g_pServerKey = LoadRSAKeys(szKeyFile);
338 if (g_pServerKey == NULL)
339 {
35f836fe 340 DbgPrintf(1, _T("Generating RSA key pair..."));
5039dede
AK
341 g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
342 if (g_pServerKey != NULL)
343 {
35f836fe 344 fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
5039dede
AK
345 if (fd != -1)
346 {
347 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
348 dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
349 pKeyBuffer = (BYTE *)malloc(dwLen);
350
351 pBufPos = pKeyBuffer;
352 i2d_RSAPublicKey(g_pServerKey, &pBufPos);
353 i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
354 write(fd, &dwLen, sizeof(DWORD));
355 write(fd, pKeyBuffer, dwLen);
356
357 CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
358 write(fd, hash, SHA1_DIGEST_SIZE);
359
360 close(fd);
361 free(pKeyBuffer);
362 bResult = TRUE;
363 }
364 }
365 }
366 else
367 {
368 bResult = TRUE;
369 }
370
35f836fe 371 iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
5039dede
AK
372 if ((iPolicy < 0) || (iPolicy > 3))
373 iPolicy = 1;
374 SetAgentDEP(iPolicy);
375
376 return bResult;
377#else
378 return TRUE;
379#endif
380}
381
382
383//
384// Check if process with given PID exists and is a NetXMS server process
385//
386
387static BOOL IsNetxmsdProcess(DWORD dwPID)
388{
389#ifdef _WIN32
390 HANDLE hProcess;
391 TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
392 BOOL bRet = FALSE;
393
394 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
395 if (hProcess != NULL)
396 {
397 if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
398 (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
399 {
400 bRet = !_tcsicmp(szExtModule, szIntModule);
401 }
402 else
403 {
404 // Cannot read process name, for safety assume that it's a server process
405 bRet = TRUE;
406 }
407 CloseHandle(hProcess);
408 }
409 return bRet;
410#else
411 return (kill((pid_t)dwPID, 0) != -1);
412#endif
413}
414
415
416//
417// Database event handler
418//
419
35f836fe 420static void DBEventHandler(DWORD dwEvent, const WCHAR *pszArg1, const WCHAR *pszArg2, void *userArg)
5039dede
AK
421{
422 if (!(g_dwFlags & AF_SERVER_INITIALIZED))
423 return; // Don't try to do anything if server is not ready yet
424
425 switch(dwEvent)
426 {
427 case DBEVENT_CONNECTION_LOST:
428 PostEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
429 g_dwFlags |= AF_DB_CONNECTION_LOST;
430 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
431 break;
432 case DBEVENT_CONNECTION_RESTORED:
433 PostEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
434 g_dwFlags &= ~AF_DB_CONNECTION_LOST;
435 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
436 break;
4d0c32f3
VK
437 case DBEVENT_QUERY_FAILED:
438 PostEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "ss", pszArg1, pszArg2);
439 break;
5039dede
AK
440 default:
441 break;
442 }
443}
444
445
446//
f669df41
VK
447// Send console message to session with open console
448//
449
450static void SendConsoleMessage(ClientSession *session, void *arg)
451{
452 if (session->isConsoleOpen())
453 {
454 CSCPMessage msg;
455
456 msg.SetCode(CMD_ADM_MESSAGE);
457 msg.SetVariable(VID_MESSAGE, (TCHAR *)arg);
458 session->sendMessage(&msg);
459 }
460}
461
462
463//
464// Console writer
465//
466
467static void LogConsoleWriter(const TCHAR *format, ...)
468{
469 TCHAR buffer[8192];
470 va_list args;
471
472 va_start(args, format);
473 _vsntprintf(buffer, 8192, format, args);
474 buffer[8191] = 0;
475 va_end(args);
476
477 WriteToTerminal(buffer);
478
479 EnumerateClientSessions(SendConsoleMessage, buffer);
480}
481
482
483//
5039dede
AK
484// Server initialization
485//
486
bc980b27 487BOOL NXCORE_EXPORTABLE Initialize()
5039dede
AK
488{
489 int i, iDBVersion;
490 DWORD dwAddr;
35f836fe 491 TCHAR szInfo[256];
5039dede
AK
492
493 g_tServerStartTime = time(NULL);
494 srand((unsigned int)g_tServerStartTime);
495 nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
496 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
497 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
498 _T("LIBNXSRV.DLL"),
499#ifdef _WIN32
500 0, NULL);
501#else
502 g_dwNumMessages, g_szMessages);
503#endif
f669df41 504 nxlog_set_console_writer(LogConsoleWriter);
5039dede
AK
505
506 // Set code page
507#ifndef _WIN32
508 if (SetDefaultCodepage(g_szCodePage))
509 {
35f836fe 510 DbgPrintf(1, _T("Code page set to %s"), g_szCodePage);
5039dede
AK
511 }
512 else
513 {
514 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "s", g_szCodePage);
515 }
516#endif
517
73869331
VK
518 // Set process affinity mask
519 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
520 {
521#ifdef _WIN32
522 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
523 DbgPrintf(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
524#else
525 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
526#endif
527 }
528
5039dede
AK
529#ifdef _WIN32
530 WSADATA wsaData;
1ddf3f0c
VK
531 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
532 if (wrc != 0)
533 {
534 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
535 return FALSE;
536 }
5039dede
AK
537#endif
538
539 InitLocalNetInfo();
540
541 // Create queue for delayed SQL queries
73869331 542 g_pLazyRequestQueue = new Queue(256, 64);
5039dede
AK
543
544 // Initialize database driver and connect to database
9d88cdc9 545 DBSetDebugPrintCallback(DbgPrintf2);
b8c1ec69
VK
546 if (!DBInit(MSG_OTHER, (g_dwFlags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
547 return FALSE;
548 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (g_nDebugLevel >= 9), DBEventHandler, NULL);
549 if (g_dbDriver == NULL)
5039dede
AK
550 return FALSE;
551
552 // Connect to database
465b3f2d 553 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
5039dede
AK
554 for(i = 0; ; i++)
555 {
465b3f2d 556 g_hCoreDB = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, errorText);
5039dede
AK
557 if ((g_hCoreDB != NULL) || (i == 5))
558 break;
559 ThreadSleep(5);
560 }
561 if (g_hCoreDB == NULL)
562 {
465b3f2d 563 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
5039dede
AK
564 return FALSE;
565 }
35f836fe 566 DbgPrintf(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
5039dede
AK
567
568 // Check database version
28f5b9a4 569 iDBVersion = DBGetSchemaVersion(g_hCoreDB);
5039dede
AK
570 if (iDBVersion != DB_FORMAT_VERSION)
571 {
572 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
573 return FALSE;
574 }
575
35f836fe
VK
576 int baseSize = ConfigReadInt(_T("ConnectionPoolBaseSize"), 5);
577 int maxSize = ConfigReadInt(_T("ConnectionPoolMaxSize"), 20);
578 int cooldownTime = ConfigReadInt(_T("ConnectionPoolCooldownTime"), 300);
465b3f2d 579 DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, baseSize, maxSize, cooldownTime, g_hCoreDB);
f727d089 580
5039dede 581 // Read database syntax
28f5b9a4 582 g_nDBSyntax = DBGetSyntax(g_hCoreDB);
5039dede
AK
583
584 // Read server ID
35f836fe 585 ConfigReadStr(_T("ServerID"), szInfo, 256, _T(""));
5039dede
AK
586 StrStrip(szInfo);
587 if (szInfo[0] != 0)
588 {
589 StrToBin(szInfo, (BYTE *)&g_qwServerId, sizeof(QWORD));
590 }
591 else
592 {
593 // Generate new ID
594 g_qwServerId = (((QWORD)time(NULL)) << 32) | rand();
595 BinToStr((BYTE *)&g_qwServerId, sizeof(QWORD), szInfo);
35f836fe 596 ConfigWriteStr(_T("ServerID"), szInfo, TRUE);
5039dede
AK
597 }
598
599 // Initialize locks
600retry_db_lock:
601 if (!InitLocks(&dwAddr, szInfo))
602 {
603 if (dwAddr == UNLOCKED) // Some SQL problems
604 {
605 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
606 }
607 else // Database already locked by another server instance
608 {
609 // Check for lock from crashed/terminated local process
610 if (dwAddr == GetLocalIpAddr())
611 {
612 DWORD dwPID;
613
35f836fe 614 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
5039dede
AK
615 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
616 {
617 UnlockDB();
618 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
619 goto retry_db_lock;
620 }
621 }
622 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
623 }
624 return FALSE;
625 }
626 g_dwFlags |= AF_DB_LOCKED;
627
628 // Load global configuration parameters
629 LoadGlobalConfig();
35f836fe 630 DbgPrintf(1, _T("Global configuration loaded"));
5039dede
AK
631
632 // Check data directory
633 if (!CheckDataDir())
634 return FALSE;
635
636 // Initialize cryptografy
637 if (!InitCryptografy())
638 {
639 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
640 return FALSE;
641 }
642
643 // Initialize certificate store and CA
644 InitCertificates();
645
5039dede
AK
646 // Create synchronization stuff
647 m_condShutdown = ConditionCreate(TRUE);
648
649 // Setup unique identifiers table
650 if (!InitIdTable())
651 return FALSE;
35f836fe 652 DbgPrintf(2, _T("ID table created"));
5039dede
AK
653
654 // Load and compile scripts
655 LoadScripts();
656
657 // Initialize mailer and SMS sender
658 InitMailer();
659 InitSMSSender();
660
661 // Load users from database
ab185583 662 InitUsers();
5039dede
AK
663 if (!LoadUsers())
664 {
665 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
666 return FALSE;
667 }
35f836fe 668 DbgPrintf(2, _T("User accounts loaded"));
5039dede
AK
669
670 // Initialize audit
671 InitAuditLog();
672
673 // Initialize objects infrastructure and load objects from database
9796ce45 674 LoadNetworkDeviceDrivers();
5039dede
AK
675 ObjectsInit();
676 if (!LoadObjects())
677 return FALSE;
678 LoadMaps();
35f836fe 679 DbgPrintf(1, _T("Objects loaded and initialized"));
5039dede
AK
680
681 // Initialize situations
682 if (!SituationsInit())
683 return FALSE;
35f836fe 684 DbgPrintf(1, _T("Situations loaded and initialized"));
5039dede
AK
685
686 // Initialize and load event actions
687 if (!InitActions())
688 {
689 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
690 return FALSE;
691 }
692
693 // Initialize event handling subsystem
694 if (!InitEventSubsystem())
695 return FALSE;
696
697 // Initialize alarms
698 if (!g_alarmMgr.Init())
699 return FALSE;
700
701 // Initialize data collection subsystem
702 if (!InitDataCollector())
703 return FALSE;
704
e05b1945
VK
705 InitLogAccess();
706
5039dede
AK
707 // Initialize watchdog
708 WatchdogInit();
709
710 // Check if management node object presented in database
711 CheckForMgmtNode();
712 if (g_dwMgmtNode == 0)
713 {
714 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
715 return FALSE;
716 }
717
718 // Start threads
719 ThreadCreate(WatchdogThread, 0, NULL);
720 ThreadCreate(NodePoller, 0, NULL);
3929b1ca 721 ThreadCreate(JobManagerThread, 0, NULL);
5039dede
AK
722 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
723 m_thHouseKeeper = ThreadCreateEx(HouseKeeper, 0, NULL);
724 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
725
726 // Start event processor
727 ThreadCreate(EventProcessor, 0, NULL);
728
729 // Start SNMP trapper
730 InitTraps();
35f836fe 731 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
5039dede
AK
732 ThreadCreate(SNMPTrapReceiver, 0, NULL);
733
734 // Start built-in syslog daemon
35f836fe 735 if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
5039dede
AK
736 m_thSyslogDaemon = ThreadCreateEx(SyslogDaemon, 0, NULL);
737
35f836fe 738 // Start database _T("lazy") write thread
5039dede
AK
739 StartDBWriter();
740
741 // Start local administartive interface listener if required
35f836fe 742 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
5039dede
AK
743 ThreadCreate(LocalAdminListener, 0, NULL);
744
745 // Load modules
746 LoadNetXMSModules();
747
748 // Start beacon host poller
749 ThreadCreate(BeaconPoller, 0, NULL);
750
751 // Start inter-server communication listener
35f836fe 752 if (ConfigReadInt(_T("EnableISCListener"), 0))
5039dede
AK
753 ThreadCreate(ISCListener, 0, NULL);
754
755 // Allow clients to connect
756 ThreadCreate(ClientListener, 0, NULL);
757
758 g_dwFlags |= AF_SERVER_INITIALIZED;
35f836fe 759 DbgPrintf(1, _T("Server initialization completed"));
5039dede
AK
760 return TRUE;
761}
762
763
764//
765// Server shutdown
766//
767
035a4d73 768void NXCORE_EXPORTABLE Shutdown()
5039dede
AK
769{
770 DWORD i, dwNumThreads;
771
772 // Notify clients
773 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
774
775 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
776 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
777 ConditionSet(m_condShutdown);
778
779#ifndef _WIN32
780 if (IsStandalone() && (m_nShutdownReason != SHUTDOWN_BY_SIGNAL))
781 {
782 pthread_kill(m_signalHandlerThread, SIGUSR1); // Terminate signal handler
783 }
784#endif
785
786 // Stop event processor(s)
787 g_pEventQueue->Clear();
35f836fe 788 dwNumThreads = ConfigReadInt(_T("NumberOfEventProcessors"), 1);
5039dede
AK
789 for(i = 0; i < dwNumThreads; i++)
790 g_pEventQueue->Put(INVALID_POINTER_VALUE);
791
792 ShutdownMailer();
793 ShutdownSMSSender();
794
795 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
35f836fe 796 DbgPrintf(2, _T("All threads was notified, continue with shutdown"));
5039dede
AK
797
798 // Wait for critical threads
799 ThreadJoin(m_thHouseKeeper);
800 ThreadJoin(m_thPollManager);
801 ThreadJoin(m_thSyncer);
802 ThreadJoin(m_thSyslogDaemon);
803
804 SaveObjects(g_hCoreDB);
35f836fe 805 DbgPrintf(2, _T("All objects saved to database"));
5039dede 806 SaveUsers(g_hCoreDB);
35f836fe 807 DbgPrintf(2, _T("All users saved to database"));
5039dede 808 StopDBWriter();
35f836fe 809 DbgPrintf(1, _T("Database writer stopped"));
5039dede 810
ab185583
VK
811 CleanupUsers();
812
5039dede
AK
813 // Remove database lock
814 UnlockDB();
815
816 // Disconnect from database and unload driver
817 if (g_hCoreDB != NULL)
818 DBDisconnect(g_hCoreDB);
c20b2798
AK
819
820 DBConnectionPoolShutdown();
821
b8c1ec69 822 DBUnloadDriver(g_dbDriver);
35f836fe 823 DbgPrintf(1, _T("Database driver unloaded"));
5039dede
AK
824
825 CleanupActions();
826 ShutdownEventSubsystem();
35f836fe 827 DbgPrintf(1, _T("Event processing stopped"));
5039dede
AK
828
829 // Delete all objects
830 //for(i = 0; i < g_dwIdIndexSize; i++)
831 // delete g_pIndexById[i].pObject;
832
833 delete g_pScriptLibrary;
834
835 nxlog_close();
836
837 // Remove PID file
838#ifndef _WIN32
839 remove(g_szPIDFile);
840#endif
841
842 // Terminate process
843#ifdef _WIN32
844 if (!(g_dwFlags & AF_DAEMON))
845 ExitProcess(0);
846#else
847 exit(0);
848#endif
849}
850
851
852//
853// Fast server shutdown - normally called only by Windows service on system shutdown
854//
855
f669df41 856void NXCORE_EXPORTABLE FastShutdown()
5039dede
AK
857{
858 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
859 ConditionSet(m_condShutdown);
860
861 SaveObjects(g_hCoreDB);
35f836fe 862 DbgPrintf(2, _T("All objects saved to database"));
5039dede 863 SaveUsers(g_hCoreDB);
35f836fe 864 DbgPrintf(2, _T("All users saved to database"));
5039dede
AK
865
866 // Remove database lock first, because we have a chance to loose DB connection
867 UnlockDB();
868
869 // Stop database writers
870 StopDBWriter();
35f836fe 871 DbgPrintf(1, _T("Database writer stopped"));
5039dede
AK
872
873 nxlog_close();
874}
875
876
877//
878// Compare given string to command template with abbreviation possibility
879//
880
35f836fe 881static BOOL IsCommand(const TCHAR *pszTemplate, TCHAR *pszString, int iMinChars)
5039dede
AK
882{
883 int i;
884
885 // Convert given string to uppercase
35f836fe 886 _tcsupr(pszString);
5039dede
AK
887
888 for(i = 0; pszString[i] != 0; i++)
889 if (pszString[i] != pszTemplate[i])
890 return FALSE;
891 if (i < iMinChars)
892 return FALSE;
893 return TRUE;
894}
895
896
897//
898// Dump index
899//
900
35f836fe 901static void DumpIndex(CONSOLE_CTX pCtx, RWLOCK hLock, INDEX *pIndex, DWORD dwSize, BOOL bIndexByIp)
5039dede
AK
902{
903 DWORD i;
35f836fe 904 TCHAR szIpAddr[16];
5039dede
AK
905
906 if (!RWLockReadLock(hLock, 5000))
907 {
35f836fe 908 ConsolePrintf(pCtx, _T("ERROR: unable to obtain index lock in 5 seconds\n"));
5039dede
AK
909 return;
910 }
911
912 for(i = 0; i < dwSize; i++)
913 {
914 if (bIndexByIp)
915 {
35f836fe 916 ConsolePrintf(pCtx, _T("%08X [%-15s] %p %s\n"), pIndex[i].dwKey,
5039dede
AK
917 IpToStr(pIndex[i].dwKey, szIpAddr),
918 pIndex[i].pObject, ((NetObj *)pIndex[i].pObject)->Name());
919 }
920 else
921 {
35f836fe 922 ConsolePrintf(pCtx, _T("%08X %p %s\n"), pIndex[i].dwKey, pIndex[i].pObject,
5039dede
AK
923 ((NetObj *)pIndex[i].pObject)->Name());
924 }
925 }
926
927 RWLockUnlock(hLock);
928}
929
930
931//
932// Process command entered from command line in standalone mode
35f836fe 933// Return TRUE if command was _T("down")
5039dede
AK
934//
935
35f836fe 936int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
5039dede 937{
35f836fe 938 const TCHAR *pArg;
48c0079d 939 TCHAR szBuffer[256], *eptr;
5039dede
AK
940 int nExitCode = CMD_EXIT_CONTINUE;
941
942 // Get command
943 pArg = ExtractWord(pszCmdLine, szBuffer);
944
35f836fe 945 if (IsCommand(_T("DEBUG"), szBuffer, 2))
5039dede
AK
946 {
947 // Get argument
948 pArg = ExtractWord(pArg, szBuffer);
48c0079d
VK
949 int level = (int)_tcstol(szBuffer, &eptr, 0);
950 if ((*eptr == 0) && (level >= 0) && (level <= 9))
5039dede 951 {
48c0079d
VK
952 g_nDebugLevel = level;
953 ConsolePrintf(pCtx, (level == 0) ? _T("Debug mode turned off\n") : _T("Debug level set to %d\n"), level);
5039dede 954 }
35f836fe 955 else if (IsCommand(_T("OFF"), szBuffer, 2))
5039dede
AK
956 {
957 g_nDebugLevel = 0;
35f836fe 958 ConsolePrintf(pCtx, _T("Debug mode turned off\n"));
5039dede
AK
959 }
960 else
961 {
962 if (szBuffer[0] == 0)
35f836fe 963 ConsolePrintf(pCtx, _T("ERROR: Missing argument\n\n"));
5039dede 964 else
48c0079d 965 ConsolePrintf(pCtx, _T("ERROR: Invalid debug level\n\n"));
5039dede
AK
966 }
967 }
35f836fe 968 else if (IsCommand(_T("DOWN"), szBuffer, 4))
5039dede 969 {
35f836fe 970 ConsolePrintf(pCtx, _T("Proceeding with server shutdown...\n"));
5039dede
AK
971 nExitCode = CMD_EXIT_SHUTDOWN;
972 }
35f836fe 973 else if (IsCommand(_T("DUMP"), szBuffer, 4))
5039dede
AK
974 {
975 DumpProcess(pCtx);
976 }
35f836fe 977 else if (IsCommand(_T("RAISE"), szBuffer, 5))
5039dede
AK
978 {
979 // Get argument
980 pArg = ExtractWord(pArg, szBuffer);
981
35f836fe 982 if (IsCommand(_T("ACCESS"), szBuffer, 6))
5039dede 983 {
35f836fe 984 ConsolePrintf(pCtx, _T("Raising exception...\n"));
5039dede
AK
985 char *p = NULL;
986 *p = 0;
987 }
35f836fe 988 else if (IsCommand(_T("BREAKPOINT"), szBuffer, 5))
5039dede
AK
989 {
990#ifdef _WIN32
35f836fe 991 ConsolePrintf(pCtx, _T("Raising exception...\n"));
5039dede
AK
992 RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
993#else
35f836fe 994 ConsolePrintf(pCtx, _T("ERROR: Not supported on current platform\n"));
5039dede
AK
995#endif
996 }
997 else
998 {
35f836fe 999 ConsolePrintf(pCtx, _T("Invalid exception name; possible names are:\nACCESS BREAKPOINT\n"));
5039dede
AK
1000 }
1001 }
35f836fe 1002 else if (IsCommand(_T("EXIT"), szBuffer, 4))
5039dede 1003 {
f669df41 1004 if ((pCtx->hSocket != -1) || (pCtx->session != NULL))
5039dede 1005 {
35f836fe 1006 ConsolePrintf(pCtx, _T("Closing session...\n"));
5039dede
AK
1007 nExitCode = CMD_EXIT_CLOSE_SESSION;
1008 }
1009 else
1010 {
35f836fe 1011 ConsolePrintf(pCtx, _T("Cannot exit from local server console\n"));
5039dede
AK
1012 }
1013 }
35f836fe 1014 else if (IsCommand(_T("SHOW"), szBuffer, 2))
5039dede
AK
1015 {
1016 // Get argument
1017 pArg = ExtractWord(pArg, szBuffer);
1018
35f836fe 1019 if (IsCommand(_T("FLAGS"), szBuffer, 1))
5039dede 1020 {
35f836fe 1021 ConsolePrintf(pCtx, _T("Flags: 0x%08X\n"), g_dwFlags);
5039dede
AK
1022 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DAEMON));
1023 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_SYSLOG));
1024 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NETWORK_DISCOVERY));
1025 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ACTIVE_NETWORK_DISCOVERY));
1026 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_LOG_SQL_ERRORS));
1027 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DELETE_EMPTY_SUBNETS));
1028 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_SNMP_TRAPD));
1029 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_ZONING));
5039dede 1030 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYNC_NODE_NAMES_WITH_DNS));
cbc777ee
VK
1031 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CHECK_TRUSTED_NODES));
1032 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_WRITE_FULL_DUMP));
1033 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_NODE_NAMES));
5039dede 1034 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CATCH_EXCEPTIONS));
5039dede
AK
1035 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_INTERNAL_CA));
1036 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_LOCKED));
cbc777ee 1037 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_MULTIPLE_DB_CONN));
5039dede 1038 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_CONNECTION_LOST));
cbc777ee 1039 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_NO_NETWORK_CONNECTIVITY));
5039dede
AK
1040 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_EVENT_STORM_DETECTED));
1041 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
1042 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
35f836fe 1043 ConsolePrintf(pCtx, _T("\n"));
5039dede 1044 }
35f836fe 1045 else if (IsCommand(_T("INDEX"), szBuffer, 1))
5039dede
AK
1046 {
1047 // Get argument
1048 pArg = ExtractWord(pArg, szBuffer);
1049
35f836fe 1050 if (IsCommand(_T("CONDITION"), szBuffer, 1))
5039dede
AK
1051 {
1052 DumpIndex(pCtx, g_rwlockConditionIndex, g_pConditionIndex, g_dwConditionIndexSize, FALSE);
1053 }
35f836fe 1054 else if (IsCommand(_T("ID"), szBuffer, 2))
5039dede
AK
1055 {
1056 DumpIndex(pCtx, g_rwlockIdIndex, g_pIndexById, g_dwIdIndexSize, FALSE);
1057 }
35f836fe 1058 else if (IsCommand(_T("INTERFACE"), szBuffer, 2))
5039dede
AK
1059 {
1060 DumpIndex(pCtx, g_rwlockInterfaceIndex, g_pInterfaceIndexByAddr,
1061 g_dwInterfaceAddrIndexSize, TRUE);
1062 }
35f836fe 1063 else if (IsCommand(_T("SUBNET"), szBuffer, 1))
5039dede
AK
1064 {
1065 DumpIndex(pCtx, g_rwlockSubnetIndex, g_pSubnetIndexByAddr,
1066 g_dwSubnetAddrIndexSize, TRUE);
1067 }
1068 else
1069 {
1070 if (szBuffer[0] == 0)
35f836fe 1071 ConsolePrintf(pCtx, _T("ERROR: Missing index name\n")
889d7ff7 1072 _T("Valid names are: CONDITION, ID, INTERFACE, SUBNET\n\n"));
5039dede 1073 else
35f836fe 1074 ConsolePrintf(pCtx, _T("ERROR: Invalid index name\n\n"));
5039dede
AK
1075 }
1076 }
35f836fe 1077 else if (IsCommand(_T("MEMORY"), szBuffer, 2))
5039dede
AK
1078 {
1079#ifdef NETXMS_MEMORY_DEBUG
1080 PrintMemoryBlocks();
1081#else
35f836fe 1082 ConsolePrintf(pCtx, _T("ERROR: Server was compiled without memory debugger\n\n"));
5039dede
AK
1083#endif
1084 }
35f836fe 1085 else if (IsCommand(_T("MUTEX"), szBuffer, 2))
5039dede 1086 {
35f836fe
VK
1087 ConsolePrintf(pCtx, _T("Mutex status:\n"));
1088 DbgTestRWLock(g_rwlockIdIndex, _T("g_hMutexIdIndex"), pCtx);
35f836fe
VK
1089 DbgTestRWLock(g_rwlockSubnetIndex, _T("g_hMutexSubnetIndex"), pCtx);
1090 DbgTestRWLock(g_rwlockInterfaceIndex, _T("g_hMutexInterfaceIndex"), pCtx);
1091 ConsolePrintf(pCtx, _T("\n"));
5039dede 1092 }
35f836fe 1093 else if (IsCommand(_T("OBJECTS"), szBuffer, 1))
5039dede
AK
1094 {
1095 DumpObjects(pCtx);
1096 }
35f836fe 1097 else if (IsCommand(_T("POLLERS"), szBuffer, 1))
5039dede
AK
1098 {
1099 ShowPollerState(pCtx);
1100 }
35f836fe 1101 else if (IsCommand(_T("QUEUES"), szBuffer, 1))
5039dede 1102 {
35f836fe
VK
1103 ShowQueueStats(pCtx, &g_conditionPollerQueue, _T("Condition poller"));
1104 ShowQueueStats(pCtx, &g_configPollQueue, _T("Configuration poller"));
040c45fa 1105 ShowQueueStats(pCtx, &g_topologyPollQueue, _T("Topology poller"));
35f836fe
VK
1106 ShowQueueStats(pCtx, g_pItemQueue, _T("Data collector"));
1107 ShowQueueStats(pCtx, g_pLazyRequestQueue, _T("Database writer"));
1108 ShowQueueStats(pCtx, g_pEventQueue, _T("Event processor"));
1109 ShowQueueStats(pCtx, &g_discoveryPollQueue, _T("Network discovery poller"));
1110 ShowQueueStats(pCtx, &g_nodePollerQueue, _T("Node poller"));
1111 ShowQueueStats(pCtx, &g_routePollQueue, _T("Routing table poller"));
1112 ShowQueueStats(pCtx, &g_statusPollQueue, _T("Status poller"));
1113 ConsolePrintf(pCtx, _T("\n"));
5039dede 1114 }
35f836fe 1115 else if (IsCommand(_T("ROUTING-TABLE"), szBuffer, 1))
5039dede
AK
1116 {
1117 DWORD dwNode;
1118 NetObj *pObject;
1119
1120 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1121 dwNode = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1122 if (dwNode != 0)
1123 {
1124 pObject = FindObjectById(dwNode);
1125 if (pObject != NULL)
1126 {
1127 if (pObject->Type() == OBJECT_NODE)
1128 {
1129 ROUTING_TABLE *pRT;
35f836fe 1130 TCHAR szIpAddr[16];
5039dede
AK
1131 int i;
1132
35f836fe 1133 ConsolePrintf(pCtx, _T("Routing table for node %s:\n\n"), pObject->Name());
58b3e451 1134 pRT = ((Node *)pObject)->getCachedRoutingTable();
5039dede
AK
1135 if (pRT != NULL)
1136 {
1137 for(i = 0; i < pRT->iNumEntries; i++)
1138 {
35f836fe
VK
1139 _sntprintf(szBuffer, 256, _T("%s/%d"), IpToStr(pRT->pRoutes[i].dwDestAddr, szIpAddr),
1140 BitsInMask(pRT->pRoutes[i].dwDestMask));
1141 ConsolePrintf(pCtx, _T("%-18s %-15s %-6d %d\n"), szBuffer,
1142 IpToStr(pRT->pRoutes[i].dwNextHop, szIpAddr),
1143 pRT->pRoutes[i].dwIfIndex, pRT->pRoutes[i].dwRouteType);
5039dede 1144 }
35f836fe 1145 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1146 }
1147 else
1148 {
35f836fe 1149 ConsolePrintf(pCtx, _T("Node doesn't have cached routing table\n\n"));
5039dede
AK
1150 }
1151 }
1152 else
1153 {
35f836fe 1154 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
5039dede
AK
1155 }
1156 }
1157 else
1158 {
35f836fe 1159 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
5039dede
AK
1160 }
1161 }
1162 else
1163 {
35f836fe 1164 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
5039dede
AK
1165 }
1166 }
35f836fe 1167 else if (IsCommand(_T("SESSIONS"), szBuffer, 2))
5039dede
AK
1168 {
1169 DumpSessions(pCtx);
1170 }
35f836fe 1171 else if (IsCommand(_T("STATS"), szBuffer, 2))
5039dede
AK
1172 {
1173 ShowServerStats(pCtx);
1174 }
35f836fe 1175 else if (IsCommand(_T("USERS"), szBuffer, 1))
5039dede
AK
1176 {
1177 DumpUsers(pCtx);
1178 }
35f836fe 1179 else if (IsCommand(_T("WATCHDOG"), szBuffer, 1))
5039dede
AK
1180 {
1181 WatchdogPrintStatus(pCtx);
35f836fe 1182 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1183 }
1184 else
1185 {
1186 if (szBuffer[0] == 0)
35f836fe 1187 ConsolePrintf(pCtx, _T("ERROR: Missing subcommand\n\n"));
5039dede 1188 else
35f836fe 1189 ConsolePrintf(pCtx, _T("ERROR: Invalid SHOW subcommand\n\n"));
5039dede
AK
1190 }
1191 }
35f836fe 1192 else if (IsCommand(_T("TRACE"), szBuffer, 1))
5039dede
AK
1193 {
1194 DWORD dwNode1, dwNode2;
1195 NetObj *pObject1, *pObject2;
1196 NETWORK_PATH_TRACE *pTrace;
35f836fe 1197 TCHAR szNextHop[16];
5039dede
AK
1198 int i;
1199
1200 // Get arguments
1201 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1202 dwNode1 = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1203
1204 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1205 dwNode2 = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1206
1207 if ((dwNode1 != 0) && (dwNode2 != 0))
1208 {
1209 pObject1 = FindObjectById(dwNode1);
1210 if (pObject1 == NULL)
1211 {
35f836fe 1212 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode1);
5039dede
AK
1213 }
1214 else
1215 {
1216 pObject2 = FindObjectById(dwNode2);
1217 if (pObject2 == NULL)
1218 {
35f836fe 1219 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode2);
5039dede
AK
1220 }
1221 else
1222 {
1223 if ((pObject1->Type() == OBJECT_NODE) &&
1224 (pObject2->Type() == OBJECT_NODE))
1225 {
1226 pTrace = TraceRoute((Node *)pObject1, (Node *)pObject2);
1227 if (pTrace != NULL)
1228 {
35f836fe 1229 ConsolePrintf(pCtx, _T("Trace from %s to %s (%d hops):\n"),
5039dede
AK
1230 pObject1->Name(), pObject2->Name(), pTrace->iNumHops);
1231 for(i = 0; i < pTrace->iNumHops; i++)
35f836fe 1232 ConsolePrintf(pCtx, _T("[%d] %s %s %s %d\n"),
5039dede
AK
1233 pTrace->pHopList[i].pObject->Id(),
1234 pTrace->pHopList[i].pObject->Name(),
1235 IpToStr(pTrace->pHopList[i].dwNextHop, szNextHop),
35f836fe 1236 pTrace->pHopList[i].bIsVPN ? _T("VPN Connector ID:") : _T("Interface Index: "),
5039dede
AK
1237 pTrace->pHopList[i].dwIfIndex);
1238 DestroyTraceData(pTrace);
35f836fe 1239 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1240 }
1241 else
1242 {
35f836fe 1243 ConsolePrintf(pCtx, _T("ERROR: Call to TraceRoute() failed\n\n"));
5039dede
AK
1244 }
1245 }
1246 else
1247 {
35f836fe 1248 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
5039dede
AK
1249 }
1250 }
1251 }
1252 }
1253 else
1254 {
35f836fe 1255 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node id(s)\n\n"));
5039dede
AK
1256 }
1257 }
35f836fe 1258 else if (IsCommand(_T("HELP"), szBuffer, 2) || IsCommand(_T("?"), szBuffer, 1))
5039dede 1259 {
35f836fe 1260 ConsolePrintf(pCtx, _T("Valid commands are:\n")
48c0079d 1261 _T(" debug [<level>|off] - Set debug level (valid range is 0..9)\n")
35f836fe
VK
1262 _T(" down - Down NetXMS server\n")
1263 _T(" exit - Exit from remote session\n")
1264 _T(" help - Display this help\n")
1265 _T(" raise <exception> - Raise exception\n")
1266 _T(" show flags - Show internal server flags\n")
1267 _T(" show index <index> - Show internal index\n")
1268 _T(" show mutex - Display mutex status\n")
1269 _T(" show objects - Dump network objects to screen\n")
1270 _T(" show pollers - Show poller threads state information\n")
1271 _T(" show queues - Show internal queues statistics\n")
1272 _T(" show routing-table <node> - Show cached routing table for node\n")
1273 _T(" show sessions - Show active client sessions\n")
1274 _T(" show stats - Show server statistics\n")
1275 _T(" show users - Show users\n")
1276 _T(" show watchdog - Display watchdog information\n")
1277 _T(" trace <node1> <node2> - Show network path trace between two nodes\n")
1278 _T("\nAlmost all commands can be abbreviated to 2 or 3 characters\n")
1279 _T("\n"));
5039dede
AK
1280 }
1281 else
1282 {
35f836fe 1283 ConsolePrintf(pCtx, _T("UNKNOWN COMMAND\n\n"));
5039dede
AK
1284 }
1285
1286 return nExitCode;
1287}
1288
1289
1290//
1291// Signal handler for UNIX platforms
1292//
1293
1294#ifndef _WIN32
1295
1296void SignalHandlerStub(int nSignal)
1297{
1298 // should be unused, but JIC...
1299 if (nSignal == SIGCHLD)
1300 {
1301 while (waitpid(-1, NULL, WNOHANG) > 0)
1302 ;
1303 }
1304}
1305
1306THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1307{
1308 sigset_t signals;
1309 int nSignal;
1310 BOOL bCallShutdown = FALSE;
1311
1312 m_signalHandlerThread = pthread_self();
1313
1314 // default for SIGCHLD: ignore
1315 signal(SIGCHLD, &SignalHandlerStub);
1316
1317 sigemptyset(&signals);
1318 sigaddset(&signals, SIGTERM);
1319 sigaddset(&signals, SIGINT);
1320 sigaddset(&signals, SIGPIPE);
1321 sigaddset(&signals, SIGSEGV);
1322 sigaddset(&signals, SIGCHLD);
1323 sigaddset(&signals, SIGHUP);
1324 sigaddset(&signals, SIGUSR1);
1325 sigaddset(&signals, SIGUSR2);
1326
1327 sigprocmask(SIG_BLOCK, &signals, NULL);
1328
1329 while(1)
1330 {
1331 if (sigwait(&signals, &nSignal) == 0)
1332 {
1333 switch(nSignal)
1334 {
1335 case SIGTERM:
1336 case SIGINT:
1337 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1338 if (IsStandalone())
1339 bCallShutdown = TRUE;
1340 ConditionSet(m_condShutdown);
1341 goto stop_handler;
1342 case SIGSEGV:
1343 abort();
1344 break;
1345 case SIGCHLD:
1346 while (waitpid(-1, NULL, WNOHANG) > 0)
1347 ;
1348 break;
1349 case SIGUSR1:
1350 if (g_dwFlags & AF_SHUTDOWN)
1351 goto stop_handler;
1352 break;
1353 default:
1354 break;
1355 }
1356 }
1357 else
1358 {
1359 ThreadSleepMs(100);
1360 }
1361 }
1362
1363stop_handler:
1364 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1365 if (bCallShutdown)
1366 Shutdown();
1367 return THREAD_OK;
1368}
1369
1370#endif
1371
1372
1373//
1374// Common main()
1375//
1376
1377THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1378{
1379 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1380
1381 if (IsStandalone())
1382 {
1383 char *ptr, szCommand[256];
1384 struct __console_ctx ctx;
35f836fe
VK
1385#ifdef UNICODE
1386 WCHAR wcCommand[256];
1387#endif
5039dede
AK
1388
1389 ctx.hSocket = -1;
1390 ctx.pMsg = NULL;
200d662d 1391 ctx.session = NULL;
f669df41
VK
1392 WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Ready\n")
1393 _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
1394 _T("System Console\n\n"));
5039dede
AK
1395
1396#if USE_READLINE
1397 // Initialize readline library if we use it
0322eed7 1398 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
5039dede
AK
1399#endif
1400
1401 while(1)
1402 {
1403#if USE_READLINE
f669df41 1404 ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
5039dede 1405#else
f669df41 1406 WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
5039dede
AK
1407 fflush(stdout);
1408 if (fgets(szCommand, 255, stdin) == NULL)
1409 break; // Error reading stdin
1410 ptr = strchr(szCommand, '\n');
1411 if (ptr != NULL)
1412 *ptr = 0;
1413 ptr = szCommand;
1414#endif
1415
1416 if (ptr != NULL)
1417 {
35f836fe
VK
1418#ifdef UNICODE
1419 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
1420 wcCommand[255] = 0;
1421 StrStrip(wcCommand);
1422 if (wcCommand[0] != 0)
1423 {
1424 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
1425#else
5039dede
AK
1426 StrStrip(ptr);
1427 if (*ptr != 0)
1428 {
1429 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
35f836fe 1430#endif
5039dede
AK
1431 break;
1432#if USE_READLINE
1433 add_history(ptr);
1434#endif
1435 }
1436#if USE_READLINE
1437 free(ptr);
1438#endif
1439 }
1440 else
1441 {
1442 printf("\n");
1443 }
1444 }
1445
1446#if USE_READLINE
1447 free(ptr);
1448#endif
1449 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1450 Shutdown();
1451 }
1452 else
1453 {
1454 ConditionWait(m_condShutdown, INFINITE);
1455 // On Win32, Shutdown() will be called by service control handler
1456#ifndef _WIN32
1457 Shutdown();
1458#endif
1459 }
1460 return THREAD_OK;
1461}
1462
1463
1464//
1465// Initiate server shutdown
1466//
1467
035a4d73 1468void InitiateShutdown()
5039dede
AK
1469{
1470#ifdef _WIN32
1471 Shutdown();
1472#else
1473 if (IsStandalone())
1474 {
1475 Shutdown();
1476 }
1477 else
1478 {
1479 pthread_kill(m_signalHandlerThread, SIGTERM);
1480 }
1481#endif
1482}
1483
1484
1485//
1486// DLL Entry point
1487//
1488
1489#ifdef _WIN32
1490
1491BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1492{
1493 if (dwReason == DLL_PROCESS_ATTACH)
1494 DisableThreadLibraryCalls(hInstance);
1495 return TRUE;
1496}
1497
1498#endif