temporary added to source tree
[public/netxms.git] / src / server / core / main.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
1ddf3f0c 3** Copyright (C) 2003-2010 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("");
28f5b9a4 122int g_nDBSyntax = DB_SYNTAX_UNKNOWN;
73869331 123DWORD NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
5039dede
AK
124QWORD g_qwServerId;
125RSA *g_pServerKey = NULL;
126time_t g_tServerStartTime = 0;
127DWORD g_dwLockTimeout = 60000; // Default timeout for acquiring mutex
128DWORD g_dwSNMPTimeout = 2000; // Default timeout for SNMP requests
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//
447// Server initialization
448//
449
bc980b27 450BOOL NXCORE_EXPORTABLE Initialize()
5039dede
AK
451{
452 int i, iDBVersion;
453 DWORD dwAddr;
35f836fe 454 TCHAR szInfo[256];
5039dede
AK
455
456 g_tServerStartTime = time(NULL);
457 srand((unsigned int)g_tServerStartTime);
458 nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
459 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
460 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
461 _T("LIBNXSRV.DLL"),
462#ifdef _WIN32
463 0, NULL);
464#else
465 g_dwNumMessages, g_szMessages);
466#endif
467
468 // Set code page
469#ifndef _WIN32
470 if (SetDefaultCodepage(g_szCodePage))
471 {
35f836fe 472 DbgPrintf(1, _T("Code page set to %s"), g_szCodePage);
5039dede
AK
473 }
474 else
475 {
476 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "s", g_szCodePage);
477 }
478#endif
479
73869331
VK
480 // Set process affinity mask
481 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
482 {
483#ifdef _WIN32
484 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
485 DbgPrintf(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
486#else
487 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
488#endif
489 }
490
5039dede
AK
491#ifdef _WIN32
492 WSADATA wsaData;
1ddf3f0c
VK
493 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
494 if (wrc != 0)
495 {
496 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
497 return FALSE;
498 }
5039dede
AK
499#endif
500
501 InitLocalNetInfo();
502
503 // Create queue for delayed SQL queries
73869331 504 g_pLazyRequestQueue = new Queue(256, 64);
5039dede
AK
505
506 // Initialize database driver and connect to database
9d88cdc9 507 DBSetDebugPrintCallback(DbgPrintf2);
b8c1ec69
VK
508 if (!DBInit(MSG_OTHER, (g_dwFlags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
509 return FALSE;
510 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (g_nDebugLevel >= 9), DBEventHandler, NULL);
511 if (g_dbDriver == NULL)
5039dede
AK
512 return FALSE;
513
514 // Connect to database
465b3f2d 515 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
5039dede
AK
516 for(i = 0; ; i++)
517 {
465b3f2d 518 g_hCoreDB = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, errorText);
5039dede
AK
519 if ((g_hCoreDB != NULL) || (i == 5))
520 break;
521 ThreadSleep(5);
522 }
523 if (g_hCoreDB == NULL)
524 {
465b3f2d 525 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
5039dede
AK
526 return FALSE;
527 }
35f836fe 528 DbgPrintf(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
5039dede
AK
529
530 // Check database version
28f5b9a4 531 iDBVersion = DBGetSchemaVersion(g_hCoreDB);
5039dede
AK
532 if (iDBVersion != DB_FORMAT_VERSION)
533 {
534 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
535 return FALSE;
536 }
537
35f836fe
VK
538 int baseSize = ConfigReadInt(_T("ConnectionPoolBaseSize"), 5);
539 int maxSize = ConfigReadInt(_T("ConnectionPoolMaxSize"), 20);
540 int cooldownTime = ConfigReadInt(_T("ConnectionPoolCooldownTime"), 300);
465b3f2d 541 DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, baseSize, maxSize, cooldownTime, g_hCoreDB);
f727d089 542
5039dede 543 // Read database syntax
28f5b9a4 544 g_nDBSyntax = DBGetSyntax(g_hCoreDB);
5039dede
AK
545
546 // Read server ID
35f836fe 547 ConfigReadStr(_T("ServerID"), szInfo, 256, _T(""));
5039dede
AK
548 StrStrip(szInfo);
549 if (szInfo[0] != 0)
550 {
551 StrToBin(szInfo, (BYTE *)&g_qwServerId, sizeof(QWORD));
552 }
553 else
554 {
555 // Generate new ID
556 g_qwServerId = (((QWORD)time(NULL)) << 32) | rand();
557 BinToStr((BYTE *)&g_qwServerId, sizeof(QWORD), szInfo);
35f836fe 558 ConfigWriteStr(_T("ServerID"), szInfo, TRUE);
5039dede
AK
559 }
560
561 // Initialize locks
562retry_db_lock:
563 if (!InitLocks(&dwAddr, szInfo))
564 {
565 if (dwAddr == UNLOCKED) // Some SQL problems
566 {
567 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
568 }
569 else // Database already locked by another server instance
570 {
571 // Check for lock from crashed/terminated local process
572 if (dwAddr == GetLocalIpAddr())
573 {
574 DWORD dwPID;
575
35f836fe 576 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
5039dede
AK
577 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
578 {
579 UnlockDB();
580 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
581 goto retry_db_lock;
582 }
583 }
584 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
585 }
586 return FALSE;
587 }
588 g_dwFlags |= AF_DB_LOCKED;
589
590 // Load global configuration parameters
591 LoadGlobalConfig();
35f836fe 592 DbgPrintf(1, _T("Global configuration loaded"));
5039dede
AK
593
594 // Check data directory
595 if (!CheckDataDir())
596 return FALSE;
597
598 // Initialize cryptografy
599 if (!InitCryptografy())
600 {
601 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
602 return FALSE;
603 }
604
605 // Initialize certificate store and CA
606 InitCertificates();
607
608 // Initialize SNMP stuff
609 SnmpInit();
610
5039dede
AK
611 // Create synchronization stuff
612 m_condShutdown = ConditionCreate(TRUE);
613
614 // Setup unique identifiers table
615 if (!InitIdTable())
616 return FALSE;
35f836fe 617 DbgPrintf(2, _T("ID table created"));
5039dede
AK
618
619 // Load and compile scripts
620 LoadScripts();
621
622 // Initialize mailer and SMS sender
623 InitMailer();
624 InitSMSSender();
625
626 // Load users from database
ab185583 627 InitUsers();
5039dede
AK
628 if (!LoadUsers())
629 {
630 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
631 return FALSE;
632 }
35f836fe 633 DbgPrintf(2, _T("User accounts loaded"));
5039dede
AK
634
635 // Initialize audit
636 InitAuditLog();
637
638 // Initialize objects infrastructure and load objects from database
639 ObjectsInit();
640 if (!LoadObjects())
641 return FALSE;
642 LoadMaps();
35f836fe 643 DbgPrintf(1, _T("Objects loaded and initialized"));
5039dede
AK
644
645 // Initialize situations
646 if (!SituationsInit())
647 return FALSE;
35f836fe 648 DbgPrintf(1, _T("Situations loaded and initialized"));
5039dede
AK
649
650 // Initialize and load event actions
651 if (!InitActions())
652 {
653 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
654 return FALSE;
655 }
656
657 // Initialize event handling subsystem
658 if (!InitEventSubsystem())
659 return FALSE;
660
661 // Initialize alarms
662 if (!g_alarmMgr.Init())
663 return FALSE;
664
665 // Initialize data collection subsystem
666 if (!InitDataCollector())
667 return FALSE;
668
e05b1945
VK
669 InitLogAccess();
670
5039dede
AK
671 // Initialize watchdog
672 WatchdogInit();
673
674 // Check if management node object presented in database
675 CheckForMgmtNode();
676 if (g_dwMgmtNode == 0)
677 {
678 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
679 return FALSE;
680 }
681
682 // Start threads
683 ThreadCreate(WatchdogThread, 0, NULL);
684 ThreadCreate(NodePoller, 0, NULL);
3929b1ca 685 ThreadCreate(JobManagerThread, 0, NULL);
5039dede
AK
686 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
687 m_thHouseKeeper = ThreadCreateEx(HouseKeeper, 0, NULL);
688 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
689
690 // Start event processor
691 ThreadCreate(EventProcessor, 0, NULL);
692
693 // Start SNMP trapper
694 InitTraps();
35f836fe 695 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
5039dede
AK
696 ThreadCreate(SNMPTrapReceiver, 0, NULL);
697
698 // Start built-in syslog daemon
35f836fe 699 if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
5039dede
AK
700 m_thSyslogDaemon = ThreadCreateEx(SyslogDaemon, 0, NULL);
701
35f836fe 702 // Start database _T("lazy") write thread
5039dede
AK
703 StartDBWriter();
704
705 // Start local administartive interface listener if required
35f836fe 706 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
5039dede
AK
707 ThreadCreate(LocalAdminListener, 0, NULL);
708
709 // Load modules
710 LoadNetXMSModules();
711
712 // Start beacon host poller
713 ThreadCreate(BeaconPoller, 0, NULL);
714
715 // Start inter-server communication listener
35f836fe 716 if (ConfigReadInt(_T("EnableISCListener"), 0))
5039dede
AK
717 ThreadCreate(ISCListener, 0, NULL);
718
719 // Allow clients to connect
720 ThreadCreate(ClientListener, 0, NULL);
721
722 g_dwFlags |= AF_SERVER_INITIALIZED;
35f836fe 723 DbgPrintf(1, _T("Server initialization completed"));
5039dede
AK
724 return TRUE;
725}
726
727
728//
729// Server shutdown
730//
731
035a4d73 732void NXCORE_EXPORTABLE Shutdown()
5039dede
AK
733{
734 DWORD i, dwNumThreads;
735
736 // Notify clients
737 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
738
739 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
740 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
741 ConditionSet(m_condShutdown);
742
743#ifndef _WIN32
744 if (IsStandalone() && (m_nShutdownReason != SHUTDOWN_BY_SIGNAL))
745 {
746 pthread_kill(m_signalHandlerThread, SIGUSR1); // Terminate signal handler
747 }
748#endif
749
750 // Stop event processor(s)
751 g_pEventQueue->Clear();
35f836fe 752 dwNumThreads = ConfigReadInt(_T("NumberOfEventProcessors"), 1);
5039dede
AK
753 for(i = 0; i < dwNumThreads; i++)
754 g_pEventQueue->Put(INVALID_POINTER_VALUE);
755
756 ShutdownMailer();
757 ShutdownSMSSender();
758
759 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
35f836fe 760 DbgPrintf(2, _T("All threads was notified, continue with shutdown"));
5039dede
AK
761
762 // Wait for critical threads
763 ThreadJoin(m_thHouseKeeper);
764 ThreadJoin(m_thPollManager);
765 ThreadJoin(m_thSyncer);
766 ThreadJoin(m_thSyslogDaemon);
767
768 SaveObjects(g_hCoreDB);
35f836fe 769 DbgPrintf(2, _T("All objects saved to database"));
5039dede 770 SaveUsers(g_hCoreDB);
35f836fe 771 DbgPrintf(2, _T("All users saved to database"));
5039dede 772 StopDBWriter();
35f836fe 773 DbgPrintf(1, _T("Database writer stopped"));
5039dede 774
ab185583
VK
775 CleanupUsers();
776
5039dede
AK
777 // Remove database lock
778 UnlockDB();
779
780 // Disconnect from database and unload driver
781 if (g_hCoreDB != NULL)
782 DBDisconnect(g_hCoreDB);
c20b2798
AK
783
784 DBConnectionPoolShutdown();
785
b8c1ec69 786 DBUnloadDriver(g_dbDriver);
35f836fe 787 DbgPrintf(1, _T("Database driver unloaded"));
5039dede
AK
788
789 CleanupActions();
790 ShutdownEventSubsystem();
35f836fe 791 DbgPrintf(1, _T("Event processing stopped"));
5039dede
AK
792
793 // Delete all objects
794 //for(i = 0; i < g_dwIdIndexSize; i++)
795 // delete g_pIndexById[i].pObject;
796
797 delete g_pScriptLibrary;
798
799 nxlog_close();
800
801 // Remove PID file
802#ifndef _WIN32
803 remove(g_szPIDFile);
804#endif
805
806 // Terminate process
807#ifdef _WIN32
808 if (!(g_dwFlags & AF_DAEMON))
809 ExitProcess(0);
810#else
811 exit(0);
812#endif
813}
814
815
816//
817// Fast server shutdown - normally called only by Windows service on system shutdown
818//
819
820void NXCORE_EXPORTABLE FastShutdown(void)
821{
822 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
823 ConditionSet(m_condShutdown);
824
825 SaveObjects(g_hCoreDB);
35f836fe 826 DbgPrintf(2, _T("All objects saved to database"));
5039dede 827 SaveUsers(g_hCoreDB);
35f836fe 828 DbgPrintf(2, _T("All users saved to database"));
5039dede
AK
829
830 // Remove database lock first, because we have a chance to loose DB connection
831 UnlockDB();
832
833 // Stop database writers
834 StopDBWriter();
35f836fe 835 DbgPrintf(1, _T("Database writer stopped"));
5039dede
AK
836
837 nxlog_close();
838}
839
840
841//
842// Compare given string to command template with abbreviation possibility
843//
844
35f836fe 845static BOOL IsCommand(const TCHAR *pszTemplate, TCHAR *pszString, int iMinChars)
5039dede
AK
846{
847 int i;
848
849 // Convert given string to uppercase
35f836fe 850 _tcsupr(pszString);
5039dede
AK
851
852 for(i = 0; pszString[i] != 0; i++)
853 if (pszString[i] != pszTemplate[i])
854 return FALSE;
855 if (i < iMinChars)
856 return FALSE;
857 return TRUE;
858}
859
860
861//
862// Dump index
863//
864
35f836fe 865static void DumpIndex(CONSOLE_CTX pCtx, RWLOCK hLock, INDEX *pIndex, DWORD dwSize, BOOL bIndexByIp)
5039dede
AK
866{
867 DWORD i;
35f836fe 868 TCHAR szIpAddr[16];
5039dede
AK
869
870 if (!RWLockReadLock(hLock, 5000))
871 {
35f836fe 872 ConsolePrintf(pCtx, _T("ERROR: unable to obtain index lock in 5 seconds\n"));
5039dede
AK
873 return;
874 }
875
876 for(i = 0; i < dwSize; i++)
877 {
878 if (bIndexByIp)
879 {
35f836fe 880 ConsolePrintf(pCtx, _T("%08X [%-15s] %p %s\n"), pIndex[i].dwKey,
5039dede
AK
881 IpToStr(pIndex[i].dwKey, szIpAddr),
882 pIndex[i].pObject, ((NetObj *)pIndex[i].pObject)->Name());
883 }
884 else
885 {
35f836fe 886 ConsolePrintf(pCtx, _T("%08X %p %s\n"), pIndex[i].dwKey, pIndex[i].pObject,
5039dede
AK
887 ((NetObj *)pIndex[i].pObject)->Name());
888 }
889 }
890
891 RWLockUnlock(hLock);
892}
893
894
895//
896// Process command entered from command line in standalone mode
35f836fe 897// Return TRUE if command was _T("down")
5039dede
AK
898//
899
35f836fe 900int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
5039dede 901{
35f836fe
VK
902 const TCHAR *pArg;
903 TCHAR szBuffer[256];
5039dede
AK
904 int nExitCode = CMD_EXIT_CONTINUE;
905
906 // Get command
907 pArg = ExtractWord(pszCmdLine, szBuffer);
908
35f836fe 909 if (IsCommand(_T("DEBUG"), szBuffer, 2))
5039dede
AK
910 {
911 // Get argument
912 pArg = ExtractWord(pArg, szBuffer);
913
35f836fe 914 if (IsCommand(_T("ON"), szBuffer, 2))
5039dede
AK
915 {
916 g_nDebugLevel = 8;
35f836fe 917 ConsolePrintf(pCtx, _T("Debug mode turned on\n"));
5039dede 918 }
35f836fe 919 else if (IsCommand(_T("OFF"), szBuffer, 2))
5039dede
AK
920 {
921 g_nDebugLevel = 0;
35f836fe 922 ConsolePrintf(pCtx, _T("Debug mode turned off\n"));
5039dede
AK
923 }
924 else
925 {
926 if (szBuffer[0] == 0)
35f836fe 927 ConsolePrintf(pCtx, _T("ERROR: Missing argument\n\n"));
5039dede 928 else
35f836fe 929 ConsolePrintf(pCtx, _T("ERROR: Invalid DEBUG argument\n\n"));
5039dede
AK
930 }
931 }
35f836fe 932 else if (IsCommand(_T("DOWN"), szBuffer, 4))
5039dede 933 {
35f836fe 934 ConsolePrintf(pCtx, _T("Proceeding with server shutdown...\n"));
5039dede
AK
935 nExitCode = CMD_EXIT_SHUTDOWN;
936 }
35f836fe 937 else if (IsCommand(_T("DUMP"), szBuffer, 4))
5039dede
AK
938 {
939 DumpProcess(pCtx);
940 }
35f836fe 941 else if (IsCommand(_T("RAISE"), szBuffer, 5))
5039dede
AK
942 {
943 // Get argument
944 pArg = ExtractWord(pArg, szBuffer);
945
35f836fe 946 if (IsCommand(_T("ACCESS"), szBuffer, 6))
5039dede 947 {
35f836fe 948 ConsolePrintf(pCtx, _T("Raising exception...\n"));
5039dede
AK
949 char *p = NULL;
950 *p = 0;
951 }
35f836fe 952 else if (IsCommand(_T("BREAKPOINT"), szBuffer, 5))
5039dede
AK
953 {
954#ifdef _WIN32
35f836fe 955 ConsolePrintf(pCtx, _T("Raising exception...\n"));
5039dede
AK
956 RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
957#else
35f836fe 958 ConsolePrintf(pCtx, _T("ERROR: Not supported on current platform\n"));
5039dede
AK
959#endif
960 }
961 else
962 {
35f836fe 963 ConsolePrintf(pCtx, _T("Invalid exception name; possible names are:\nACCESS BREAKPOINT\n"));
5039dede
AK
964 }
965 }
35f836fe 966 else if (IsCommand(_T("EXIT"), szBuffer, 4))
5039dede
AK
967 {
968 if (pCtx->hSocket != -1)
969 {
35f836fe 970 ConsolePrintf(pCtx, _T("Closing session...\n"));
5039dede
AK
971 nExitCode = CMD_EXIT_CLOSE_SESSION;
972 }
973 else
974 {
35f836fe 975 ConsolePrintf(pCtx, _T("Cannot exit from local server console\n"));
5039dede
AK
976 }
977 }
35f836fe 978 else if (IsCommand(_T("SHOW"), szBuffer, 2))
5039dede
AK
979 {
980 // Get argument
981 pArg = ExtractWord(pArg, szBuffer);
982
35f836fe 983 if (IsCommand(_T("FLAGS"), szBuffer, 1))
5039dede 984 {
35f836fe 985 ConsolePrintf(pCtx, _T("Flags: 0x%08X\n"), g_dwFlags);
5039dede
AK
986 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DAEMON));
987 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_SYSLOG));
988 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NETWORK_DISCOVERY));
989 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ACTIVE_NETWORK_DISCOVERY));
990 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_LOG_SQL_ERRORS));
991 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DELETE_EMPTY_SUBNETS));
992 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_SNMP_TRAPD));
993 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_ZONING));
5039dede 994 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYNC_NODE_NAMES_WITH_DNS));
cbc777ee
VK
995 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CHECK_TRUSTED_NODES));
996 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_WRITE_FULL_DUMP));
997 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_NODE_NAMES));
5039dede 998 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CATCH_EXCEPTIONS));
5039dede
AK
999 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_INTERNAL_CA));
1000 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_LOCKED));
cbc777ee 1001 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_MULTIPLE_DB_CONN));
5039dede 1002 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_CONNECTION_LOST));
cbc777ee 1003 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_NO_NETWORK_CONNECTIVITY));
5039dede
AK
1004 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_EVENT_STORM_DETECTED));
1005 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
1006 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
35f836fe 1007 ConsolePrintf(pCtx, _T("\n"));
5039dede 1008 }
35f836fe 1009 else if (IsCommand(_T("INDEX"), szBuffer, 1))
5039dede
AK
1010 {
1011 // Get argument
1012 pArg = ExtractWord(pArg, szBuffer);
1013
35f836fe 1014 if (IsCommand(_T("CONDITION"), szBuffer, 1))
5039dede
AK
1015 {
1016 DumpIndex(pCtx, g_rwlockConditionIndex, g_pConditionIndex, g_dwConditionIndexSize, FALSE);
1017 }
35f836fe 1018 else if (IsCommand(_T("ID"), szBuffer, 2))
5039dede
AK
1019 {
1020 DumpIndex(pCtx, g_rwlockIdIndex, g_pIndexById, g_dwIdIndexSize, FALSE);
1021 }
35f836fe 1022 else if (IsCommand(_T("INTERFACE"), szBuffer, 2))
5039dede
AK
1023 {
1024 DumpIndex(pCtx, g_rwlockInterfaceIndex, g_pInterfaceIndexByAddr,
1025 g_dwInterfaceAddrIndexSize, TRUE);
1026 }
35f836fe 1027 else if (IsCommand(_T("NODE"), szBuffer, 1))
5039dede
AK
1028 {
1029 DumpIndex(pCtx, g_rwlockNodeIndex, g_pNodeIndexByAddr,
1030 g_dwNodeAddrIndexSize, TRUE);
1031 }
35f836fe 1032 else if (IsCommand(_T("SUBNET"), szBuffer, 1))
5039dede
AK
1033 {
1034 DumpIndex(pCtx, g_rwlockSubnetIndex, g_pSubnetIndexByAddr,
1035 g_dwSubnetAddrIndexSize, TRUE);
1036 }
1037 else
1038 {
1039 if (szBuffer[0] == 0)
35f836fe
VK
1040 ConsolePrintf(pCtx, _T("ERROR: Missing index name\n")
1041 _T("Valid names are: CONDITION, ID, INTERFACE, NODE, SUBNET\n\n"));
5039dede 1042 else
35f836fe 1043 ConsolePrintf(pCtx, _T("ERROR: Invalid index name\n\n"));
5039dede
AK
1044 }
1045 }
35f836fe 1046 else if (IsCommand(_T("MEMORY"), szBuffer, 2))
5039dede
AK
1047 {
1048#ifdef NETXMS_MEMORY_DEBUG
1049 PrintMemoryBlocks();
1050#else
35f836fe 1051 ConsolePrintf(pCtx, _T("ERROR: Server was compiled without memory debugger\n\n"));
5039dede
AK
1052#endif
1053 }
35f836fe 1054 else if (IsCommand(_T("MUTEX"), szBuffer, 2))
5039dede 1055 {
35f836fe
VK
1056 ConsolePrintf(pCtx, _T("Mutex status:\n"));
1057 DbgTestRWLock(g_rwlockIdIndex, _T("g_hMutexIdIndex"), pCtx);
1058 DbgTestRWLock(g_rwlockNodeIndex, _T("g_hMutexNodeIndex"), pCtx);
1059 DbgTestRWLock(g_rwlockSubnetIndex, _T("g_hMutexSubnetIndex"), pCtx);
1060 DbgTestRWLock(g_rwlockInterfaceIndex, _T("g_hMutexInterfaceIndex"), pCtx);
1061 ConsolePrintf(pCtx, _T("\n"));
5039dede 1062 }
35f836fe 1063 else if (IsCommand(_T("OBJECTS"), szBuffer, 1))
5039dede
AK
1064 {
1065 DumpObjects(pCtx);
1066 }
35f836fe 1067 else if (IsCommand(_T("POLLERS"), szBuffer, 1))
5039dede
AK
1068 {
1069 ShowPollerState(pCtx);
1070 }
35f836fe 1071 else if (IsCommand(_T("QUEUES"), szBuffer, 1))
5039dede 1072 {
35f836fe
VK
1073 ShowQueueStats(pCtx, &g_conditionPollerQueue, _T("Condition poller"));
1074 ShowQueueStats(pCtx, &g_configPollQueue, _T("Configuration poller"));
040c45fa 1075 ShowQueueStats(pCtx, &g_topologyPollQueue, _T("Topology poller"));
35f836fe
VK
1076 ShowQueueStats(pCtx, g_pItemQueue, _T("Data collector"));
1077 ShowQueueStats(pCtx, g_pLazyRequestQueue, _T("Database writer"));
1078 ShowQueueStats(pCtx, g_pEventQueue, _T("Event processor"));
1079 ShowQueueStats(pCtx, &g_discoveryPollQueue, _T("Network discovery poller"));
1080 ShowQueueStats(pCtx, &g_nodePollerQueue, _T("Node poller"));
1081 ShowQueueStats(pCtx, &g_routePollQueue, _T("Routing table poller"));
1082 ShowQueueStats(pCtx, &g_statusPollQueue, _T("Status poller"));
1083 ConsolePrintf(pCtx, _T("\n"));
5039dede 1084 }
35f836fe 1085 else if (IsCommand(_T("ROUTING-TABLE"), szBuffer, 1))
5039dede
AK
1086 {
1087 DWORD dwNode;
1088 NetObj *pObject;
1089
1090 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1091 dwNode = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1092 if (dwNode != 0)
1093 {
1094 pObject = FindObjectById(dwNode);
1095 if (pObject != NULL)
1096 {
1097 if (pObject->Type() == OBJECT_NODE)
1098 {
1099 ROUTING_TABLE *pRT;
35f836fe 1100 TCHAR szIpAddr[16];
5039dede
AK
1101 int i;
1102
35f836fe 1103 ConsolePrintf(pCtx, _T("Routing table for node %s:\n\n"), pObject->Name());
58b3e451 1104 pRT = ((Node *)pObject)->getCachedRoutingTable();
5039dede
AK
1105 if (pRT != NULL)
1106 {
1107 for(i = 0; i < pRT->iNumEntries; i++)
1108 {
35f836fe
VK
1109 _sntprintf(szBuffer, 256, _T("%s/%d"), IpToStr(pRT->pRoutes[i].dwDestAddr, szIpAddr),
1110 BitsInMask(pRT->pRoutes[i].dwDestMask));
1111 ConsolePrintf(pCtx, _T("%-18s %-15s %-6d %d\n"), szBuffer,
1112 IpToStr(pRT->pRoutes[i].dwNextHop, szIpAddr),
1113 pRT->pRoutes[i].dwIfIndex, pRT->pRoutes[i].dwRouteType);
5039dede 1114 }
35f836fe 1115 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1116 }
1117 else
1118 {
35f836fe 1119 ConsolePrintf(pCtx, _T("Node doesn't have cached routing table\n\n"));
5039dede
AK
1120 }
1121 }
1122 else
1123 {
35f836fe 1124 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
5039dede
AK
1125 }
1126 }
1127 else
1128 {
35f836fe 1129 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
5039dede
AK
1130 }
1131 }
1132 else
1133 {
35f836fe 1134 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
5039dede
AK
1135 }
1136 }
35f836fe 1137 else if (IsCommand(_T("SESSIONS"), szBuffer, 2))
5039dede
AK
1138 {
1139 DumpSessions(pCtx);
1140 }
35f836fe 1141 else if (IsCommand(_T("STATS"), szBuffer, 2))
5039dede
AK
1142 {
1143 ShowServerStats(pCtx);
1144 }
35f836fe 1145 else if (IsCommand(_T("USERS"), szBuffer, 1))
5039dede
AK
1146 {
1147 DumpUsers(pCtx);
1148 }
35f836fe 1149 else if (IsCommand(_T("WATCHDOG"), szBuffer, 1))
5039dede
AK
1150 {
1151 WatchdogPrintStatus(pCtx);
35f836fe 1152 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1153 }
1154 else
1155 {
1156 if (szBuffer[0] == 0)
35f836fe 1157 ConsolePrintf(pCtx, _T("ERROR: Missing subcommand\n\n"));
5039dede 1158 else
35f836fe 1159 ConsolePrintf(pCtx, _T("ERROR: Invalid SHOW subcommand\n\n"));
5039dede
AK
1160 }
1161 }
35f836fe 1162 else if (IsCommand(_T("TRACE"), szBuffer, 1))
5039dede
AK
1163 {
1164 DWORD dwNode1, dwNode2;
1165 NetObj *pObject1, *pObject2;
1166 NETWORK_PATH_TRACE *pTrace;
35f836fe 1167 TCHAR szNextHop[16];
5039dede
AK
1168 int i;
1169
1170 // Get arguments
1171 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1172 dwNode1 = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1173
1174 pArg = ExtractWord(pArg, szBuffer);
35f836fe 1175 dwNode2 = _tcstoul(szBuffer, NULL, 0);
5039dede
AK
1176
1177 if ((dwNode1 != 0) && (dwNode2 != 0))
1178 {
1179 pObject1 = FindObjectById(dwNode1);
1180 if (pObject1 == NULL)
1181 {
35f836fe 1182 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode1);
5039dede
AK
1183 }
1184 else
1185 {
1186 pObject2 = FindObjectById(dwNode2);
1187 if (pObject2 == NULL)
1188 {
35f836fe 1189 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode2);
5039dede
AK
1190 }
1191 else
1192 {
1193 if ((pObject1->Type() == OBJECT_NODE) &&
1194 (pObject2->Type() == OBJECT_NODE))
1195 {
1196 pTrace = TraceRoute((Node *)pObject1, (Node *)pObject2);
1197 if (pTrace != NULL)
1198 {
35f836fe 1199 ConsolePrintf(pCtx, _T("Trace from %s to %s (%d hops):\n"),
5039dede
AK
1200 pObject1->Name(), pObject2->Name(), pTrace->iNumHops);
1201 for(i = 0; i < pTrace->iNumHops; i++)
35f836fe 1202 ConsolePrintf(pCtx, _T("[%d] %s %s %s %d\n"),
5039dede
AK
1203 pTrace->pHopList[i].pObject->Id(),
1204 pTrace->pHopList[i].pObject->Name(),
1205 IpToStr(pTrace->pHopList[i].dwNextHop, szNextHop),
35f836fe 1206 pTrace->pHopList[i].bIsVPN ? _T("VPN Connector ID:") : _T("Interface Index: "),
5039dede
AK
1207 pTrace->pHopList[i].dwIfIndex);
1208 DestroyTraceData(pTrace);
35f836fe 1209 ConsolePrintf(pCtx, _T("\n"));
5039dede
AK
1210 }
1211 else
1212 {
35f836fe 1213 ConsolePrintf(pCtx, _T("ERROR: Call to TraceRoute() failed\n\n"));
5039dede
AK
1214 }
1215 }
1216 else
1217 {
35f836fe 1218 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
5039dede
AK
1219 }
1220 }
1221 }
1222 }
1223 else
1224 {
35f836fe 1225 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node id(s)\n\n"));
5039dede
AK
1226 }
1227 }
35f836fe 1228 else if (IsCommand(_T("HELP"), szBuffer, 2) || IsCommand(_T("?"), szBuffer, 1))
5039dede 1229 {
35f836fe
VK
1230 ConsolePrintf(pCtx, _T("Valid commands are:\n")
1231 _T(" debug [on|off] - Turn debug mode on or off\n")
1232 _T(" down - Down NetXMS server\n")
1233 _T(" exit - Exit from remote session\n")
1234 _T(" help - Display this help\n")
1235 _T(" raise <exception> - Raise exception\n")
1236 _T(" show flags - Show internal server flags\n")
1237 _T(" show index <index> - Show internal index\n")
1238 _T(" show mutex - Display mutex status\n")
1239 _T(" show objects - Dump network objects to screen\n")
1240 _T(" show pollers - Show poller threads state information\n")
1241 _T(" show queues - Show internal queues statistics\n")
1242 _T(" show routing-table <node> - Show cached routing table for node\n")
1243 _T(" show sessions - Show active client sessions\n")
1244 _T(" show stats - Show server statistics\n")
1245 _T(" show users - Show users\n")
1246 _T(" show watchdog - Display watchdog information\n")
1247 _T(" trace <node1> <node2> - Show network path trace between two nodes\n")
1248 _T("\nAlmost all commands can be abbreviated to 2 or 3 characters\n")
1249 _T("\n"));
5039dede
AK
1250 }
1251 else
1252 {
35f836fe 1253 ConsolePrintf(pCtx, _T("UNKNOWN COMMAND\n\n"));
5039dede
AK
1254 }
1255
1256 return nExitCode;
1257}
1258
1259
1260//
1261// Signal handler for UNIX platforms
1262//
1263
1264#ifndef _WIN32
1265
1266void SignalHandlerStub(int nSignal)
1267{
1268 // should be unused, but JIC...
1269 if (nSignal == SIGCHLD)
1270 {
1271 while (waitpid(-1, NULL, WNOHANG) > 0)
1272 ;
1273 }
1274}
1275
1276THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1277{
1278 sigset_t signals;
1279 int nSignal;
1280 BOOL bCallShutdown = FALSE;
1281
1282 m_signalHandlerThread = pthread_self();
1283
1284 // default for SIGCHLD: ignore
1285 signal(SIGCHLD, &SignalHandlerStub);
1286
1287 sigemptyset(&signals);
1288 sigaddset(&signals, SIGTERM);
1289 sigaddset(&signals, SIGINT);
1290 sigaddset(&signals, SIGPIPE);
1291 sigaddset(&signals, SIGSEGV);
1292 sigaddset(&signals, SIGCHLD);
1293 sigaddset(&signals, SIGHUP);
1294 sigaddset(&signals, SIGUSR1);
1295 sigaddset(&signals, SIGUSR2);
1296
1297 sigprocmask(SIG_BLOCK, &signals, NULL);
1298
1299 while(1)
1300 {
1301 if (sigwait(&signals, &nSignal) == 0)
1302 {
1303 switch(nSignal)
1304 {
1305 case SIGTERM:
1306 case SIGINT:
1307 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1308 if (IsStandalone())
1309 bCallShutdown = TRUE;
1310 ConditionSet(m_condShutdown);
1311 goto stop_handler;
1312 case SIGSEGV:
1313 abort();
1314 break;
1315 case SIGCHLD:
1316 while (waitpid(-1, NULL, WNOHANG) > 0)
1317 ;
1318 break;
1319 case SIGUSR1:
1320 if (g_dwFlags & AF_SHUTDOWN)
1321 goto stop_handler;
1322 break;
1323 default:
1324 break;
1325 }
1326 }
1327 else
1328 {
1329 ThreadSleepMs(100);
1330 }
1331 }
1332
1333stop_handler:
1334 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1335 if (bCallShutdown)
1336 Shutdown();
1337 return THREAD_OK;
1338}
1339
1340#endif
1341
1342
1343//
1344// Common main()
1345//
1346
1347THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1348{
1349 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1350
1351 if (IsStandalone())
1352 {
1353 char *ptr, szCommand[256];
1354 struct __console_ctx ctx;
35f836fe
VK
1355#ifdef UNICODE
1356 WCHAR wcCommand[256];
1357#endif
5039dede
AK
1358
1359 ctx.hSocket = -1;
1360 ctx.pMsg = NULL;
200d662d 1361 ctx.session = NULL;
35f836fe
VK
1362 _tprintf(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Ready\n")
1363 _T("Enter \"help\" for command list or \"down\" for server shutdown\n")
1364 _T("System Console\n\n"));
5039dede
AK
1365
1366#if USE_READLINE
1367 // Initialize readline library if we use it
0322eed7 1368 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
5039dede
AK
1369#endif
1370
1371 while(1)
1372 {
1373#if USE_READLINE
1374 ptr = readline("netxmsd: ");
1375#else
1376 printf("netxmsd: ");
1377 fflush(stdout);
1378 if (fgets(szCommand, 255, stdin) == NULL)
1379 break; // Error reading stdin
1380 ptr = strchr(szCommand, '\n');
1381 if (ptr != NULL)
1382 *ptr = 0;
1383 ptr = szCommand;
1384#endif
1385
1386 if (ptr != NULL)
1387 {
35f836fe
VK
1388#ifdef UNICODE
1389 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
1390 wcCommand[255] = 0;
1391 StrStrip(wcCommand);
1392 if (wcCommand[0] != 0)
1393 {
1394 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
1395#else
5039dede
AK
1396 StrStrip(ptr);
1397 if (*ptr != 0)
1398 {
1399 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
35f836fe 1400#endif
5039dede
AK
1401 break;
1402#if USE_READLINE
1403 add_history(ptr);
1404#endif
1405 }
1406#if USE_READLINE
1407 free(ptr);
1408#endif
1409 }
1410 else
1411 {
1412 printf("\n");
1413 }
1414 }
1415
1416#if USE_READLINE
1417 free(ptr);
1418#endif
1419 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1420 Shutdown();
1421 }
1422 else
1423 {
1424 ConditionWait(m_condShutdown, INFINITE);
1425 // On Win32, Shutdown() will be called by service control handler
1426#ifndef _WIN32
1427 Shutdown();
1428#endif
1429 }
1430 return THREAD_OK;
1431}
1432
1433
1434//
1435// Initiate server shutdown
1436//
1437
035a4d73 1438void InitiateShutdown()
5039dede
AK
1439{
1440#ifdef _WIN32
1441 Shutdown();
1442#else
1443 if (IsStandalone())
1444 {
1445 Shutdown();
1446 }
1447 else
1448 {
1449 pthread_kill(m_signalHandlerThread, SIGTERM);
1450 }
1451#endif
1452}
1453
1454
1455//
1456// DLL Entry point
1457//
1458
1459#ifdef _WIN32
1460
1461BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1462{
1463 if (dwReason == DLL_PROCESS_ATTACH)
1464 DisableThreadLibraryCalls(hInstance);
1465 return TRUE;
1466}
1467
1468#endif