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