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