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