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