d55f6f89548921f44c07d20f379992652fd80046
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Raden Solutions
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 #include <netxms_mt.h>
26 #include <hdlink.h>
27
28 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE && !defined(UNICODE)
29 #include <readline/readline.h>
30 #include <readline/history.h>
31 #define USE_READLINE 1
32 #endif
33
34 #ifdef _WIN32
35 #include <errno.h>
36 #include <psapi.h>
37 #include <conio.h>
38 #define open _open
39 #define write _write
40 #define close _close
41 #else
42 #include <signal.h>
43 #include <sys/wait.h>
44 #endif
45
46 #ifdef WITH_ZMQ
47 #include "zeromq.h"
48 #endif
49
50 /**
51 * Format string to show value of global flag
52 */
53 #define SHOW_FLAG_VALUE(x) _T(" %-38s = %d\n"), _T(#x), (g_flags & x) ? 1 : 0
54
55 /**
56 * Messages generated by mc.pl (for UNIX version only)
57 */
58 #ifndef _WIN32
59 extern unsigned int g_dwNumMessages;
60 extern const TCHAR *g_szMessages[];
61 #endif
62
63 /**
64 * Shutdown reasons
65 */
66 #define SHUTDOWN_DEFAULT 0
67 #define SHUTDOWN_FROM_CONSOLE 1
68 #define SHUTDOWN_BY_SIGNAL 2
69
70 /**
71 * Externals
72 */
73 extern Queue g_nodePollerQueue;
74 extern Queue g_dataCollectionQueue;
75 extern Queue g_dciCacheLoaderQueue;
76 extern Queue g_syslogProcessingQueue;
77 extern Queue g_syslogWriteQueue;
78 extern ThreadPool *g_pollerThreadPool;
79 extern ThreadPool *g_schedulerThreadPool;
80
81 void InitClientListeners();
82 void InitMobileDeviceListeners();
83 void InitCertificates();
84 void InitUsers();
85 void CleanupUsers();
86 void LoadPerfDataStorageDrivers();
87 void ImportLocalConfiguration();
88
89 void ExecuteScheduledScript(const ScheduledTaskParameters *param);
90 void MaintenanceModeEnter(const ScheduledTaskParameters *params);
91 void MaintenanceModeLeave(const ScheduledTaskParameters *params);
92
93 #if XMPP_SUPPORTED
94 void StartXMPPConnector();
95 void StopXMPPConnector();
96 #endif
97
98 /**
99 * Syslog server control
100 */
101 void StartSyslogServer();
102 void StopSyslogServer();
103
104 /**
105 * Housekeeper control
106 */
107 void StartHouseKeeper();
108 void StopHouseKeeper();
109 void RunHouseKeeper();
110
111 /**
112 * Thread functions
113 */
114 THREAD_RESULT THREAD_CALL Syncer(void *);
115 THREAD_RESULT THREAD_CALL NodePoller(void *);
116 THREAD_RESULT THREAD_CALL PollManager(void *);
117 THREAD_RESULT THREAD_CALL EventProcessor(void *);
118 THREAD_RESULT THREAD_CALL WatchdogThread(void *);
119 THREAD_RESULT THREAD_CALL ClientListener(void *);
120 THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *);
121 THREAD_RESULT THREAD_CALL MobileDeviceListener(void *);
122 THREAD_RESULT THREAD_CALL MobileDeviceListenerIPv6(void *);
123 THREAD_RESULT THREAD_CALL ISCListener(void *);
124 THREAD_RESULT THREAD_CALL LocalAdminListener(void *);
125 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
126 THREAD_RESULT THREAD_CALL BeaconPoller(void *);
127 THREAD_RESULT THREAD_CALL JobManagerThread(void *);
128 THREAD_RESULT THREAD_CALL UptimeCalculator(void *);
129 THREAD_RESULT THREAD_CALL ReportingServerConnector(void *);
130
131 /**
132 * Global variables
133 */
134 TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = _T("{search}");
135 TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
136 UINT32 g_dwLogRotationMode = NXLOG_ROTATION_BY_SIZE;
137 UINT32 g_dwMaxLogSize = 16384 * 1024;
138 UINT32 g_dwLogHistorySize = 4;
139 TCHAR g_szDailyLogFileSuffix[64] = _T("");
140 TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
141 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
142 TCHAR NXCORE_EXPORTABLE g_szListenAddress[MAX_PATH] = _T("*");
143 #ifndef _WIN32
144 TCHAR NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = _T("/var/run/netxmsd.pid");
145 #endif
146 UINT32 g_dwDiscoveryPollingInterval;
147 UINT32 g_dwStatusPollingInterval;
148 UINT32 g_dwConfigurationPollingInterval;
149 UINT32 g_dwRoutingTableUpdateInterval;
150 UINT32 g_dwTopologyPollingInterval;
151 UINT32 g_dwConditionPollingInterval;
152 UINT32 g_instancePollingInterval;
153 UINT32 g_icmpPingSize;
154 UINT32 g_icmpPingTimeout = 1500; // ICMP ping timeout (milliseconds)
155 UINT32 g_auditFlags;
156 UINT32 g_slmPollingInterval;
157 TCHAR NXCORE_EXPORTABLE g_netxmsdDataDir[MAX_PATH] = _T("");
158 TCHAR NXCORE_EXPORTABLE g_netxmsdLibDir[MAX_PATH] = _T("");
159 int g_dbSyntax = DB_SYNTAX_UNKNOWN;
160 UINT32 NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
161 UINT64 g_serverId = 0;
162 RSA *g_pServerKey = NULL;
163 time_t g_serverStartTime = 0;
164 UINT32 g_lockTimeout = 60000; // Default timeout for acquiring mutex
165 UINT32 g_agentCommandTimeout = 4000; // Default timeout for requests to agent
166 UINT32 g_thresholdRepeatInterval = 0; // Disabled by default
167 int g_requiredPolls = 1;
168 DB_DRIVER g_dbDriver = NULL;
169 ThreadPool *g_mainThreadPool = NULL;
170 INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
171
172 /**
173 * Static data
174 */
175 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
176 static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
177 static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
178 static int m_nShutdownReason = SHUTDOWN_DEFAULT;
179
180 #ifndef _WIN32
181 static pthread_t m_signalHandlerThread;
182 #endif
183
184 /**
185 * Sleep for specified number of seconds or until system shutdown arrives
186 * Function will return TRUE if shutdown event occurs
187 *
188 * @param seconds seconds to sleep
189 * @return true if server is shutting down
190 */
191 bool NXCORE_EXPORTABLE SleepAndCheckForShutdown(int seconds)
192 {
193 return ConditionWait(m_condShutdown, seconds * 1000);
194 }
195
196 /**
197 * Disconnect from database (exportable function for startup module)
198 */
199 void NXCORE_EXPORTABLE ShutdownDB()
200 {
201 DBConnectionPoolShutdown();
202 DBUnloadDriver(g_dbDriver);
203 }
204
205 /**
206 * Check data directory for existence
207 */
208 static BOOL CheckDataDir()
209 {
210 TCHAR szBuffer[MAX_PATH];
211
212 if (_tchdir(g_netxmsdDataDir) == -1)
213 {
214 nxlog_write(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_netxmsdDataDir);
215 return FALSE;
216 }
217
218 #ifdef _WIN32
219 #define MKDIR(name) _tmkdir(name)
220 #else
221 #define MKDIR(name) _tmkdir(name, 0700)
222 #endif
223
224 // Create directory for package files if it doesn't exist
225 _tcscpy(szBuffer, g_netxmsdDataDir);
226 _tcscat(szBuffer, DDIR_PACKAGES);
227 if (MKDIR(szBuffer) == -1)
228 if (errno != EEXIST)
229 {
230 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
231 return FALSE;
232 }
233
234 // Create directory for map background images if it doesn't exist
235 _tcscpy(szBuffer, g_netxmsdDataDir);
236 _tcscat(szBuffer, DDIR_BACKGROUNDS);
237 if (MKDIR(szBuffer) == -1)
238 if (errno != EEXIST)
239 {
240 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
241 return FALSE;
242 }
243
244 // Create directory for image library is if does't exists
245 _tcscpy(szBuffer, g_netxmsdDataDir);
246 _tcscat(szBuffer, DDIR_IMAGES);
247 if (MKDIR(szBuffer) == -1)
248 {
249 if (errno != EEXIST)
250 {
251 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
252 return FALSE;
253 }
254 }
255
256 // Create directory for file store if does't exists
257 _tcscpy(szBuffer, g_netxmsdDataDir);
258 _tcscat(szBuffer, DDIR_FILES);
259 if (MKDIR(szBuffer) == -1)
260 {
261 if (errno != EEXIST)
262 {
263 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
264 return FALSE;
265 }
266 }
267
268 #undef MKDIR
269
270 return TRUE;
271 }
272
273 /**
274 * Load global configuration parameters
275 */
276 static void LoadGlobalConfig()
277 {
278 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
279 g_dwStatusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
280 g_dwConfigurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
281 g_instancePollingInterval = ConfigReadInt(_T("InstancePollingInterval"), 600);
282 g_dwRoutingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
283 g_dwTopologyPollingInterval = ConfigReadInt(_T("TopologyPollingInterval"), 1800);
284 g_dwConditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
285 g_slmPollingInterval = ConfigReadInt(_T("SlmPollingInterval"), 60);
286 DCObject::m_defaultPollingInterval = ConfigReadInt(_T("DefaultDCIPollingInterval"), 60);
287 DCObject::m_defaultRetentionTime = ConfigReadInt(_T("DefaultDCIRetentionTime"), 30);
288 g_defaultAgentCacheMode = (INT16)ConfigReadInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF);
289 if ((g_defaultAgentCacheMode != AGENT_CACHE_ON) && (g_defaultAgentCacheMode != AGENT_CACHE_OFF))
290 {
291 DbgPrintf(1, _T("Invalid value %d of DefaultAgentCacheMode: reset to %d (OFF)"), g_defaultAgentCacheMode, AGENT_CACHE_OFF);
292 ConfigWriteInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF, true, true, true);
293 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
294 }
295 if (ConfigReadInt(_T("DeleteEmptySubnets"), 1))
296 g_flags |= AF_DELETE_EMPTY_SUBNETS;
297 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
298 g_flags |= AF_ENABLE_SNMP_TRAPD;
299 if (ConfigReadInt(_T("ProcessTrapsFromUnmanagedNodes"), 0))
300 g_flags |= AF_TRAPS_FROM_UNMANAGED_NODES;
301 if (ConfigReadInt(_T("EnableZoning"), 0))
302 g_flags |= AF_ENABLE_ZONING;
303 if (ConfigReadInt(_T("EnableObjectTransactions"), 0))
304 g_flags |= AF_ENABLE_OBJECT_TRANSACTIONS;
305 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
306 g_flags |= AF_ENABLE_NETWORK_DISCOVERY;
307 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
308 g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
309 if (ConfigReadInt(_T("UseSNMPTrapsForDiscovery"), 0))
310 g_flags |= AF_SNMP_TRAP_DISCOVERY;
311 if (ConfigReadInt(_T("ResolveNodeNames"), 1))
312 g_flags |= AF_RESOLVE_NODE_NAMES;
313 if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
314 g_flags |= AF_SYNC_NODE_NAMES_WITH_DNS;
315 if (ConfigReadInt(_T("CheckTrustedNodes"), 1))
316 g_flags |= AF_CHECK_TRUSTED_NODES;
317 if (ConfigReadInt(_T("EnableNXSLContainerFunctions"), 1))
318 g_flags |= AF_ENABLE_NXSL_CONTAINER_FUNCS;
319 if (ConfigReadInt(_T("UseFQDNForNodeNames"), 1))
320 g_flags |= AF_USE_FQDN_FOR_NODE_NAMES;
321 if (ConfigReadInt(_T("ApplyDCIFromTemplateToDisabledDCI"), 1))
322 g_flags |= AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE;
323 if (ConfigReadInt(_T("ResolveDNSToIPOnStatusPoll"), 0))
324 g_flags |= AF_RESOLVE_IP_FOR_EACH_STATUS_POLL;
325 if (ConfigReadInt(_T("CaseInsensitiveLoginNames"), 0))
326 g_flags |= AF_CASE_INSENSITIVE_LOGINS;
327 if (ConfigReadInt(_T("TrapSourcesInAllZones"), 0))
328 g_flags |= AF_TRAP_SOURCES_IN_ALL_ZONES;
329
330 if (g_netxmsdDataDir[0] == 0)
331 {
332 GetNetXMSDirectory(nxDirData, g_netxmsdDataDir);
333 DbgPrintf(1, _T("Data directory set to %s"), g_netxmsdDataDir);
334 }
335 else
336 {
337 DbgPrintf(1, _T("Using data directory %s"), g_netxmsdDataDir);
338 }
339
340 g_icmpPingTimeout = ConfigReadInt(_T("IcmpPingTimeout"), 1500);
341 g_icmpPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
342 g_lockTimeout = ConfigReadInt(_T("LockTimeout"), 60000);
343 g_agentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 4000);
344 g_thresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
345 g_requiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
346
347 UINT32 snmpTimeout = ConfigReadInt(_T("SNMPRequestTimeout"), 1500);
348 SnmpSetDefaultTimeout(snmpTimeout);
349 }
350
351 /**
352 * Initialize cryptografic functions
353 */
354 static BOOL InitCryptografy()
355 {
356 #ifdef _WITH_ENCRYPTION
357 TCHAR szKeyFile[MAX_PATH];
358 BOOL bResult = FALSE;
359 int fd, iPolicy;
360 UINT32 dwLen;
361 BYTE *pBufPos, *pKeyBuffer, hash[SHA1_DIGEST_SIZE];
362
363 if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F), DbgPrintf2))
364 return FALSE;
365 DbgPrintf(4, _T("Supported ciphers: %s"), (const TCHAR *)NXCPGetSupportedCiphersAsText());
366
367 SSL_library_init();
368 SSL_load_error_strings();
369
370 _tcscpy(szKeyFile, g_netxmsdDataDir);
371 _tcscat(szKeyFile, DFILE_KEYS);
372 fd = _topen(szKeyFile, O_RDONLY | O_BINARY);
373 g_pServerKey = LoadRSAKeys(szKeyFile);
374 if (g_pServerKey == NULL)
375 {
376 DbgPrintf(1, _T("Generating RSA key pair..."));
377 g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
378 if (g_pServerKey != NULL)
379 {
380 fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
381 if (fd != -1)
382 {
383 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
384 dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
385 pKeyBuffer = (BYTE *)malloc(dwLen);
386
387 pBufPos = pKeyBuffer;
388 i2d_RSAPublicKey(g_pServerKey, &pBufPos);
389 i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
390 write(fd, &dwLen, sizeof(UINT32));
391 write(fd, pKeyBuffer, dwLen);
392
393 CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
394 write(fd, hash, SHA1_DIGEST_SIZE);
395
396 close(fd);
397 free(pKeyBuffer);
398 bResult = TRUE;
399 }
400 else
401 {
402 DbgPrintf(0, _T("Failed to open %s for writing"), szKeyFile);
403 }
404 }
405 else
406 {
407 DbgPrintf(0, _T("Failed to generate RSA key"));
408 }
409 }
410 else
411 {
412 bResult = TRUE;
413 }
414
415 iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
416 if ((iPolicy < 0) || (iPolicy > 3))
417 iPolicy = 1;
418 SetAgentDEP(iPolicy);
419
420 return bResult;
421 #else
422 return TRUE;
423 #endif
424 }
425
426 /**
427 * Check if process with given PID exists and is a NetXMS server process
428 */
429 static BOOL IsNetxmsdProcess(UINT32 dwPID)
430 {
431 #ifdef _WIN32
432 HANDLE hProcess;
433 TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
434 BOOL bRet = FALSE;
435
436 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
437 if (hProcess != NULL)
438 {
439 if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
440 (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
441 {
442 bRet = !_tcsicmp(szExtModule, szIntModule);
443 }
444 else
445 {
446 // Cannot read process name, for safety assume that it's a server process
447 bRet = TRUE;
448 }
449 CloseHandle(hProcess);
450 }
451 return bRet;
452 #else
453 return (kill((pid_t)dwPID, 0) != -1);
454 #endif
455 }
456
457 /**
458 * Database event handler
459 */
460 static void DBEventHandler(DWORD dwEvent, const WCHAR *pszArg1, const WCHAR *pszArg2, bool connLost, void *userArg)
461 {
462 if (!(g_flags & AF_SERVER_INITIALIZED))
463 return; // Don't try to do anything if server is not ready yet
464
465 switch(dwEvent)
466 {
467 case DBEVENT_CONNECTION_LOST:
468 PostEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
469 g_flags |= AF_DB_CONNECTION_LOST;
470 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
471 break;
472 case DBEVENT_CONNECTION_RESTORED:
473 PostEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
474 g_flags &= ~AF_DB_CONNECTION_LOST;
475 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
476 break;
477 case DBEVENT_QUERY_FAILED:
478 PostEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "uud", pszArg1, pszArg2, connLost ? 1 : 0);
479 break;
480 default:
481 break;
482 }
483 }
484
485 /**
486 * Send console message to session with open console
487 */
488 static void SendConsoleMessage(ClientSession *session, void *arg)
489 {
490 if (session->isConsoleOpen())
491 {
492 NXCPMessage msg;
493
494 msg.setCode(CMD_ADM_MESSAGE);
495 msg.setField(VID_MESSAGE, (TCHAR *)arg);
496 session->postMessage(&msg);
497 }
498 }
499
500 /**
501 * Console writer
502 */
503 static void LogConsoleWriter(const TCHAR *format, ...)
504 {
505 TCHAR buffer[8192];
506 va_list args;
507
508 va_start(args, format);
509 _vsntprintf(buffer, 8192, format, args);
510 buffer[8191] = 0;
511 va_end(args);
512
513 WriteToTerminal(buffer);
514
515 EnumerateClientSessions(SendConsoleMessage, buffer);
516 }
517
518 /**
519 * Oracle session init callback
520 */
521 static void OracleSessionInitCallback(DB_HANDLE hdb)
522 {
523 DBQuery(hdb, _T("ALTER SESSION SET DDL_LOCK_TIMEOUT = 60"));
524 }
525
526 /**
527 * Server initialization
528 */
529 BOOL NXCORE_EXPORTABLE Initialize()
530 {
531 int i, iDBVersion;
532 TCHAR szInfo[256];
533
534 g_serverStartTime = time(NULL);
535 srand((unsigned int)g_serverStartTime);
536
537 if (g_netxmsdLibDir[0] == 0)
538 {
539 GetNetXMSDirectory(nxDirLib, g_netxmsdLibDir);
540 DbgPrintf(1, _T("Lib directory set to %s"), g_netxmsdLibDir);
541 }
542
543 if (!(g_flags & AF_USE_SYSLOG))
544 {
545 if (!nxlog_set_rotation_policy((int)g_dwLogRotationMode, (int)g_dwMaxLogSize, (int)g_dwLogHistorySize, g_szDailyLogFileSuffix))
546 if (!(g_flags & AF_DAEMON))
547 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
548 }
549 if (!nxlog_open((g_flags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
550 ((g_flags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
551 ((g_flags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
552 ((g_flags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
553 _T("LIBNXSRV.DLL"),
554 #ifdef _WIN32
555 0, NULL))
556 #else
557 g_dwNumMessages, g_szMessages))
558 #endif
559 {
560 _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
561 return FALSE;
562 }
563 nxlog_set_console_writer(LogConsoleWriter);
564
565 // Set code page
566 #ifndef _WIN32
567 if (SetDefaultCodepage(g_szCodePage))
568 {
569 DbgPrintf(1, _T("Code page set to %hs"), g_szCodePage);
570 }
571 else
572 {
573 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "m", g_szCodePage);
574 }
575 #endif
576
577 // Set process affinity mask
578 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
579 {
580 #ifdef _WIN32
581 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
582 DbgPrintf(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
583 #else
584 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
585 #endif
586 }
587
588 #ifdef _WIN32
589 WSADATA wsaData;
590 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
591 if (wrc != 0)
592 {
593 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
594 return FALSE;
595 }
596 #endif
597
598 InitLocalNetInfo();
599 SnmpSetMessageIds(MSG_OID_PARSE_ERROR, MSG_SNMP_UNKNOWN_TYPE, MSG_SNMP_GET_ERROR);
600
601 // Create queue for delayed SQL queries
602 g_dbWriterQueue = new Queue(256, 64);
603 g_dciDataWriterQueue = new Queue(1024, 1024);
604 g_dciRawDataWriterQueue = new Queue(1024, 1024);
605
606 // Initialize database driver and connect to database
607 DBSetDebugPrintCallback(DbgPrintf2);
608 if (!DBInit(MSG_OTHER, (g_flags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
609 return FALSE;
610 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (g_debugLevel >= 9), DBEventHandler, NULL);
611 if (g_dbDriver == NULL)
612 return FALSE;
613
614 // Connect to database
615 DB_HANDLE hdbBootstrap = NULL;
616 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
617 for(i = 0; ; i++)
618 {
619 hdbBootstrap = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
620 if ((hdbBootstrap != NULL) || (i == 5))
621 break;
622 ThreadSleep(5);
623 }
624 if (hdbBootstrap == NULL)
625 {
626 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
627 return FALSE;
628 }
629 DbgPrintf(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
630
631 // Check database version
632 iDBVersion = DBGetSchemaVersion(hdbBootstrap);
633 if (iDBVersion != DB_FORMAT_VERSION)
634 {
635 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
636 DBDisconnect(hdbBootstrap);
637 return FALSE;
638 }
639
640 // Read database syntax
641 g_dbSyntax = DBGetSyntax(hdbBootstrap);
642 if (g_dbSyntax == DB_SYNTAX_ORACLE)
643 {
644 DBSetSessionInitCallback(OracleSessionInitCallback);
645 }
646
647 int baseSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolBaseSize"), 10);
648 int maxSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxSize"), 30);
649 int cooldownTime = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolCooldownTime"), 300);
650 int ttl = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxLifetime"), 14400);
651
652 DBDisconnect(hdbBootstrap);
653
654 if (!DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, baseSize, maxSize, cooldownTime, ttl))
655 {
656 nxlog_write(MSG_DBCONNPOOL_INIT_FAILED, EVENTLOG_ERROR_TYPE, NULL);
657 return FALSE;
658 }
659
660 UINT32 lrt = ConfigReadULong(_T("LongRunningQueryThreshold"), 0);
661 if (lrt != 0)
662 DBSetLongRunningThreshold(lrt);
663
664 // Read server ID
665 MetaDataReadStr(_T("ServerID"), szInfo, 256, _T(""));
666 StrStrip(szInfo);
667 if (szInfo[0] != 0)
668 {
669 g_serverId = _tcstoull(szInfo, NULL, 16);
670 }
671 else
672 {
673 // Generate new ID
674 g_serverId = ((UINT64)time(NULL) << 31) | (UINT64)((UINT32)rand() & 0x7FFFFFFF);
675 _sntprintf(szInfo, 256, UINT64X_FMT(_T("016")), g_serverId);
676 MetaDataWriteStr(_T("ServerID"), szInfo);
677 }
678 DbgPrintf(1, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
679
680 // Initialize locks
681 retry_db_lock:
682 InetAddress addr;
683 if (!InitLocks(&addr, szInfo))
684 {
685 if (!addr.isValid()) // Some SQL problems
686 {
687 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
688 }
689 else // Database already locked by another server instance
690 {
691 // Check for lock from crashed/terminated local process
692 if (GetLocalIpAddr().equals(addr))
693 {
694 UINT32 dwPID;
695
696 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
697 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
698 {
699 UnlockDB();
700 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
701 goto retry_db_lock;
702 }
703 }
704 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "As", &addr, szInfo);
705 }
706 return FALSE;
707 }
708 g_flags |= AF_DB_LOCKED;
709
710 // Load global configuration parameters
711 LoadGlobalConfig();
712 CASReadSettings();
713 DbgPrintf(1, _T("Global configuration loaded"));
714
715 // Check data directory
716 if (!CheckDataDir())
717 return FALSE;
718
719 // Initialize cryptografy
720 if (!InitCryptografy())
721 {
722 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
723 return FALSE;
724 }
725
726 // Initialize certificate store and CA
727 InitCertificates();
728
729 // Create synchronization stuff
730 m_condShutdown = ConditionCreate(TRUE);
731
732 // Create thread pools
733 DbgPrintf(2, _T("Creating thread pools"));
734 ThreadPoolSetDebugCallback(DbgPrintf2);
735 g_mainThreadPool = ThreadPoolCreate(8, 256, _T("MAIN"));
736 g_agentConnectionThreadPool = ThreadPoolCreate(4, 256, _T("AGENT"));
737
738 // Setup unique identifiers table
739 if (!InitIdTable())
740 return FALSE;
741 DbgPrintf(2, _T("ID table created"));
742
743 // Update status for unfinished jobs in job history
744 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
745 DBQuery(hdb, _T("UPDATE job_history SET status=4,failure_message='Aborted due to server shutdown or crash' WHERE status NOT IN (3,4,5)"));
746 DBConnectionPoolReleaseConnection(hdb);
747
748 // Load and compile scripts
749 LoadScripts();
750
751 // Initialize watchdog
752 WatchdogInit();
753
754 // Load modules
755 if (!LoadNetXMSModules())
756 return FALSE; // Mandatory module not loaded
757
758 // Initialize mailer and SMS sender
759 InitMailer();
760 InitSMSSender();
761
762 // Load users from database
763 InitUsers();
764 if (!LoadUsers())
765 {
766 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
767 return FALSE;
768 }
769 DbgPrintf(2, _T("User accounts loaded"));
770
771 // Initialize audit
772 InitAuditLog();
773
774 // Initialize event handling subsystem
775 if (!InitEventSubsystem())
776 return FALSE;
777
778 // Initialize alarms
779 if (!InitAlarmManager())
780 return FALSE;
781
782 // Initialize objects infrastructure and load objects from database
783 LoadNetworkDeviceDrivers();
784 ObjectsInit();
785 if (!LoadObjects())
786 return FALSE;
787 DbgPrintf(1, _T("Objects loaded and initialized"));
788
789 // Initialize situations
790 if (!SituationsInit())
791 return FALSE;
792 DbgPrintf(1, _T("Situations loaded and initialized"));
793
794 // Initialize and load event actions
795 if (!InitActions())
796 {
797 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
798 return FALSE;
799 }
800
801 // Initialize helpdesk link
802 SetHDLinkEntryPoints(ResolveAlarmByHDRef, TerminateAlarmByHDRef);
803 LoadHelpDeskLink();
804
805 // Initialize data collection subsystem
806 LoadPerfDataStorageDrivers();
807 if (!InitDataCollector())
808 return FALSE;
809
810 InitLogAccess();
811 FileUploadJob::init();
812 InitMappingTables();
813
814 InitClientListeners();
815 if (ConfigReadInt(_T("ImportConfigurationOnStartup"), 1))
816 ImportLocalConfiguration();
817
818 // Check if management node object presented in database
819 CheckForMgmtNode();
820 if (g_dwMgmtNode == 0)
821 {
822 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
823 return FALSE;
824 }
825
826 // Start threads
827 ThreadCreate(WatchdogThread, 0, NULL);
828 ThreadCreate(NodePoller, 0, NULL);
829 ThreadCreate(JobManagerThread, 0, NULL);
830 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
831 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
832
833 StartHouseKeeper();
834
835 // Start event processor
836 ThreadCreate(EventProcessor, 0, NULL);
837
838 // Start SNMP trapper
839 InitTraps();
840 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
841 ThreadCreate(SNMPTrapReceiver, 0, NULL);
842
843 // Start built-in syslog daemon
844 if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
845 StartSyslogServer();
846
847 // Start database _T("lazy") write thread
848 StartDBWriter();
849
850 // Start local administartive interface listener if required
851 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
852 ThreadCreate(LocalAdminListener, 0, NULL);
853
854 // Start beacon host poller
855 ThreadCreate(BeaconPoller, 0, NULL);
856
857 // Start inter-server communication listener
858 if (ConfigReadInt(_T("EnableISCListener"), 0))
859 ThreadCreate(ISCListener, 0, NULL);
860
861 // Start reporting server connector
862 if (ConfigReadInt(_T("EnableReportingServer"), 0))
863 ThreadCreate(ReportingServerConnector, 0, NULL);
864
865 //Start ldap synchronization
866 if (ConfigReadInt(_T("LdapSyncInterval"), 0))
867 ThreadCreate(SyncLDAPUsers, 0, NULL);
868
869 RegisterSchedulerTaskHandler(_T("Execute.Script"), ExecuteScheduledScript, SYSTEM_ACCESS_SCHEDULE_SCRIPT);
870 RegisterSchedulerTaskHandler(_T("Maintenance.Enter"), MaintenanceModeEnter, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
871 RegisterSchedulerTaskHandler(_T("Maintenance.Leave"), MaintenanceModeLeave, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
872 InitializeTaskScheduler();
873
874 // Allow clients to connect
875 ThreadCreate(ClientListener, 0, NULL);
876 #ifdef WITH_IPV6
877 ThreadCreate(ClientListenerIPv6, 0, NULL);
878 #endif
879
880 // Allow mobile devices to connect
881 InitMobileDeviceListeners();
882 ThreadCreate(MobileDeviceListener, 0, NULL);
883 #ifdef WITH_IPV6
884 ThreadCreate(MobileDeviceListenerIPv6, 0, NULL);
885 #endif
886
887 // Start uptime calculator for SLM
888 ThreadCreate(UptimeCalculator, 0, NULL);
889
890 DbgPrintf(2, _T("LIBDIR: %s"), g_netxmsdLibDir);
891
892 // Call startup functions for the modules
893 CALL_ALL_MODULES(pfServerStarted, ());
894
895 #if XMPP_SUPPORTED
896 if (ConfigReadInt(_T("EnableXMPPConnector"), 1))
897 {
898 StartXMPPConnector();
899 }
900 #endif
901
902 #if WITH_ZMQ
903 StartZMQConnector();
904 #endif
905
906 g_flags |= AF_SERVER_INITIALIZED;
907 DbgPrintf(1, _T("Server initialization completed"));
908 return TRUE;
909 }
910
911 /**
912 * Server shutdown
913 */
914 void NXCORE_EXPORTABLE Shutdown()
915 {
916 // Notify clients
917 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
918
919 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
920 g_flags |= AF_SHUTDOWN; // Set shutdown flag
921 ConditionSet(m_condShutdown);
922
923 CloseTaskScheduler();
924
925 // Stop DCI cache loading thread
926 g_dciCacheLoaderQueue.setShutdownMode();
927
928 #if XMPP_SUPPORTED
929 StopXMPPConnector();
930 #endif
931
932 #if WITH_ZMQ
933 StopZMQConnector();
934 #endif
935
936 g_pEventQueue->clear();
937 g_pEventQueue->put(INVALID_POINTER_VALUE);
938
939 ShutdownMailer();
940 ShutdownSMSSender();
941
942 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
943 DbgPrintf(2, _T("All threads was notified, continue with shutdown"));
944
945 StopSyslogServer();
946 StopHouseKeeper();
947
948 // Wait for critical threads
949 ThreadJoin(m_thPollManager);
950 ThreadJoin(m_thSyncer);
951
952 // Call shutdown functions for the modules
953 // CALL_ALL_MODULES cannot be used here because it checks for shutdown flag
954 for(UINT32 i = 0; i < g_dwNumModules; i++)
955 {
956 if (g_pModuleList[i].pfShutdown != NULL)
957 g_pModuleList[i].pfShutdown();
958 }
959
960 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
961 SaveObjects(hdb);
962 DbgPrintf(2, _T("All objects saved to database"));
963 SaveUsers(hdb);
964 DbgPrintf(2, _T("All users saved to database"));
965 DBConnectionPoolReleaseConnection(hdb);
966
967 StopDBWriter();
968 DbgPrintf(1, _T("Database writer stopped"));
969
970 CleanupUsers();
971
972 // Remove database lock
973 UnlockDB();
974
975 DBConnectionPoolShutdown();
976 DBUnloadDriver(g_dbDriver);
977 DbgPrintf(1, _T("Database driver unloaded"));
978
979 CleanupActions();
980 ShutdownEventSubsystem();
981 ShutdownAlarmManager();
982 DbgPrintf(1, _T("Event processing stopped"));
983
984 ThreadPoolDestroy(g_agentConnectionThreadPool);
985 ThreadPoolDestroy(g_mainThreadPool);
986 MsgWaitQueue::shutdown();
987
988 delete g_pScriptLibrary;
989
990 DbgPrintf(1, _T("Server shutdown complete"));
991 nxlog_close();
992
993 // Remove PID file
994 #ifndef _WIN32
995 _tremove(g_szPIDFile);
996 #endif
997
998 // Terminate process
999 #ifdef _WIN32
1000 if (!(g_flags & AF_DAEMON))
1001 ExitProcess(0);
1002 #else
1003 exit(0);
1004 #endif
1005 }
1006
1007 /**
1008 * Fast server shutdown - normally called only by Windows service on system shutdown
1009 */
1010 void NXCORE_EXPORTABLE FastShutdown()
1011 {
1012 DbgPrintf(1, _T("Using fast shutdown procedure"));
1013
1014 g_flags |= AF_SHUTDOWN; // Set shutdown flag
1015 ConditionSet(m_condShutdown);
1016
1017 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1018 SaveObjects(hdb);
1019 DbgPrintf(2, _T("All objects saved to database"));
1020 SaveUsers(hdb);
1021 DbgPrintf(2, _T("All users saved to database"));
1022 DBConnectionPoolReleaseConnection(hdb);
1023
1024 // Remove database lock first, because we have a chance to lose DB connection
1025 UnlockDB();
1026
1027 // Stop database writers
1028 StopDBWriter();
1029 DbgPrintf(1, _T("Database writer stopped"));
1030
1031 DbgPrintf(1, _T("Server shutdown complete"));
1032 nxlog_close();
1033 }
1034
1035 /**
1036 * Compare given string to command template with abbreviation possibility
1037 */
1038 static bool IsCommand(const TCHAR *cmdTemplate, TCHAR *pszString, int iMinChars)
1039 {
1040 int i;
1041
1042 // Convert given string to uppercase
1043 _tcsupr(pszString);
1044
1045 for(i = 0; pszString[i] != 0; i++)
1046 if (pszString[i] != cmdTemplate[i])
1047 return false;
1048 if (i < iMinChars)
1049 return false;
1050 return true;
1051 }
1052
1053 /**
1054 * Dump index callback (by IP address)
1055 */
1056 static void DumpIndexCallbackByInetAddr(const InetAddress& addr, NetObj *object, void *data)
1057 {
1058 TCHAR buffer[64];
1059 ConsolePrintf((CONSOLE_CTX)data, _T("%-40s %p %s [%d]\n"), addr.toString(buffer), object, object->getName(), (int)object->getId());
1060 }
1061
1062 /**
1063 * Dump index (by IP address)
1064 */
1065 static void DumpIndex(CONSOLE_CTX pCtx, InetAddressIndex *index)
1066 {
1067 index->forEach(DumpIndexCallbackByInetAddr, pCtx);
1068 }
1069
1070 /**
1071 * Dump index callback (by ID)
1072 */
1073 static void DumpIndexCallbackById(NetObj *object, void *data)
1074 {
1075 ConsolePrintf((CONSOLE_CTX)data, _T("%08X %p %s\n"), object->getId(), object, object->getName());
1076 }
1077
1078 /**
1079 * Dump index (by ID)
1080 */
1081 static void DumpIndex(CONSOLE_CTX pCtx, ObjectIndex *index)
1082 {
1083 index->forEach(DumpIndexCallbackById, pCtx);
1084 }
1085
1086 /**
1087 * Process command entered from command line in standalone mode
1088 * Return TRUE if command was _T("down")
1089 */
1090 int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
1091 {
1092 const TCHAR *pArg;
1093 TCHAR szBuffer[256], *eptr;
1094 int nExitCode = CMD_EXIT_CONTINUE;
1095
1096 // Get command
1097 pArg = ExtractWord(pszCmdLine, szBuffer);
1098
1099 if (IsCommand(_T("AT"), szBuffer, 2))
1100 {
1101 pArg = ExtractWord(pArg, szBuffer);
1102 if (szBuffer[0] == _T('+'))
1103 {
1104 int offset = _tcstoul(&szBuffer[1], NULL, 0);
1105 AddOneTimeScheduledTask(_T("Execute.Script"), time(NULL) + offset, pArg, 0, 0, SYSTEM_ACCESS_FULL);//TODO: change to correct user
1106 }
1107 else
1108 {
1109 AddScheduledTask(_T("Execute.Script"), szBuffer, pArg, 0, 0, SYSTEM_ACCESS_FULL); //TODO: change to correct user
1110 }
1111 }
1112 if (IsCommand(_T("DEBUG"), szBuffer, 2))
1113 {
1114 // Get argument
1115 pArg = ExtractWord(pArg, szBuffer);
1116 int level = (int)_tcstol(szBuffer, &eptr, 0);
1117 if ((*eptr == 0) && (level >= 0) && (level <= 9))
1118 {
1119 g_debugLevel = (UINT32)level;
1120 ConsolePrintf(pCtx, (level == 0) ? _T("Debug mode turned off\n") : _T("Debug level set to %d\n"), level);
1121 }
1122 else if (IsCommand(_T("OFF"), szBuffer, 2))
1123 {
1124 g_debugLevel = 0;
1125 ConsoleWrite(pCtx, _T("Debug mode turned off\n"));
1126 }
1127 else
1128 {
1129 if (szBuffer[0] == 0)
1130 ConsoleWrite(pCtx, _T("ERROR: Missing argument\n\n"));
1131 else
1132 ConsoleWrite(pCtx, _T("ERROR: Invalid debug level\n\n"));
1133 }
1134 }
1135 else if (IsCommand(_T("DOWN"), szBuffer, 4))
1136 {
1137 ConsoleWrite(pCtx, _T("Proceeding with server shutdown...\n"));
1138 nExitCode = CMD_EXIT_SHUTDOWN;
1139 }
1140 else if (IsCommand(_T("DUMP"), szBuffer, 4))
1141 {
1142 DumpProcess(pCtx);
1143 }
1144 else if (IsCommand(_T("GET"), szBuffer, 3))
1145 {
1146 pArg = ExtractWord(pArg, szBuffer);
1147 if (szBuffer[0] != 0)
1148 {
1149 TCHAR value[MAX_CONFIG_VALUE];
1150 ConfigReadStr(szBuffer, value, MAX_CONFIG_VALUE, _T(""));
1151 ConsolePrintf(pCtx, _T("%s = %s\n"), szBuffer, value);
1152 }
1153 else
1154 {
1155 ConsoleWrite(pCtx, _T("Variable name missing\n"));
1156 }
1157 }
1158 else if (IsCommand(_T("HKRUN"), szBuffer, 2))
1159 {
1160 ConsoleWrite(pCtx, _T("Starting housekeeper\n"));
1161 RunHouseKeeper();
1162 }
1163 else if (IsCommand(_T("LDAPSYNC"), szBuffer, 4))
1164 {
1165 LDAPConnection conn;
1166 conn.syncUsers();
1167 }
1168 else if (IsCommand(_T("LOG"), szBuffer, 3))
1169 {
1170 while(_istspace(*pArg))
1171 pArg++;
1172 if (*pArg != 0)
1173 DbgPrintf(0, _T("%s"), pArg);
1174 }
1175 else if (IsCommand(_T("LOGMARK"), szBuffer, 4))
1176 {
1177 DbgPrintf(0, _T("******* MARK *******"));
1178 }
1179 else if (IsCommand(_T("RAISE"), szBuffer, 5))
1180 {
1181 // Get argument
1182 pArg = ExtractWord(pArg, szBuffer);
1183
1184 if (IsCommand(_T("ACCESS"), szBuffer, 6))
1185 {
1186 ConsoleWrite(pCtx, _T("Raising exception...\n"));
1187 char *p = NULL;
1188 *p = 0;
1189 }
1190 else if (IsCommand(_T("BREAKPOINT"), szBuffer, 5))
1191 {
1192 #ifdef _WIN32
1193 ConsoleWrite(pCtx, _T("Raising exception...\n"));
1194 RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
1195 #else
1196 ConsoleWrite(pCtx, _T("ERROR: Not supported on current platform\n"));
1197 #endif
1198 }
1199 else
1200 {
1201 ConsoleWrite(pCtx, _T("Invalid exception name; possible names are:\nACCESS BREAKPOINT\n"));
1202 }
1203 }
1204 else if (IsCommand(_T("EXIT"), szBuffer, 4))
1205 {
1206 if ((pCtx->hSocket != -1) || (pCtx->session != NULL))
1207 {
1208 ConsoleWrite(pCtx, _T("Closing session...\n"));
1209 nExitCode = CMD_EXIT_CLOSE_SESSION;
1210 }
1211 else
1212 {
1213 ConsoleWrite(pCtx, _T("Cannot exit from local server console\n"));
1214 }
1215 }
1216 else if (IsCommand(_T("KILL"), szBuffer, 4))
1217 {
1218 pArg = ExtractWord(pArg, szBuffer);
1219 if (szBuffer[0] != 0)
1220 {
1221 int id = _tcstol(szBuffer, &eptr, 10);
1222 if (*eptr == 0)
1223 {
1224 if (KillClientSession(id))
1225 {
1226 ConsoleWrite(pCtx, _T("Session killed\n"));
1227 }
1228 else
1229 {
1230 ConsoleWrite(pCtx, _T("Invalid session ID\n"));
1231 }
1232 }
1233 else
1234 {
1235 ConsoleWrite(pCtx, _T("Invalid session ID\n"));
1236 }
1237 }
1238 else
1239 {
1240 ConsoleWrite(pCtx, _T("Session ID missing\n"));
1241 }
1242 }
1243 else if (IsCommand(_T("PING"), szBuffer, 4))
1244 {
1245 pArg = ExtractWord(pArg, szBuffer);
1246 if (szBuffer[0] != 0)
1247 {
1248 InetAddress addr = InetAddress::parse(szBuffer);
1249 if (addr.isValid())
1250 {
1251 UINT32 rtt;
1252 UINT32 rc = IcmpPing(addr, 1, 2000, &rtt, 128);
1253 switch(rc)
1254 {
1255 case ICMP_SUCCESS:
1256 ConsolePrintf(pCtx, _T("Success, RTT = %d ms\n"), (int)rtt);
1257 break;
1258 case ICMP_UNREACHEABLE:
1259 ConsolePrintf(pCtx, _T("Destination unreachable\n"));
1260 break;
1261 case ICMP_TIMEOUT:
1262 ConsolePrintf(pCtx, _T("Request timeout\n"));
1263 break;
1264 case ICMP_RAW_SOCK_FAILED:
1265 ConsolePrintf(pCtx, _T("Cannot create raw socket\n"));
1266 break;
1267 case ICMP_API_ERROR:
1268 ConsolePrintf(pCtx, _T("API error\n"));
1269 break;
1270 default:
1271 ConsolePrintf(pCtx, _T("ERROR %d\n"), (int)rc);
1272 break;
1273 }
1274 }
1275 else
1276 {
1277 ConsoleWrite(pCtx, _T("Invalid IP address\n"));
1278 }
1279 }
1280 else
1281 {
1282 ConsoleWrite(pCtx, _T("Usage: PING <address>\n"));
1283 }
1284 }
1285 else if (IsCommand(_T("POLL"), szBuffer, 2))
1286 {
1287 pArg = ExtractWord(pArg, szBuffer);
1288 if (szBuffer[0] != 0)
1289 {
1290 int pollType;
1291 if (IsCommand(_T("CONFIGURATION"), szBuffer, 1))
1292 {
1293 pollType = 1;
1294 }
1295 else if (IsCommand(_T("STATUS"), szBuffer, 1))
1296 {
1297 pollType = 2;
1298 }
1299 else if (IsCommand(_T("TOPOLOGY"), szBuffer, 1))
1300 {
1301 pollType = 3;
1302 }
1303 else
1304 {
1305 pollType = 0;
1306 }
1307
1308 if (pollType > 0)
1309 {
1310 pArg = ExtractWord(pArg, szBuffer);
1311 UINT32 id = _tcstoul(szBuffer, NULL, 0);
1312 if (id != 0)
1313 {
1314 Node *node = (Node *)FindObjectById(id, OBJECT_NODE);
1315 if (node != NULL)
1316 {
1317 switch(pollType)
1318 {
1319 case 1:
1320 node->lockForConfigurationPoll();
1321 ThreadPoolExecute(g_pollerThreadPool, node, &Node::configurationPoll, RegisterPoller(POLLER_TYPE_CONFIGURATION, node));
1322 break;
1323 case 2:
1324 node->lockForStatusPoll();
1325 ThreadPoolExecute(g_pollerThreadPool, node, &Node::statusPoll, RegisterPoller(POLLER_TYPE_STATUS, node));
1326 break;
1327 case 3:
1328 node->lockForTopologyPoll();
1329 ThreadPoolExecute(g_pollerThreadPool, node, &Node::topologyPoll, RegisterPoller(POLLER_TYPE_TOPOLOGY, node));
1330 break;
1331 }
1332 }
1333 else
1334 {
1335 ConsolePrintf(pCtx, _T("ERROR: Node with ID %d does not exist\n\n"), id);
1336 }
1337 }
1338 else
1339 {
1340 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1341 }
1342 }
1343 else
1344 {
1345 ConsoleWrite(pCtx, _T("Usage POLL [CONFIGURATION|STATUS|TOPOLOGY] <node>\n"));
1346 }
1347 }
1348 else
1349 {
1350 ConsoleWrite(pCtx, _T("Usage POLL [CONFIGURATION|STATUS|TOPOLOGY] <node>\n"));
1351 }
1352 }
1353 else if (IsCommand(_T("SET"), szBuffer, 3))
1354 {
1355 pArg = ExtractWord(pArg, szBuffer);
1356 if (szBuffer[0] != 0)
1357 {
1358 TCHAR value[256];
1359 pArg = ExtractWord(pArg, value);
1360 if (ConfigWriteStr(szBuffer, value, TRUE, TRUE, TRUE))
1361 {
1362 ConsolePrintf(pCtx, _T("Configuration variable %s updated\n"), szBuffer);
1363 }
1364 else
1365 {
1366 ConsolePrintf(pCtx, _T("ERROR: cannot update configuration variable %s\n"), szBuffer);
1367 }
1368 }
1369 else
1370 {
1371 ConsolePrintf(pCtx, _T("Variable name missing\n"));
1372 }
1373 }
1374 else if (IsCommand(_T("SHOW"), szBuffer, 2))
1375 {
1376 // Get argument
1377 pArg = ExtractWord(pArg, szBuffer);
1378
1379 if (IsCommand(_T("COMPONENTS"), szBuffer, 1))
1380 {
1381 // Get argument
1382 pArg = ExtractWord(pArg, szBuffer);
1383 UINT32 dwNode = _tcstoul(szBuffer, NULL, 0);
1384 if (dwNode != 0)
1385 {
1386 NetObj *pObject = FindObjectById(dwNode);
1387 if (pObject != NULL)
1388 {
1389 if (pObject->getObjectClass() == OBJECT_NODE)
1390 {
1391 ComponentTree *components = ((Node *)pObject)->getComponents();
1392 if (components != NULL)
1393 {
1394 components->print(pCtx);
1395 components->decRefCount();
1396 }
1397 else
1398 {
1399 ConsoleWrite(pCtx, _T("ERROR: Node does not have physical component information\n\n"));
1400 }
1401 }
1402 else
1403 {
1404 ConsoleWrite(pCtx, _T("ERROR: Object is not a node\n\n"));
1405 }
1406 }
1407 else
1408 {
1409 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1410 }
1411 }
1412 else
1413 {
1414 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1415 }
1416 }
1417 else if (IsCommand(_T("DBCP"), szBuffer, 4))
1418 {
1419 ObjectArray<PoolConnectionInfo> *list = DBConnectionPoolGetConnectionList();
1420 for(int i = 0; i < list->size(); i++)
1421 {
1422 PoolConnectionInfo *c = list->get(i);
1423 TCHAR accessTime[64];
1424 struct tm *ltm = localtime(&c->lastAccessTime);
1425 _tcsftime(accessTime, 64, _T("%d.%b.%Y %H:%M:%S"), ltm);
1426 ConsolePrintf(pCtx, _T("%p %s %hs:%d\n"), c->handle, accessTime, c->srcFile, c->srcLine);
1427 }
1428 ConsolePrintf(pCtx, _T("%d database connections in use\n\n"), list->size());
1429 delete list;
1430 }
1431 else if (IsCommand(_T("DBSTATS"), szBuffer, 3))
1432 {
1433 LIBNXDB_PERF_COUNTERS counters;
1434 DBGetPerfCounters(&counters);
1435 ConsolePrintf(pCtx, _T("SQL query counters:\n"));
1436 ConsolePrintf(pCtx, _T(" Total .......... ") INT64_FMT _T("\n"), counters.totalQueries);
1437 ConsolePrintf(pCtx, _T(" SELECT ......... ") INT64_FMT _T("\n"), counters.selectQueries);
1438 ConsolePrintf(pCtx, _T(" Non-SELECT ..... ") INT64_FMT _T("\n"), counters.nonSelectQueries);
1439 ConsolePrintf(pCtx, _T(" Long running ... ") INT64_FMT _T("\n"), counters.longRunningQueries);
1440 ConsolePrintf(pCtx, _T(" Failed ......... ") INT64_FMT _T("\n"), counters.failedQueries);
1441
1442 ConsolePrintf(pCtx, _T("Background writer requests:\n"));
1443 ConsolePrintf(pCtx, _T(" DCI data ....... ") INT64_FMT _T("\n"), g_idataWriteRequests);
1444 ConsolePrintf(pCtx, _T(" DCI raw data ... ") INT64_FMT _T("\n"), g_rawDataWriteRequests);
1445 ConsolePrintf(pCtx, _T(" Others ......... ") INT64_FMT _T("\n"), g_otherWriteRequests);
1446 }
1447 else if (IsCommand(_T("FDB"), szBuffer, 3))
1448 {
1449 // Get argument
1450 pArg = ExtractWord(pArg, szBuffer);
1451 UINT32 dwNode = _tcstoul(szBuffer, NULL, 0);
1452 if (dwNode != 0)
1453 {
1454 NetObj *pObject = FindObjectById(dwNode);
1455 if (pObject != NULL)
1456 {
1457 if (pObject->getObjectClass() == OBJECT_NODE)
1458 {
1459 ForwardingDatabase *fdb = ((Node *)pObject)->getSwitchForwardingDatabase();
1460 if (fdb != NULL)
1461 {
1462 fdb->print(pCtx, (Node *)pObject);
1463 fdb->decRefCount();
1464 }
1465 else
1466 {
1467 ConsoleWrite(pCtx, _T("ERROR: Node does not have forwarding database information\n\n"));
1468 }
1469 }
1470 else
1471 {
1472 ConsoleWrite(pCtx, _T("ERROR: Object is not a node\n\n"));
1473 }
1474 }
1475 else
1476 {
1477 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1478 }
1479 }
1480 else
1481 {
1482 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1483 }
1484 }
1485 else if (IsCommand(_T("FLAGS"), szBuffer, 1))
1486 {
1487 ConsolePrintf(pCtx, _T("Flags: 0x") UINT64X_FMT(_T("016")) _T("\n"), g_flags);
1488 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DAEMON));
1489 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_SYSLOG));
1490 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NETWORK_DISCOVERY));
1491 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ACTIVE_NETWORK_DISCOVERY));
1492 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_LOG_SQL_ERRORS));
1493 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DELETE_EMPTY_SUBNETS));
1494 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_SNMP_TRAPD));
1495 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_ZONING));
1496 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYNC_NODE_NAMES_WITH_DNS));
1497 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CHECK_TRUSTED_NODES));
1498 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NXSL_CONTAINER_FUNCS));
1499 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_FQDN_FOR_NODE_NAMES));
1500 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE));
1501 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DEBUG_CONSOLE_DISABLED));
1502 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_OBJECT_TRANSACTIONS));
1503 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_WRITE_FULL_DUMP));
1504 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_NODE_NAMES));
1505 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CATCH_EXCEPTIONS));
1506 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_HELPDESK_LINK_ACTIVE));
1507 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_LOCKED));
1508 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_CONNECTION_LOST));
1509 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_NO_NETWORK_CONNECTIVITY));
1510 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_EVENT_STORM_DETECTED));
1511 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SNMP_TRAP_DISCOVERY));
1512 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_TRAPS_FROM_UNMANAGED_NODES));
1513 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_IP_FOR_EACH_STATUS_POLL));
1514 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_PERFDATA_STORAGE_DRIVER_LOADED));
1515 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_BACKGROUND_LOG_WRITER));
1516 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CASE_INSENSITIVE_LOGINS));
1517 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_TRAP_SOURCES_IN_ALL_ZONES));
1518 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
1519 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
1520 ConsolePrintf(pCtx, _T("\n"));
1521 }
1522 else if (IsCommand(_T("HEAP"), szBuffer, 1))
1523 {
1524 TCHAR *text = GetHeapInfo();
1525 if (text != NULL)
1526 {
1527 ConsoleWrite(pCtx, text);
1528 ConsoleWrite(pCtx, _T("\n"));
1529 free(text);
1530 }
1531 else
1532 {
1533 ConsoleWrite(pCtx, _T("Error reading heap information\n"));
1534 }
1535 }
1536 else if (IsCommand(_T("INDEX"), szBuffer, 1))
1537 {
1538 // Get argument
1539 pArg = ExtractWord(pArg, szBuffer);
1540
1541 if (IsCommand(_T("CONDITION"), szBuffer, 1))
1542 {
1543 DumpIndex(pCtx, &g_idxConditionById);
1544 }
1545 else if (IsCommand(_T("ID"), szBuffer, 2))
1546 {
1547 DumpIndex(pCtx, &g_idxObjectById);
1548 }
1549 else if (IsCommand(_T("INTERFACE"), szBuffer, 2))
1550 {
1551 DumpIndex(pCtx, &g_idxInterfaceByAddr);
1552 }
1553 else if (IsCommand(_T("NODEADDR"), szBuffer, 5))
1554 {
1555 DumpIndex(pCtx, &g_idxNodeByAddr);
1556 }
1557 else if (IsCommand(_T("NODEID"), szBuffer, 5))
1558 {
1559 DumpIndex(pCtx, &g_idxNodeById);
1560 }
1561 else if (IsCommand(_T("SUBNET"), szBuffer, 1))
1562 {
1563 DumpIndex(pCtx, &g_idxSubnetByAddr);
1564 }
1565 else if (IsCommand(_T("ZONE"), szBuffer, 1))
1566 {
1567 DumpIndex(pCtx, &g_idxZoneByGUID);
1568 }
1569 else
1570 {
1571 if (szBuffer[0] == 0)
1572 ConsoleWrite(pCtx, _T("ERROR: Missing index name\n")
1573 _T("Valid names are: CONDITION, ID, INTERFACE, NODEADDR, NODEID, SUBNET, ZONE\n\n"));
1574 else
1575 ConsoleWrite(pCtx, _T("ERROR: Invalid index name\n\n"));
1576 }
1577 }
1578 else if (IsCommand(_T("MODULES"), szBuffer, 3))
1579 {
1580 ConsoleWrite(pCtx, _T("Loaded server modules:\n"));
1581 for(UINT32 i = 0; i < g_dwNumModules; i++)
1582 {
1583 ConsolePrintf(pCtx, _T(" %s\n"), g_pModuleList[i].szName);
1584 }
1585 ConsolePrintf(pCtx, _T("%d modules loaded\n"), g_dwNumModules);
1586 }
1587 else if (IsCommand(_T("MSGWQ"), szBuffer, 2))
1588 {
1589 String text = MsgWaitQueue::getDiagInfo();
1590 ConsoleWrite(pCtx, text);
1591 ConsoleWrite(pCtx, _T("\n"));
1592 }
1593 else if (IsCommand(_T("OBJECTS"), szBuffer, 1))
1594 {
1595 // Get filter
1596 pArg = ExtractWord(pArg, szBuffer);
1597 StrStrip(szBuffer);
1598 DumpObjects(pCtx, (szBuffer[0] != 0) ? szBuffer : NULL);
1599 }
1600 else if (IsCommand(_T("POLLERS"), szBuffer, 1))
1601 {
1602 ShowPollers(pCtx);
1603 }
1604 else if (IsCommand(_T("QUEUES"), szBuffer, 1))
1605 {
1606 ShowQueueStats(pCtx, &g_dataCollectionQueue, _T("Data collector"));
1607 ShowQueueStats(pCtx, &g_dciCacheLoaderQueue, _T("DCI cache loader"));
1608 ShowQueueStats(pCtx, g_dbWriterQueue, _T("Database writer"));
1609 ShowQueueStats(pCtx, g_dciDataWriterQueue, _T("Database writer (IData)"));
1610 ShowQueueStats(pCtx, g_dciRawDataWriterQueue, _T("Database writer (raw DCI values)"));
1611 ShowQueueStats(pCtx, g_pEventQueue, _T("Event processor"));
1612 ShowQueueStats(pCtx, &g_nodePollerQueue, _T("Node poller"));
1613 ShowQueueStats(pCtx, &g_syslogProcessingQueue, _T("Syslog processing"));
1614 ShowQueueStats(pCtx, &g_syslogWriteQueue, _T("Syslog writer"));
1615 ConsolePrintf(pCtx, _T("\n"));
1616 }
1617 else if (IsCommand(_T("ROUTING-TABLE"), szBuffer, 1))
1618 {
1619 UINT32 dwNode;
1620 NetObj *pObject;
1621
1622 pArg = ExtractWord(pArg, szBuffer);
1623 dwNode = _tcstoul(szBuffer, NULL, 0);
1624 if (dwNode != 0)
1625 {
1626 pObject = FindObjectById(dwNode);
1627 if (pObject != NULL)
1628 {
1629 if (pObject->getObjectClass() == OBJECT_NODE)
1630 {
1631 ROUTING_TABLE *pRT;
1632 TCHAR szIpAddr[16];
1633 int i;
1634
1635 ConsolePrintf(pCtx, _T("Routing table for node %s:\n\n"), pObject->getName());
1636 pRT = ((Node *)pObject)->getCachedRoutingTable();
1637 if (pRT != NULL)
1638 {
1639 for(i = 0; i < pRT->iNumEntries; i++)
1640 {
1641 _sntprintf(szBuffer, 256, _T("%s/%d"), IpToStr(pRT->pRoutes[i].dwDestAddr, szIpAddr),
1642 BitsInMask(pRT->pRoutes[i].dwDestMask));
1643 ConsolePrintf(pCtx, _T("%-18s %-15s %-6d %d\n"), szBuffer,
1644 IpToStr(pRT->pRoutes[i].dwNextHop, szIpAddr),
1645 pRT->pRoutes[i].dwIfIndex, pRT->pRoutes[i].dwRouteType);
1646 }
1647 ConsoleWrite(pCtx, _T("\n"));
1648 }
1649 else
1650 {
1651 ConsoleWrite(pCtx, _T("Node doesn't have cached routing table\n\n"));
1652 }
1653 }
1654 else
1655 {
1656 ConsoleWrite(pCtx, _T("ERROR: Object is not a node\n\n"));
1657 }
1658 }
1659 else
1660 {
1661 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1662 }
1663 }
1664 else
1665 {
1666 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1667 }
1668 }
1669 else if (IsCommand(_T("SESSIONS"), szBuffer, 2))
1670 {
1671 ConsoleWrite(pCtx, _T("\x1b[1mCLIENT SESSIONS\x1b[0m\n============================================================\n"));
1672 DumpClientSessions(pCtx);
1673 ConsoleWrite(pCtx, _T("\n\x1b[1mMOBILE DEVICE SESSIONS\x1b[0m\n============================================================\n"));
1674 DumpMobileDeviceSessions(pCtx);
1675 }
1676 else if (IsCommand(_T("STATS"), szBuffer, 2))
1677 {
1678 ShowServerStats(pCtx);
1679 }
1680 else if (IsCommand(_T("THREADS"), szBuffer, 2))
1681 {
1682 ShowThreadPool(pCtx, g_mainThreadPool);
1683 ShowThreadPool(pCtx, g_pollerThreadPool);
1684 ShowThreadPool(pCtx, g_schedulerThreadPool);
1685 ShowThreadPool(pCtx, g_agentConnectionThreadPool);
1686 }
1687 else if (IsCommand(_T("TOPOLOGY"), szBuffer, 1))
1688 {
1689 pArg = ExtractWord(pArg, szBuffer);
1690 UINT32 nodeId = _tcstoul(szBuffer, NULL, 0);
1691 if (nodeId != 0)
1692 {
1693 Node *node = (Node *)FindObjectById(nodeId, OBJECT_NODE);
1694 if (node != NULL)
1695 {
1696 LinkLayerNeighbors *nbs = BuildLinkLayerNeighborList(node);
1697 if (nbs != NULL)
1698 {
1699 ConsolePrintf(pCtx, _T("Proto | PtP | ifLocal | ifRemote | Peer\n")
1700 _T("--------+-----+---------+----------+------------------------------------\n"));
1701 for(int i = 0; i < nbs->size(); i++)
1702 {
1703 LL_NEIGHBOR_INFO *ni = nbs->getConnection(i);
1704 TCHAR peer[256];
1705 if (ni->objectId != 0)
1706 {
1707 NetObj *object = FindObjectById(ni->objectId);
1708 if (object != NULL)
1709 _sntprintf(peer, 256, _T("%s [%d]"), object->getName(), ni->objectId);
1710 else
1711 _sntprintf(peer, 256, _T("[%d]"), ni->objectId);
1712 }
1713 else
1714 {
1715 peer[0] = 0;
1716 }
1717 ConsolePrintf(pCtx, _T("%-7s | %c | %7d | %7d | %s\n"),
1718 GetLinkLayerProtocolName(ni->protocol), ni->isPtToPt ? _T('Y') : _T('N'), ni->ifLocal, ni->ifRemote, peer);
1719 }
1720 nbs->decRefCount();
1721 }
1722 else
1723 {
1724 ConsoleWrite(pCtx, _T("ERROR: call to BuildLinkLayerNeighborList failed\n\n"));
1725 }
1726 }
1727 else
1728 {
1729 ConsolePrintf(pCtx, _T("ERROR: Node with ID %d does not exist\n\n"), nodeId);
1730 }
1731 }
1732 else
1733 {
1734 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1735 }
1736 }
1737 else if (IsCommand(_T("USERS"), szBuffer, 1))
1738 {
1739 DumpUsers(pCtx);
1740 }
1741 else if (IsCommand(_T("VLANS"), szBuffer, 1))
1742 {
1743 UINT32 dwNode;
1744 NetObj *pObject;
1745
1746 pArg = ExtractWord(pArg, szBuffer);
1747 dwNode = _tcstoul(szBuffer, NULL, 0);
1748 if (dwNode != 0)
1749 {
1750 pObject = FindObjectById(dwNode);
1751 if (pObject != NULL)
1752 {
1753 if (pObject->getObjectClass() == OBJECT_NODE)
1754 {
1755 VlanList *vlans = ((Node *)pObject)->getVlans();
1756 if (vlans != NULL)
1757 {
1758 ConsoleWrite(pCtx, _T("\x1b[1mVLAN\x1b[0m | \x1b[1mName\x1b[0m | \x1b[1mPorts\x1b[0m\n")
1759 _T("-----+------------------+-----------------------------------------------------------------\n"));
1760 for(int i = 0; i < vlans->size(); i++)
1761 {
1762 VlanInfo *vlan = vlans->get(i);
1763 ConsolePrintf(pCtx, _T("%4d | %-16s |"), vlan->getVlanId(), vlan->getName());
1764 for(int j = 0; j < vlan->getNumPorts(); j++)
1765 ConsolePrintf(pCtx, _T(" %d.%d"), (int)(vlan->getPorts()[j] >> 16), (int)(vlan->getPorts()[j] & 0xFFFF));
1766 ConsolePrintf(pCtx, _T("\n"));
1767 }
1768 ConsolePrintf(pCtx, _T("\n"));
1769 vlans->decRefCount();
1770 }
1771 else
1772 {
1773 ConsoleWrite(pCtx, _T("\x1b[31mNode doesn't have VLAN information\x1b[0m\n\n"));
1774 }
1775 }
1776 else
1777 {
1778 ConsoleWrite(pCtx, _T("\x1b[31mERROR: Object is not a node\x1b[0m\n\n"));
1779 }
1780 }
1781 else
1782 {
1783 ConsolePrintf(pCtx, _T("\x1b[31mERROR: Object with ID %d does not exist\x1b[0m\n\n"), dwNode);
1784 }
1785 }
1786 else
1787 {
1788 ConsoleWrite(pCtx, _T("\x1b[31mERROR: Invalid or missing node ID\x1b[0m\n\n"));
1789 }
1790 }
1791 else if (IsCommand(_T("WATCHDOG"), szBuffer, 1))
1792 {
1793 WatchdogPrintStatus(pCtx);
1794 ConsoleWrite(pCtx, _T("\n"));
1795 }
1796 else
1797 {
1798 if (szBuffer[0] == 0)
1799 ConsoleWrite(pCtx, _T("ERROR: Missing subcommand\n\n"));
1800 else
1801 ConsoleWrite(pCtx, _T("ERROR: Invalid SHOW subcommand\n\n"));
1802 }
1803 }
1804 else if (IsCommand(_T("EXEC"), szBuffer, 3))
1805 {
1806 pArg = ExtractWord(pArg, szBuffer);
1807
1808 bool libraryLocked = true;
1809 bool destroyCompiledScript = false;
1810 g_pScriptLibrary->lock();
1811
1812 NXSL_Program *compiledScript = g_pScriptLibrary->findScript(szBuffer);
1813 if (compiledScript == NULL)
1814 {
1815 g_pScriptLibrary->unlock();
1816 libraryLocked = false;
1817 destroyCompiledScript = true;
1818 char *script;
1819 UINT32 fileSize;
1820 if ((script = (char *)LoadFile(szBuffer, &fileSize)) != NULL)
1821 {
1822 const int errorMsgLen = 512;
1823 TCHAR errorMsg[errorMsgLen];
1824 #ifdef UNICODE
1825 WCHAR *wscript = WideStringFromMBString(script);
1826 compiledScript = NXSLCompile(wscript, errorMsg, errorMsgLen, NULL);
1827 free(wscript);
1828 #else
1829 compiledScript = NXSLCompile(script, errorMsg, errorMsgLen, NULL);
1830 #endif
1831 free(script);
1832 if (compiledScript == NULL)
1833 {
1834 ConsolePrintf(pCtx, _T("ERROR: Script compilation error: %s\n\n"), errorMsg);
1835 }
1836 }
1837 else
1838 {
1839 ConsolePrintf(pCtx, _T("ERROR: Script \"%s\" not found\n\n"), szBuffer);
1840 }
1841 }
1842
1843 if (compiledScript != NULL)
1844 {
1845 NXSL_ServerEnv *pEnv = new NXSL_ServerEnv;
1846 pEnv->setConsole(pCtx);
1847
1848 NXSL_VM *vm = new NXSL_VM(pEnv);
1849 if (vm->load(compiledScript))
1850 {
1851 if (libraryLocked)
1852 {
1853 g_pScriptLibrary->unlock();
1854 libraryLocked = false;
1855 }
1856
1857 NXSL_Value *argv[32];
1858 int argc = 0;
1859 while(argc < 32)
1860 {
1861 pArg = ExtractWord(pArg, szBuffer);
1862 if (szBuffer[0] == 0)
1863 break;
1864 argv[argc++] = new NXSL_Value(szBuffer);
1865 }
1866
1867 if (vm->run(argc, argv))
1868 {
1869 NXSL_Value *pValue = vm->getResult();
1870 int retCode = pValue->getValueAsInt32();
1871 ConsolePrintf(pCtx, _T("INFO: Script finished with rc=%d\n\n"), retCode);
1872 }
1873 else
1874 {
1875 ConsolePrintf(pCtx, _T("ERROR: Script finished with error: %s\n\n"), vm->getErrorText());
1876 }
1877 }
1878 else
1879 {
1880 ConsolePrintf(pCtx, _T("ERROR: VM creation failed: %s\n\n"), vm->getErrorText());
1881 }
1882 delete vm;
1883 if (destroyCompiledScript)
1884 delete compiledScript;
1885 }
1886 if (libraryLocked)
1887 g_pScriptLibrary->unlock();
1888 }
1889 else if (IsCommand(_T("TRACE"), szBuffer, 1))
1890 {
1891 UINT32 dwNode1, dwNode2;
1892 NetObj *pObject1, *pObject2;
1893 NetworkPath *pTrace;
1894 TCHAR szNextHop[16];
1895 int i;
1896
1897 // Get arguments
1898 pArg = ExtractWord(pArg, szBuffer);
1899 dwNode1 = _tcstoul(szBuffer, NULL, 0);
1900
1901 pArg = ExtractWord(pArg, szBuffer);
1902 dwNode2 = _tcstoul(szBuffer, NULL, 0);
1903
1904 if ((dwNode1 != 0) && (dwNode2 != 0))
1905 {
1906 pObject1 = FindObjectById(dwNode1);
1907 if (pObject1 == NULL)
1908 {
1909 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode1);
1910 }
1911 else
1912 {
1913 pObject2 = FindObjectById(dwNode2);
1914 if (pObject2 == NULL)
1915 {
1916 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode2);
1917 }
1918 else
1919 {
1920 if ((pObject1->getObjectClass() == OBJECT_NODE) && (pObject2->getObjectClass() == OBJECT_NODE))
1921 {
1922 pTrace = TraceRoute((Node *)pObject1, (Node *)pObject2);
1923 if (pTrace != NULL)
1924 {
1925 TCHAR sourceIp[32];
1926 ConsolePrintf(pCtx, _T("Trace from %s to %s (%d hops, %s, source IP %s):\n"),
1927 pObject1->getName(), pObject2->getName(), pTrace->getHopCount(),
1928 pTrace->isComplete() ? _T("complete") : _T("incomplete"),
1929 pTrace->getSourceAddress().toString(sourceIp));
1930 for(i = 0; i < pTrace->getHopCount(); i++)
1931 {
1932 HOP_INFO *hop = pTrace->getHopInfo(i);
1933 ConsolePrintf(pCtx, _T("[%d] %s %s %s %d\n"),
1934 hop->object->getId(),
1935 hop->object->getName(),
1936 hop->nextHop.toString(szNextHop),
1937 hop->isVpn ? _T("VPN Connector ID:") : _T("Interface Index: "),
1938 hop->ifIndex);
1939 }
1940 delete pTrace;
1941 ConsolePrintf(pCtx, _T("\n"));
1942 }
1943 else
1944 {
1945 ConsoleWrite(pCtx, _T("ERROR: Call to TraceRoute() failed\n\n"));
1946 }
1947 }
1948 else
1949 {
1950 ConsoleWrite(pCtx, _T("ERROR: Object is not a node\n\n"));
1951 }
1952 }
1953 }
1954 }
1955 else
1956 {
1957 ConsoleWrite(pCtx, _T("ERROR: Invalid or missing node id(s)\n\n"));
1958 }
1959 }
1960 else if (IsCommand(_T("HELP"), szBuffer, 2) || IsCommand(_T("?"), szBuffer, 1))
1961 {
1962 ConsoleWrite(pCtx,
1963 _T("Valid commands are:\n")
1964 _T(" at +<seconds>|<schedule> <script> [<parameters>]\n")
1965 _T(" - Schedule script execution task\n")
1966 _T(" debug [<level>|off] - Set debug level (valid range is 0..9)\n")
1967 _T(" down - Shutdown NetXMS server\n")
1968 _T(" exec <script> [<params>] - Executes NXSL script from script library\n")
1969 _T(" exit - Exit from remote session\n")
1970 _T(" kill <session> - Kill client session\n")
1971 _T(" get <variable> - Get value of server configuration variable\n")
1972 _T(" help - Display this help\n")
1973 _T(" hkrun - Run housekeeper immediately\n")
1974 _T(" ldapsync - Synchronize ldap users with local user database\n")
1975 _T(" log <text> - Write given text to server log file\n")
1976 _T(" logmark - Write marker ******* MARK ******* to server log file\n")
1977 _T(" ping <address> - Send ICMP echo request to given IP address\n")
1978 _T(" poll <type> <node> - Initiate node poll\n")
1979 _T(" raise <exception> - Raise exception\n")
1980 _T(" set <variable> <value> - Set value of server configuration variable\n")
1981 _T(" show components <node> - Show physical components of given node\n")
1982 _T(" show dbcp - Show active sessions in database connection pool\n")
1983 _T(" show dbstats - Show DB library statistics\n")
1984 _T(" show fdb <node> - Show forwarding database for node\n")
1985 _T(" show flags - Show internal server flags\n")
1986 _T(" show heap - Show heap information\n")
1987 _T(" show index <index> - Show internal index\n")
1988 _T(" show modules - Show loaded server modules\n")
1989 _T(" show msgwq - Show message wait queues information\n")
1990 _T(" show objects [<filter>] - Dump network objects to screen\n")
1991 _T(" show pollers - Show poller threads state information\n")
1992 _T(" show queues - Show internal queues statistics\n")
1993 _T(" show routing-table <node> - Show cached routing table for node\n")
1994 _T(" show sessions - Show active client sessions\n")
1995 _T(" show stats - Show server statistics\n")
1996 _T(" show topology <node> - Collect and show link layer topology for node\n")
1997 _T(" show users - Show users\n")
1998 _T(" show vlans <node> - Show cached VLAN information for node\n")
1999 _T(" show watchdog - Display watchdog information\n")
2000 _T(" trace <node1> <node2> - Show network path trace between two nodes\n")
2001 _T("\nAlmost all commands can be abbreviated to 2 or 3 characters\n")
2002 _T("\n"));
2003 }
2004 else
2005 {
2006 ConsoleWrite(pCtx, _T("UNKNOWN COMMAND\n\n"));
2007 }
2008
2009 return nExitCode;
2010 }
2011
2012 /**
2013 * Signal handler for UNIX platforms
2014 */
2015 #ifndef _WIN32
2016
2017 void SignalHandlerStub(int nSignal)
2018 {
2019 // should be unused, but JIC...
2020 if (nSignal == SIGCHLD)
2021 {
2022 while (waitpid(-1, NULL, WNOHANG) > 0)
2023 ;
2024 }
2025 }
2026
2027 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
2028 {
2029 sigset_t signals;
2030 int nSignal;
2031
2032 m_signalHandlerThread = pthread_self();
2033
2034 // default for SIGCHLD: ignore
2035 signal(SIGCHLD, &SignalHandlerStub);
2036
2037 sigemptyset(&signals);
2038 sigaddset(&signals, SIGTERM);
2039 sigaddset(&signals, SIGINT);
2040 sigaddset(&signals, SIGSEGV);
2041 sigaddset(&signals, SIGCHLD);
2042 sigaddset(&signals, SIGHUP);
2043 sigaddset(&signals, SIGUSR1);
2044 sigaddset(&signals, SIGUSR2);
2045 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
2046 sigaddset(&signals, SIGPIPE);
2047 #endif
2048
2049 sigprocmask(SIG_BLOCK, &signals, NULL);
2050
2051 while(1)
2052 {
2053 if (sigwait(&signals, &nSignal) == 0)
2054 {
2055 switch(nSignal)
2056 {
2057 case SIGTERM:
2058 case SIGINT:
2059 // avoid repeat Shutdown() call
2060 if (!(g_flags & AF_SHUTDOWN))
2061 {
2062 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
2063 if (IsStandalone())
2064 Shutdown(); // will never return
2065 else
2066 ConditionSet(m_condShutdown);
2067 }
2068 break;
2069 case SIGSEGV:
2070 abort();
2071 break;
2072 case SIGCHLD:
2073 while (waitpid(-1, NULL, WNOHANG) > 0)
2074 ;
2075 break;
2076 case SIGUSR1:
2077 if (g_flags & AF_SHUTDOWN)
2078 goto stop_handler;
2079 break;
2080 default:
2081 break;
2082 }
2083 }
2084 else
2085 {
2086 ThreadSleepMs(100);
2087 }
2088 }
2089
2090 stop_handler:
2091 sigprocmask(SIG_UNBLOCK, &signals, NULL);
2092 return THREAD_OK;
2093 }
2094
2095 #endif
2096
2097 /**
2098 * Common main()
2099 */
2100 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
2101 {
2102 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
2103
2104 if (IsStandalone())
2105 {
2106 if (!(g_flags & AF_DEBUG_CONSOLE_DISABLED))
2107 {
2108 char *ptr, szCommand[256];
2109 struct __console_ctx ctx;
2110 #ifdef UNICODE
2111 WCHAR wcCommand[256];
2112 #endif
2113
2114 ctx.hSocket = -1;
2115 ctx.socketMutex = INVALID_MUTEX_HANDLE;
2116 ctx.pMsg = NULL;
2117 ctx.session = NULL;
2118 ctx.output = NULL;
2119 WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING IS_UNICODE_BUILD_STRING _T(" Ready\n")
2120 _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
2121 _T("System Console\n\n"));
2122
2123 #if USE_READLINE
2124 // Initialize readline library if we use it
2125 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
2126 #endif
2127
2128 while(1)
2129 {
2130 #if USE_READLINE
2131 ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
2132 #else
2133 WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
2134 fflush(stdout);
2135 if (fgets(szCommand, 255, stdin) == NULL)
2136 break; // Error reading stdin
2137 ptr = strchr(szCommand, '\n');
2138 if (ptr != NULL)
2139 *ptr = 0;
2140 ptr = szCommand;
2141 #endif
2142
2143 if (ptr != NULL)
2144 {
2145 #ifdef UNICODE
2146 #if HAVE_MBSTOWCS
2147 mbstowcs(wcCommand, ptr, 255);
2148 #else
2149 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
2150 #endif
2151 wcCommand[255] = 0;
2152 StrStrip(wcCommand);
2153 if (wcCommand[0] != 0)
2154 {
2155 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
2156 #else
2157 StrStrip(ptr);
2158 if (*ptr != 0)
2159 {
2160 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
2161 #endif
2162 break;
2163 #if USE_READLINE
2164 add_history(ptr);
2165 #endif
2166 }
2167 #if USE_READLINE
2168 free(ptr);
2169 #endif
2170 }
2171 else
2172 {
2173 _tprintf(_T("\n"));
2174 }
2175 }
2176
2177 #if USE_READLINE
2178 free(ptr);
2179 #endif
2180 if (!(g_flags & AF_SHUTDOWN))
2181 {
2182 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
2183 Shutdown();
2184 }
2185 }
2186 else
2187 {
2188 // standalone with debug console disabled
2189 #ifdef _WIN32
2190 _tprintf(_T("Server running. Press ESC to shutdown.\n"));
2191 while(1)
2192 {
2193 if (_getch() == 27)
2194 break;
2195 }
2196 _tprintf(_T("Server shutting down...\n"));
2197 Shutdown();
2198 #else
2199 _tprintf(_T("Server running. Press Ctrl+C to shutdown.\n"));
2200 // Shutdown will be called from signal handler
2201 ConditionWait(m_condShutdown, INFINITE);
2202 #endif
2203 }
2204 }
2205 else
2206 {
2207 ConditionWait(m_condShutdown, INFINITE);
2208 // On Win32, Shutdown() will be called by service control handler
2209 #ifndef _WIN32
2210 Shutdown();
2211 #endif
2212 }
2213 return THREAD_OK;
2214 }
2215
2216 /**
2217 * Initiate server shutdown
2218 */
2219 void InitiateShutdown()
2220 {
2221 #ifdef _WIN32
2222 Shutdown();
2223 #else
2224 if (IsStandalone())
2225 {
2226 Shutdown();
2227 }
2228 else
2229 {
2230 pthread_kill(m_signalHandlerThread, SIGTERM);
2231 }
2232 #endif
2233 }
2234
2235 /**
2236 *DLL Entry point
2237 */
2238 #ifdef _WIN32
2239
2240 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
2241 {
2242 if (dwReason == DLL_PROCESS_ATTACH)
2243 DisableThreadLibraryCalls(hInstance);
2244 return TRUE;
2245 }
2246
2247 #endif