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