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