debug console commands handler moved to separate source file
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Raden Solutions
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: main.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24 #include <netxmsdb.h>
25 #include <netxms_mt.h>
26 #include <hdlink.h>
27
28 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE && !defined(UNICODE)
29 #include <readline/readline.h>
30 #include <readline/history.h>
31 #define USE_READLINE 1
32 #endif
33
34 #ifdef _WIN32
35 #include <errno.h>
36 #include <psapi.h>
37 #include <conio.h>
38 #define open _open
39 #define write _write
40 #define close _close
41 #else
42 #include <signal.h>
43 #include <sys/wait.h>
44 #endif
45
46 #ifdef WITH_ZMQ
47 #include "zeromq.h"
48 #endif
49
50 /**
51 * Messages generated by mc.pl (for UNIX version only)
52 */
53 #ifndef _WIN32
54 extern unsigned int g_dwNumMessages;
55 extern const TCHAR *g_szMessages[];
56 #endif
57
58 /**
59 * Shutdown reasons
60 */
61 #define SHUTDOWN_DEFAULT 0
62 #define SHUTDOWN_FROM_CONSOLE 1
63 #define SHUTDOWN_BY_SIGNAL 2
64
65 /**
66 * Externals
67 */
68 extern Queue g_dciCacheLoaderQueue;
69
70 void InitClientListeners();
71 void InitMobileDeviceListeners();
72 void InitCertificates();
73 void InitUsers();
74 void CleanupUsers();
75 void LoadPerfDataStorageDrivers();
76 void ImportLocalConfiguration();
77
78 void ExecuteScheduledScript(const ScheduledTaskParameters *param);
79 void MaintenanceModeEnter(const ScheduledTaskParameters *params);
80 void MaintenanceModeLeave(const ScheduledTaskParameters *params);
81 void ScheduleDeployPolicy(const ScheduledTaskParameters *params);
82 void ScheduleUninstallPolicy(const ScheduledTaskParameters * params);
83
84 void InitCountryList();
85 void InitCurrencyList();
86
87 #if XMPP_SUPPORTED
88 void StartXMPPConnector();
89 void StopXMPPConnector();
90 #endif
91
92 /**
93 * Syslog server control
94 */
95 void StartSyslogServer();
96 void StopSyslogServer();
97
98 /**
99 * Thread functions
100 */
101 THREAD_RESULT THREAD_CALL Syncer(void *);
102 THREAD_RESULT THREAD_CALL NodePoller(void *);
103 THREAD_RESULT THREAD_CALL PollManager(void *);
104 THREAD_RESULT THREAD_CALL EventProcessor(void *);
105 THREAD_RESULT THREAD_CALL WatchdogThread(void *);
106 THREAD_RESULT THREAD_CALL ClientListener(void *);
107 THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *);
108 THREAD_RESULT THREAD_CALL MobileDeviceListener(void *);
109 THREAD_RESULT THREAD_CALL MobileDeviceListenerIPv6(void *);
110 THREAD_RESULT THREAD_CALL ISCListener(void *);
111 THREAD_RESULT THREAD_CALL LocalAdminListener(void *);
112 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
113 THREAD_RESULT THREAD_CALL BeaconPoller(void *);
114 THREAD_RESULT THREAD_CALL JobManagerThread(void *);
115 THREAD_RESULT THREAD_CALL UptimeCalculator(void *);
116 THREAD_RESULT THREAD_CALL ReportingServerConnector(void *);
117
118 /**
119 * Global variables
120 */
121 TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = _T("{search}");
122 TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
123 UINT32 g_dwLogRotationMode = NXLOG_ROTATION_BY_SIZE;
124 UINT32 g_dwMaxLogSize = 16384 * 1024;
125 UINT32 g_dwLogHistorySize = 4;
126 TCHAR g_szDailyLogFileSuffix[64] = _T("");
127 TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
128 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
129 TCHAR NXCORE_EXPORTABLE g_szListenAddress[MAX_PATH] = _T("*");
130 #ifndef _WIN32
131 TCHAR NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = _T("/var/run/netxmsd.pid");
132 #endif
133 UINT32 g_dwDiscoveryPollingInterval;
134 UINT32 g_dwStatusPollingInterval;
135 UINT32 g_dwConfigurationPollingInterval;
136 UINT32 g_dwRoutingTableUpdateInterval;
137 UINT32 g_dwTopologyPollingInterval;
138 UINT32 g_dwConditionPollingInterval;
139 UINT32 g_instancePollingInterval;
140 UINT32 g_icmpPingSize;
141 UINT32 g_icmpPingTimeout = 1500; // ICMP ping timeout (milliseconds)
142 UINT32 g_auditFlags;
143 UINT32 g_slmPollingInterval;
144 UINT32 g_offlineDataRelevanceTime = 86400;
145 TCHAR NXCORE_EXPORTABLE g_netxmsdDataDir[MAX_PATH] = _T("");
146 TCHAR NXCORE_EXPORTABLE g_netxmsdLibDir[MAX_PATH] = _T("");
147 int g_dbSyntax = DB_SYNTAX_UNKNOWN;
148 UINT32 NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
149 UINT64 g_serverId = 0;
150 RSA *g_pServerKey = NULL;
151 time_t g_serverStartTime = 0;
152 UINT32 g_lockTimeout = 60000; // Default timeout for acquiring mutex
153 UINT32 g_agentCommandTimeout = 4000; // Default timeout for requests to agent
154 UINT32 g_thresholdRepeatInterval = 0; // Disabled by default
155 int g_requiredPolls = 1;
156 DB_DRIVER g_dbDriver = NULL;
157 ThreadPool *g_mainThreadPool = NULL;
158 INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
159
160 /**
161 * Static data
162 */
163 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
164 static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
165 static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
166 static int m_nShutdownReason = SHUTDOWN_DEFAULT;
167
168 #ifndef _WIN32
169 static pthread_t m_signalHandlerThread;
170 #endif
171
172 /**
173 * Sleep for specified number of seconds or until system shutdown arrives
174 * Function will return TRUE if shutdown event occurs
175 *
176 * @param seconds seconds to sleep
177 * @return true if server is shutting down
178 */
179 bool NXCORE_EXPORTABLE SleepAndCheckForShutdown(int seconds)
180 {
181 return ConditionWait(m_condShutdown, seconds * 1000);
182 }
183
184 /**
185 * Disconnect from database (exportable function for startup module)
186 */
187 void NXCORE_EXPORTABLE ShutdownDB()
188 {
189 DBConnectionPoolShutdown();
190 DBUnloadDriver(g_dbDriver);
191 }
192
193 /**
194 * Check data directory for existence
195 */
196 static BOOL CheckDataDir()
197 {
198 TCHAR szBuffer[MAX_PATH];
199
200 if (_tchdir(g_netxmsdDataDir) == -1)
201 {
202 nxlog_write(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_netxmsdDataDir);
203 return FALSE;
204 }
205
206 #ifdef _WIN32
207 #define MKDIR(name) _tmkdir(name)
208 #else
209 #define MKDIR(name) _tmkdir(name, 0700)
210 #endif
211
212 // Create directory for package files if it doesn't exist
213 _tcscpy(szBuffer, g_netxmsdDataDir);
214 _tcscat(szBuffer, DDIR_PACKAGES);
215 if (MKDIR(szBuffer) == -1)
216 if (errno != EEXIST)
217 {
218 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
219 return FALSE;
220 }
221
222 // Create directory for map background images if it doesn't exist
223 _tcscpy(szBuffer, g_netxmsdDataDir);
224 _tcscat(szBuffer, DDIR_BACKGROUNDS);
225 if (MKDIR(szBuffer) == -1)
226 if (errno != EEXIST)
227 {
228 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
229 return FALSE;
230 }
231
232 // Create directory for image library is if does't exists
233 _tcscpy(szBuffer, g_netxmsdDataDir);
234 _tcscat(szBuffer, DDIR_IMAGES);
235 if (MKDIR(szBuffer) == -1)
236 {
237 if (errno != EEXIST)
238 {
239 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
240 return FALSE;
241 }
242 }
243
244 // Create directory for file store if does't exists
245 _tcscpy(szBuffer, g_netxmsdDataDir);
246 _tcscat(szBuffer, DDIR_FILES);
247 if (MKDIR(szBuffer) == -1)
248 {
249 if (errno != EEXIST)
250 {
251 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
252 return FALSE;
253 }
254 }
255
256 #undef MKDIR
257
258 return TRUE;
259 }
260
261 /**
262 * Load global configuration parameters
263 */
264 static void LoadGlobalConfig()
265 {
266 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
267 g_dwStatusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
268 g_dwConfigurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
269 g_instancePollingInterval = ConfigReadInt(_T("InstancePollingInterval"), 600);
270 g_dwRoutingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
271 g_dwTopologyPollingInterval = ConfigReadInt(_T("TopologyPollingInterval"), 1800);
272 g_dwConditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
273 g_slmPollingInterval = ConfigReadInt(_T("SlmPollingInterval"), 60);
274 DCObject::m_defaultPollingInterval = ConfigReadInt(_T("DefaultDCIPollingInterval"), 60);
275 DCObject::m_defaultRetentionTime = ConfigReadInt(_T("DefaultDCIRetentionTime"), 30);
276 g_defaultAgentCacheMode = (INT16)ConfigReadInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF);
277 if ((g_defaultAgentCacheMode != AGENT_CACHE_ON) && (g_defaultAgentCacheMode != AGENT_CACHE_OFF))
278 {
279 DbgPrintf(1, _T("Invalid value %d of DefaultAgentCacheMode: reset to %d (OFF)"), g_defaultAgentCacheMode, AGENT_CACHE_OFF);
280 ConfigWriteInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF, true, true, true);
281 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
282 }
283 if (ConfigReadInt(_T("DeleteEmptySubnets"), 1))
284 g_flags |= AF_DELETE_EMPTY_SUBNETS;
285 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
286 g_flags |= AF_ENABLE_SNMP_TRAPD;
287 if (ConfigReadInt(_T("ProcessTrapsFromUnmanagedNodes"), 0))
288 g_flags |= AF_TRAPS_FROM_UNMANAGED_NODES;
289 if (ConfigReadInt(_T("EnableZoning"), 0))
290 g_flags |= AF_ENABLE_ZONING;
291 if (ConfigReadInt(_T("EnableObjectTransactions"), 0))
292 g_flags |= AF_ENABLE_OBJECT_TRANSACTIONS;
293 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
294 g_flags |= AF_ENABLE_NETWORK_DISCOVERY;
295 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
296 g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
297 if (ConfigReadInt(_T("UseSNMPTrapsForDiscovery"), 0))
298 g_flags |= AF_SNMP_TRAP_DISCOVERY;
299 if (ConfigReadInt(_T("ResolveNodeNames"), 1))
300 g_flags |= AF_RESOLVE_NODE_NAMES;
301 if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
302 g_flags |= AF_SYNC_NODE_NAMES_WITH_DNS;
303 if (ConfigReadInt(_T("CheckTrustedNodes"), 1))
304 g_flags |= AF_CHECK_TRUSTED_NODES;
305 if (ConfigReadInt(_T("EnableNXSLContainerFunctions"), 1))
306 g_flags |= AF_ENABLE_NXSL_CONTAINER_FUNCS;
307 if (ConfigReadInt(_T("UseFQDNForNodeNames"), 1))
308 g_flags |= AF_USE_FQDN_FOR_NODE_NAMES;
309 if (ConfigReadInt(_T("ApplyDCIFromTemplateToDisabledDCI"), 1))
310 g_flags |= AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE;
311 if (ConfigReadInt(_T("ResolveDNSToIPOnStatusPoll"), 0))
312 g_flags |= AF_RESOLVE_IP_FOR_EACH_STATUS_POLL;
313 if (ConfigReadInt(_T("CaseInsensitiveLoginNames"), 0))
314 g_flags |= AF_CASE_INSENSITIVE_LOGINS;
315 if (ConfigReadInt(_T("TrapSourcesInAllZones"), 0))
316 g_flags |= AF_TRAP_SOURCES_IN_ALL_ZONES;
317
318 if (g_netxmsdDataDir[0] == 0)
319 {
320 GetNetXMSDirectory(nxDirData, g_netxmsdDataDir);
321 DbgPrintf(1, _T("Data directory set to %s"), g_netxmsdDataDir);
322 }
323 else
324 {
325 DbgPrintf(1, _T("Using data directory %s"), g_netxmsdDataDir);
326 }
327
328 g_icmpPingTimeout = ConfigReadInt(_T("IcmpPingTimeout"), 1500);
329 g_icmpPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
330 g_lockTimeout = ConfigReadInt(_T("LockTimeout"), 60000);
331 g_agentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 4000);
332 g_thresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
333 g_requiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
334 g_offlineDataRelevanceTime = ConfigReadInt(_T("OfflineDataRelevanceTime"), 86400);
335
336 UINT32 snmpTimeout = ConfigReadInt(_T("SNMPRequestTimeout"), 1500);
337 SnmpSetDefaultTimeout(snmpTimeout);
338 }
339
340 /**
341 * Initialize cryptografic functions
342 */
343 static BOOL InitCryptografy()
344 {
345 #ifdef _WITH_ENCRYPTION
346 BOOL bResult = FALSE;
347
348 if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F)))
349 return FALSE;
350 nxlog_debug(4, _T("Supported ciphers: %s"), (const TCHAR *)NXCPGetSupportedCiphersAsText());
351
352 SSL_library_init();
353 SSL_load_error_strings();
354
355 TCHAR szKeyFile[MAX_PATH];
356 _tcscpy(szKeyFile, g_netxmsdDataDir);
357 _tcscat(szKeyFile, DFILE_KEYS);
358 g_pServerKey = LoadRSAKeys(szKeyFile);
359 if (g_pServerKey == NULL)
360 {
361 nxlog_debug(1, _T("Generating RSA key pair..."));
362 g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
363 if (g_pServerKey != NULL)
364 {
365 int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
366 if (fd != -1)
367 {
368 UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
369 dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
370 BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
371
372 BYTE *pBufPos = pKeyBuffer;
373 i2d_RSAPublicKey(g_pServerKey, &pBufPos);
374 i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
375 write(fd, &dwLen, sizeof(UINT32));
376 write(fd, pKeyBuffer, dwLen);
377
378 BYTE hash[SHA1_DIGEST_SIZE];
379 CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
380 write(fd, hash, SHA1_DIGEST_SIZE);
381
382 close(fd);
383 free(pKeyBuffer);
384 bResult = TRUE;
385 }
386 else
387 {
388 nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
389 }
390 }
391 else
392 {
393 nxlog_debug(0, _T("Failed to generate RSA key"));
394 }
395 }
396 else
397 {
398 bResult = TRUE;
399 }
400
401 int iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
402 if ((iPolicy < 0) || (iPolicy > 3))
403 iPolicy = 1;
404 SetAgentDEP(iPolicy);
405
406 return bResult;
407 #else
408 return TRUE;
409 #endif
410 }
411
412 /**
413 * Check if process with given PID exists and is a NetXMS server process
414 */
415 static BOOL IsNetxmsdProcess(UINT32 dwPID)
416 {
417 #ifdef _WIN32
418 HANDLE hProcess;
419 TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
420 BOOL bRet = FALSE;
421
422 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
423 if (hProcess != NULL)
424 {
425 if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
426 (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
427 {
428 bRet = !_tcsicmp(szExtModule, szIntModule);
429 }
430 else
431 {
432 // Cannot read process name, for safety assume that it's a server process
433 bRet = TRUE;
434 }
435 CloseHandle(hProcess);
436 }
437 return bRet;
438 #else
439 return (kill((pid_t)dwPID, 0) != -1);
440 #endif
441 }
442
443 /**
444 * Database event handler
445 */
446 static void DBEventHandler(DWORD dwEvent, const WCHAR *pszArg1, const WCHAR *pszArg2, bool connLost, void *userArg)
447 {
448 if (!(g_flags & AF_SERVER_INITIALIZED))
449 return; // Don't try to do anything if server is not ready yet
450
451 switch(dwEvent)
452 {
453 case DBEVENT_CONNECTION_LOST:
454 PostEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
455 g_flags |= AF_DB_CONNECTION_LOST;
456 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
457 break;
458 case DBEVENT_CONNECTION_RESTORED:
459 PostEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
460 g_flags &= ~AF_DB_CONNECTION_LOST;
461 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
462 break;
463 case DBEVENT_QUERY_FAILED:
464 PostEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "uud", pszArg1, pszArg2, connLost ? 1 : 0);
465 break;
466 default:
467 break;
468 }
469 }
470
471 /**
472 * Send console message to session with open console
473 */
474 static void SendConsoleMessage(ClientSession *session, void *arg)
475 {
476 if (session->isConsoleOpen())
477 {
478 NXCPMessage msg;
479
480 msg.setCode(CMD_ADM_MESSAGE);
481 msg.setField(VID_MESSAGE, (TCHAR *)arg);
482 session->postMessage(&msg);
483 }
484 }
485
486 /**
487 * Console writer
488 */
489 static void LogConsoleWriter(const TCHAR *format, ...)
490 {
491 TCHAR buffer[8192];
492 va_list args;
493
494 va_start(args, format);
495 _vsntprintf(buffer, 8192, format, args);
496 buffer[8191] = 0;
497 va_end(args);
498
499 WriteToTerminal(buffer);
500
501 EnumerateClientSessions(SendConsoleMessage, buffer);
502 }
503
504 /**
505 * Oracle session init callback
506 */
507 static void OracleSessionInitCallback(DB_HANDLE hdb)
508 {
509 DBQuery(hdb, _T("ALTER SESSION SET DDL_LOCK_TIMEOUT = 60"));
510 }
511
512 /**
513 * Server initialization
514 */
515 BOOL NXCORE_EXPORTABLE Initialize()
516 {
517 int i, iDBVersion;
518 TCHAR szInfo[256];
519
520 g_serverStartTime = time(NULL);
521 srand((unsigned int)g_serverStartTime);
522
523 if (!(g_flags & AF_USE_SYSLOG))
524 {
525 if (!nxlog_set_rotation_policy((int)g_dwLogRotationMode, (int)g_dwMaxLogSize, (int)g_dwLogHistorySize, g_szDailyLogFileSuffix))
526 if (!(g_flags & AF_DAEMON))
527 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
528 }
529 if (!nxlog_open((g_flags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
530 ((g_flags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
531 ((g_flags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
532 ((g_flags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
533 _T("LIBNXSRV.DLL"),
534 #ifdef _WIN32
535 0, NULL, MSG_DEBUG))
536 #else
537 g_dwNumMessages, g_szMessages, MSG_DEBUG))
538 #endif
539 {
540 _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
541 return FALSE;
542 }
543 nxlog_set_console_writer(LogConsoleWriter);
544
545 if (g_netxmsdLibDir[0] == 0)
546 {
547 GetNetXMSDirectory(nxDirLib, g_netxmsdLibDir);
548 nxlog_debug(1, _T("LIB directory set to %s"), g_netxmsdLibDir);
549 }
550
551 // Set code page
552 #ifndef _WIN32
553 if (SetDefaultCodepage(g_szCodePage))
554 {
555 DbgPrintf(1, _T("Code page set to %hs"), g_szCodePage);
556 }
557 else
558 {
559 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "m", g_szCodePage);
560 }
561 #endif
562
563 // Set process affinity mask
564 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
565 {
566 #ifdef _WIN32
567 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
568 nxlog_debug(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
569 #else
570 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
571 #endif
572 }
573
574 #ifdef _WIN32
575 WSADATA wsaData;
576 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
577 if (wrc != 0)
578 {
579 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
580 return FALSE;
581 }
582 #endif
583
584 InitLocalNetInfo();
585 SnmpSetMessageIds(MSG_OID_PARSE_ERROR, MSG_SNMP_UNKNOWN_TYPE, MSG_SNMP_GET_ERROR);
586
587 // Create queue for delayed SQL queries
588 g_dbWriterQueue = new Queue(256, 64);
589 g_dciDataWriterQueue = new Queue(1024, 1024);
590 g_dciRawDataWriterQueue = new Queue(1024, 1024);
591
592 // Initialize database driver and connect to database
593 if (!DBInit(MSG_OTHER, (g_flags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
594 return FALSE;
595 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (nxlog_get_debug_level() >= 9), DBEventHandler, NULL);
596 if (g_dbDriver == NULL)
597 return FALSE;
598
599 // Connect to database
600 DB_HANDLE hdbBootstrap = NULL;
601 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
602 for(i = 0; ; i++)
603 {
604 hdbBootstrap = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
605 if ((hdbBootstrap != NULL) || (i == 5))
606 break;
607 ThreadSleep(5);
608 }
609 if (hdbBootstrap == NULL)
610 {
611 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
612 return FALSE;
613 }
614 nxlog_debug(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
615
616 // Check database version
617 iDBVersion = DBGetSchemaVersion(hdbBootstrap);
618 if (iDBVersion != DB_FORMAT_VERSION)
619 {
620 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
621 DBDisconnect(hdbBootstrap);
622 return FALSE;
623 }
624
625 // Read database syntax
626 g_dbSyntax = DBGetSyntax(hdbBootstrap);
627 if (g_dbSyntax == DB_SYNTAX_ORACLE)
628 {
629 DBSetSessionInitCallback(OracleSessionInitCallback);
630 }
631
632 int baseSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolBaseSize"), 10);
633 int maxSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxSize"), 30);
634 int cooldownTime = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolCooldownTime"), 300);
635 int ttl = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxLifetime"), 14400);
636
637 DBDisconnect(hdbBootstrap);
638
639 if (!DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, baseSize, maxSize, cooldownTime, ttl))
640 {
641 nxlog_write(MSG_DBCONNPOOL_INIT_FAILED, EVENTLOG_ERROR_TYPE, NULL);
642 return FALSE;
643 }
644
645 UINT32 lrt = ConfigReadULong(_T("LongRunningQueryThreshold"), 0);
646 if (lrt != 0)
647 DBSetLongRunningThreshold(lrt);
648
649 // Read server ID
650 MetaDataReadStr(_T("ServerID"), szInfo, 256, _T(""));
651 StrStrip(szInfo);
652 if (szInfo[0] != 0)
653 {
654 g_serverId = _tcstoull(szInfo, NULL, 16);
655 }
656 else
657 {
658 // Generate new ID
659 g_serverId = ((UINT64)time(NULL) << 31) | (UINT64)((UINT32)rand() & 0x7FFFFFFF);
660 _sntprintf(szInfo, 256, UINT64X_FMT(_T("016")), g_serverId);
661 MetaDataWriteStr(_T("ServerID"), szInfo);
662 }
663 nxlog_debug(1, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
664
665 // Initialize locks
666 retry_db_lock:
667 InetAddress addr;
668 if (!InitLocks(&addr, szInfo))
669 {
670 if (!addr.isValid()) // Some SQL problems
671 {
672 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
673 }
674 else // Database already locked by another server instance
675 {
676 // Check for lock from crashed/terminated local process
677 if (GetLocalIpAddr().equals(addr))
678 {
679 UINT32 dwPID;
680
681 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
682 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
683 {
684 UnlockDB();
685 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
686 goto retry_db_lock;
687 }
688 }
689 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "As", &addr, szInfo);
690 }
691 return FALSE;
692 }
693 g_flags |= AF_DB_LOCKED;
694
695 // Load global configuration parameters
696 LoadGlobalConfig();
697 CASReadSettings();
698 nxlog_debug(1, _T("Global configuration loaded"));
699
700 // Check data directory
701 if (!CheckDataDir())
702 return FALSE;
703
704 // Initialize cryptografy
705 if (!InitCryptografy())
706 {
707 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
708 return FALSE;
709 }
710
711 // Initialize certificate store and CA
712 InitCertificates();
713
714 // Create synchronization stuff
715 m_condShutdown = ConditionCreate(TRUE);
716
717 // Create thread pools
718 nxlog_debug(2, _T("Creating thread pools"));
719 g_mainThreadPool = ThreadPoolCreate(8, 256, _T("MAIN"));
720 g_agentConnectionThreadPool = ThreadPoolCreate(4, 256, _T("AGENT"));
721
722 // Setup unique identifiers table
723 if (!InitIdTable())
724 return FALSE;
725 nxlog_debug(2, _T("ID table created"));
726
727 InitCountryList();
728 InitCurrencyList();
729
730 // Update status for unfinished jobs in job history
731 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
732 DBQuery(hdb, _T("UPDATE job_history SET status=4,failure_message='Aborted due to server shutdown or crash' WHERE status NOT IN (3,4,5)"));
733 DBConnectionPoolReleaseConnection(hdb);
734
735 // Load and compile scripts
736 LoadScripts();
737
738 // Initialize watchdog
739 WatchdogInit();
740
741 // Load modules
742 if (!LoadNetXMSModules())
743 return FALSE; // Mandatory module not loaded
744
745 // Initialize mailer and SMS sender
746 InitMailer();
747 InitSMSSender();
748
749 // Load users from database
750 InitUsers();
751 if (!LoadUsers())
752 {
753 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
754 return FALSE;
755 }
756 nxlog_debug(2, _T("User accounts loaded"));
757
758 // Initialize audit
759 InitAuditLog();
760
761 // Initialize event handling subsystem
762 if (!InitEventSubsystem())
763 return FALSE;
764
765 // Initialize alarms
766 if (!InitAlarmManager())
767 return FALSE;
768
769 // Initialize objects infrastructure and load objects from database
770 LoadNetworkDeviceDrivers();
771 ObjectsInit();
772 if (!LoadObjects())
773 return FALSE;
774 nxlog_debug(1, _T("Objects loaded and initialized"));
775
776 // Initialize situations
777 if (!SituationsInit())
778 return FALSE;
779 nxlog_debug(1, _T("Situations loaded and initialized"));
780
781 // Initialize and load event actions
782 if (!InitActions())
783 {
784 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
785 return FALSE;
786 }
787
788 // Initialize helpdesk link
789 SetHDLinkEntryPoints(ResolveAlarmByHDRef, TerminateAlarmByHDRef);
790 LoadHelpDeskLink();
791
792 // Initialize data collection subsystem
793 LoadPerfDataStorageDrivers();
794 if (!InitDataCollector())
795 return FALSE;
796
797 InitLogAccess();
798 FileUploadJob::init();
799 InitMappingTables();
800
801 InitClientListeners();
802 if (ConfigReadInt(_T("ImportConfigurationOnStartup"), 1))
803 ImportLocalConfiguration();
804
805 // Check if management node object presented in database
806 CheckForMgmtNode();
807 if (g_dwMgmtNode == 0)
808 {
809 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
810 return FALSE;
811 }
812
813 // Start threads
814 ThreadCreate(WatchdogThread, 0, NULL);
815 ThreadCreate(NodePoller, 0, NULL);
816 ThreadCreate(JobManagerThread, 0, NULL);
817 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
818 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
819
820 StartHouseKeeper();
821
822 // Start event processor
823 ThreadCreate(EventProcessor, 0, NULL);
824
825 // Start SNMP trapper
826 InitTraps();
827 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
828 ThreadCreate(SNMPTrapReceiver, 0, NULL);
829
830 // Start built-in syslog daemon
831 if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
832 StartSyslogServer();
833
834 // Start database _T("lazy") write thread
835 StartDBWriter();
836
837 // Start local administartive interface listener if required
838 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
839 ThreadCreate(LocalAdminListener, 0, NULL);
840
841 // Start beacon host poller
842 ThreadCreate(BeaconPoller, 0, NULL);
843
844 // Start inter-server communication listener
845 if (ConfigReadInt(_T("EnableISCListener"), 0))
846 ThreadCreate(ISCListener, 0, NULL);
847
848 // Start reporting server connector
849 if (ConfigReadInt(_T("EnableReportingServer"), 0))
850 ThreadCreate(ReportingServerConnector, 0, NULL);
851
852 //Start ldap synchronization
853 if (ConfigReadInt(_T("LdapSyncInterval"), 0))
854 ThreadCreate(SyncLDAPUsers, 0, NULL);
855
856 RegisterSchedulerTaskHandler(_T("Execute.Script"), ExecuteScheduledScript, SYSTEM_ACCESS_SCHEDULE_SCRIPT);
857 RegisterSchedulerTaskHandler(_T("Maintenance.Enter"), MaintenanceModeEnter, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
858 RegisterSchedulerTaskHandler(_T("Maintenance.Leave"), MaintenanceModeLeave, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
859 RegisterSchedulerTaskHandler(_T("Policy.Deploy"), ScheduleDeployPolicy, 0); //No access right beacause it will be used only by server
860 RegisterSchedulerTaskHandler(_T("Policy.Uninstall"), ScheduleUninstallPolicy, 0); //No access right beacause it will be used only by server
861 InitializeTaskScheduler();
862
863 // Allow clients to connect
864 ThreadCreate(ClientListener, 0, NULL);
865 #ifdef WITH_IPV6
866 ThreadCreate(ClientListenerIPv6, 0, NULL);
867 #endif
868
869 // Allow mobile devices to connect
870 InitMobileDeviceListeners();
871 ThreadCreate(MobileDeviceListener, 0, NULL);
872 #ifdef WITH_IPV6
873 ThreadCreate(MobileDeviceListenerIPv6, 0, NULL);
874 #endif
875
876 // Start uptime calculator for SLM
877 ThreadCreate(UptimeCalculator, 0, NULL);
878
879 nxlog_debug(2, _T("LIBDIR: %s"), g_netxmsdLibDir);
880
881 // Call startup functions for the modules
882 CALL_ALL_MODULES(pfServerStarted, ());
883
884 #if XMPP_SUPPORTED
885 if (ConfigReadInt(_T("EnableXMPPConnector"), 1))
886 {
887 StartXMPPConnector();
888 }
889 #endif
890
891 #if WITH_ZMQ
892 StartZMQConnector();
893 #endif
894
895 g_flags |= AF_SERVER_INITIALIZED;
896 nxlog_debug(1, _T("Server initialization completed"));
897 return TRUE;
898 }
899
900 /**
901 * Server shutdown
902 */
903 void NXCORE_EXPORTABLE Shutdown()
904 {
905 // Notify clients
906 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
907
908 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
909 g_flags |= AF_SHUTDOWN; // Set shutdown flag
910 ConditionSet(m_condShutdown);
911
912 CloseTaskScheduler();
913
914 // Stop DCI cache loading thread
915 g_dciCacheLoaderQueue.setShutdownMode();
916
917 #if XMPP_SUPPORTED
918 StopXMPPConnector();
919 #endif
920
921 #if WITH_ZMQ
922 StopZMQConnector();
923 #endif
924
925 g_pEventQueue->clear();
926 g_pEventQueue->put(INVALID_POINTER_VALUE);
927
928 ShutdownMailer();
929 ShutdownSMSSender();
930
931 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
932 nxlog_debug(2, _T("All threads was notified, continue with shutdown"));
933
934 StopSyslogServer();
935 StopHouseKeeper();
936
937 // Wait for critical threads
938 ThreadJoin(m_thPollManager);
939 ThreadJoin(m_thSyncer);
940
941 // Call shutdown functions for the modules
942 // CALL_ALL_MODULES cannot be used here because it checks for shutdown flag
943 for(UINT32 i = 0; i < g_dwNumModules; i++)
944 {
945 if (g_pModuleList[i].pfShutdown != NULL)
946 g_pModuleList[i].pfShutdown();
947 }
948
949 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
950 SaveObjects(hdb);
951 nxlog_debug(2, _T("All objects saved to database"));
952 SaveUsers(hdb);
953 nxlog_debug(2, _T("All users saved to database"));
954 DBConnectionPoolReleaseConnection(hdb);
955
956 StopDBWriter();
957 nxlog_debug(1, _T("Database writer stopped"));
958
959 CleanupUsers();
960
961 // Remove database lock
962 UnlockDB();
963
964 DBConnectionPoolShutdown();
965 DBUnloadDriver(g_dbDriver);
966 nxlog_debug(1, _T("Database driver unloaded"));
967
968 CleanupActions();
969 ShutdownEventSubsystem();
970 ShutdownAlarmManager();
971 nxlog_debug(1, _T("Event processing stopped"));
972
973 ThreadPoolDestroy(g_agentConnectionThreadPool);
974 ThreadPoolDestroy(g_mainThreadPool);
975 MsgWaitQueue::shutdown();
976
977 delete g_pScriptLibrary;
978
979 nxlog_debug(1, _T("Server shutdown complete"));
980 nxlog_close();
981
982 // Remove PID file
983 #ifndef _WIN32
984 _tremove(g_szPIDFile);
985 #endif
986
987 // Terminate process
988 #ifdef _WIN32
989 if (!(g_flags & AF_DAEMON))
990 ExitProcess(0);
991 #else
992 exit(0);
993 #endif
994 }
995
996 /**
997 * Fast server shutdown - normally called only by Windows service on system shutdown
998 */
999 void NXCORE_EXPORTABLE FastShutdown()
1000 {
1001 DbgPrintf(1, _T("Using fast shutdown procedure"));
1002
1003 g_flags |= AF_SHUTDOWN; // Set shutdown flag
1004 ConditionSet(m_condShutdown);
1005
1006 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1007 SaveObjects(hdb);
1008 DbgPrintf(2, _T("All objects saved to database"));
1009 SaveUsers(hdb);
1010 DbgPrintf(2, _T("All users saved to database"));
1011 DBConnectionPoolReleaseConnection(hdb);
1012
1013 // Remove database lock first, because we have a chance to lose DB connection
1014 UnlockDB();
1015
1016 // Stop database writers
1017 StopDBWriter();
1018 DbgPrintf(1, _T("Database writer stopped"));
1019
1020 DbgPrintf(1, _T("Server shutdown complete"));
1021 nxlog_close();
1022 }
1023
1024 /**
1025 * Signal handler for UNIX platforms
1026 */
1027 #ifndef _WIN32
1028
1029 void SignalHandlerStub(int nSignal)
1030 {
1031 // should be unused, but JIC...
1032 if (nSignal == SIGCHLD)
1033 {
1034 while (waitpid(-1, NULL, WNOHANG) > 0)
1035 ;
1036 }
1037 }
1038
1039 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1040 {
1041 sigset_t signals;
1042 int nSignal;
1043
1044 m_signalHandlerThread = pthread_self();
1045
1046 // default for SIGCHLD: ignore
1047 signal(SIGCHLD, &SignalHandlerStub);
1048
1049 sigemptyset(&signals);
1050 sigaddset(&signals, SIGTERM);
1051 sigaddset(&signals, SIGINT);
1052 sigaddset(&signals, SIGSEGV);
1053 sigaddset(&signals, SIGCHLD);
1054 sigaddset(&signals, SIGHUP);
1055 sigaddset(&signals, SIGUSR1);
1056 sigaddset(&signals, SIGUSR2);
1057 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
1058 sigaddset(&signals, SIGPIPE);
1059 #endif
1060
1061 sigprocmask(SIG_BLOCK, &signals, NULL);
1062
1063 while(1)
1064 {
1065 if (sigwait(&signals, &nSignal) == 0)
1066 {
1067 switch(nSignal)
1068 {
1069 case SIGTERM:
1070 case SIGINT:
1071 // avoid repeat Shutdown() call
1072 if (!(g_flags & AF_SHUTDOWN))
1073 {
1074 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1075 if (IsStandalone())
1076 Shutdown(); // will never return
1077 else
1078 ConditionSet(m_condShutdown);
1079 }
1080 break;
1081 case SIGSEGV:
1082 abort();
1083 break;
1084 case SIGCHLD:
1085 while (waitpid(-1, NULL, WNOHANG) > 0)
1086 ;
1087 break;
1088 case SIGUSR1:
1089 if (g_flags & AF_SHUTDOWN)
1090 goto stop_handler;
1091 break;
1092 default:
1093 break;
1094 }
1095 }
1096 else
1097 {
1098 ThreadSleepMs(100);
1099 }
1100 }
1101
1102 stop_handler:
1103 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1104 return THREAD_OK;
1105 }
1106
1107 #endif
1108
1109 /**
1110 * Common main()
1111 */
1112 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1113 {
1114 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1115
1116 if (IsStandalone())
1117 {
1118 if (!(g_flags & AF_DEBUG_CONSOLE_DISABLED))
1119 {
1120 char *ptr, szCommand[256];
1121 struct __console_ctx ctx;
1122 #ifdef UNICODE
1123 WCHAR wcCommand[256];
1124 #endif
1125
1126 ctx.hSocket = -1;
1127 ctx.socketMutex = INVALID_MUTEX_HANDLE;
1128 ctx.pMsg = NULL;
1129 ctx.session = NULL;
1130 ctx.output = NULL;
1131 WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING IS_UNICODE_BUILD_STRING _T(" Ready\n")
1132 _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
1133 _T("System Console\n\n"));
1134
1135 #if USE_READLINE
1136 // Initialize readline library if we use it
1137 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
1138 #endif
1139
1140 while(1)
1141 {
1142 #if USE_READLINE
1143 ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
1144 #else
1145 WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
1146 fflush(stdout);
1147 if (fgets(szCommand, 255, stdin) == NULL)
1148 break; // Error reading stdin
1149 ptr = strchr(szCommand, '\n');
1150 if (ptr != NULL)
1151 *ptr = 0;
1152 ptr = szCommand;
1153 #endif
1154
1155 if (ptr != NULL)
1156 {
1157 #ifdef UNICODE
1158 #if HAVE_MBSTOWCS
1159 mbstowcs(wcCommand, ptr, 255);
1160 #else
1161 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
1162 #endif
1163 wcCommand[255] = 0;
1164 StrStrip(wcCommand);
1165 if (wcCommand[0] != 0)
1166 {
1167 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
1168 #else
1169 StrStrip(ptr);
1170 if (*ptr != 0)
1171 {
1172 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
1173 #endif
1174 break;
1175 #if USE_READLINE
1176 add_history(ptr);
1177 #endif
1178 }
1179 #if USE_READLINE
1180 free(ptr);
1181 #endif
1182 }
1183 else
1184 {
1185 _tprintf(_T("\n"));
1186 }
1187 }
1188
1189 #if USE_READLINE
1190 free(ptr);
1191 #endif
1192 if (!(g_flags & AF_SHUTDOWN))
1193 {
1194 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1195 Shutdown();
1196 }
1197 }
1198 else
1199 {
1200 // standalone with debug console disabled
1201 #ifdef _WIN32
1202 _tprintf(_T("Server running. Press ESC to shutdown.\n"));
1203 while(1)
1204 {
1205 if (_getch() == 27)
1206 break;
1207 }
1208 _tprintf(_T("Server shutting down...\n"));
1209 Shutdown();
1210 #else
1211 _tprintf(_T("Server running. Press Ctrl+C to shutdown.\n"));
1212 // Shutdown will be called from signal handler
1213 ConditionWait(m_condShutdown, INFINITE);
1214 #endif
1215 }
1216 }
1217 else
1218 {
1219 ConditionWait(m_condShutdown, INFINITE);
1220 // On Win32, Shutdown() will be called by service control handler
1221 #ifndef _WIN32
1222 Shutdown();
1223 #endif
1224 }
1225 return THREAD_OK;
1226 }
1227
1228 /**
1229 * Initiate server shutdown
1230 */
1231 void InitiateShutdown()
1232 {
1233 #ifdef _WIN32
1234 Shutdown();
1235 #else
1236 if (IsStandalone())
1237 {
1238 Shutdown();
1239 }
1240 else
1241 {
1242 pthread_kill(m_signalHandlerThread, SIGTERM);
1243 }
1244 #endif
1245 }
1246
1247 /**
1248 *DLL Entry point
1249 */
1250 #ifdef _WIN32
1251
1252 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1253 {
1254 if (dwReason == DLL_PROCESS_ATTACH)
1255 DisableThreadLibraryCalls(hInstance);
1256 return TRUE;
1257 }
1258
1259 #endif