Changelog update
[public/netxms.git] / src / server / netxmsd / netxmsd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** Server startup module
3adc64ec 4** Copyright (C) 2003-2017 Raden Solutions
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: netxmsd.cpp
21**
22**/
23
24#include "netxmsd.h"
25
26#if HAVE_GETOPT_H
27#include <getopt.h>
28#endif
29
30#ifdef _WIN32
31#include <dbghelp.h>
32#endif
33
d27e7413 34#if defined(_WITH_ENCRYPTION) && defined(_WIN32)
958a9397
VK
35#include <openssl/applink.c>
36#endif
37
f375019e
VK
38/**
39 * Global data
40 */
5039dede
AK
41BOOL g_bCheckDB = FALSE;
42
f375019e 43/**
2df047f4
VK
44 * Debug level
45 */
46static int s_debugLevel = NXCONFIG_UNINITIALIZED_VALUE;
b41dff2a 47static TCHAR *s_debugTags = NULL;
2df047f4
VK
48
49/**
f375019e
VK
50 * Help text
51 */
f62b7eab 52static TCHAR help_text[] = _T("NetXMS Server Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n")
43a6f3ca 53 _T("Copyright (c) 2003-2015 Raden Solutions\n\n")
e0f99bf0
VK
54 _T("Usage: netxmsd [<options>]\n\n")
55 _T("Valid options are:\n")
56 _T(" -e : Run database check on startup\n")
57 _T(" -c <file> : Set non-default configuration file\n")
e0f99bf0
VK
58 _T(" -d : Run as daemon/service\n")
59 _T(" -D <level> : Set debug level (valid levels are 0..9)\n")
60 _T(" -h : Display help and exit\n")
5039dede 61#ifdef _WIN32
e0f99bf0
VK
62 _T(" -I : Install Windows service\n")
63 _T(" -L <user> : Login name for service account.\n")
64 _T(" -P <passwd> : Password for service account.\n")
5039dede 65#else
e0f99bf0 66 _T(" -p <file> : Specify pid file.\n")
5039dede 67#endif
7bdb43c3 68 _T(" -q : Disable interactive console\n")
5039dede 69#ifdef _WIN32
e0f99bf0
VK
70 _T(" -R : Remove Windows service\n")
71 _T(" -s : Start Windows service\n")
72 _T(" -S : Stop Windows service\n")
5039dede 73#endif
e0f99bf0
VK
74 _T(" -v : Display version and exit\n")
75 _T("\n");
5039dede 76
f375019e
VK
77/**
78 * Execute command and wait
79 */
e0f99bf0 80static BOOL ExecAndWait(char *pszCommand)
5039dede
AK
81{
82 BOOL bSuccess = TRUE;
83
84#ifdef _WIN32
e0f99bf0 85 STARTUPINFOA si;
5039dede
AK
86 PROCESS_INFORMATION pi;
87
88 // Fill in process startup info structure
977e7d9d
VK
89 memset(&si, 0, sizeof(STARTUPINFOA));
90 si.cb = sizeof(STARTUPINFOA);
5039dede
AK
91 si.dwFlags = 0;
92
93 // Create new process
e0f99bf0
VK
94 if (!CreateProcessA(NULL, pszCommand, NULL, NULL, FALSE,
95 IsStandalone() ? 0 : CREATE_NO_WINDOW | DETACHED_PROCESS,
96 NULL, NULL, &si, &pi))
5039dede
AK
97 {
98 bSuccess = FALSE;
99 }
100 else
101 {
102 WaitForSingleObject(pi.hProcess, INFINITE);
103
104 // Close all handles
105 CloseHandle(pi.hThread);
106 CloseHandle(pi.hProcess);
107 }
108#else
109 bSuccess = (system(pszCommand) != -1);
110#endif
111
112 return bSuccess;
113}
114
5039dede
AK
115#ifdef _WIN32
116
2d803d58
VK
117/**
118* Create minidump of given process
119*/
5039dede
AK
120static void CreateMiniDump(DWORD pid)
121{
122 HANDLE hFile, hProcess;
123 TCHAR error[256];
124
e0f99bf0 125 _tprintf(_T("INFO: Starting minidump for process %d\n"), pid);
5039dede
AK
126 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
127 if (hProcess != NULL)
128 {
3af85b5e 129 TCHAR fname[MAX_PATH];
2d803d58 130 _sntprintf(fname, MAX_PATH, _T("%s\\netxmsd-%u-%I64u.mdmp"), g_szDumpDir, pid, static_cast<UINT64>(time(NULL)));
3af85b5e 131 hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL,
5039dede
AK
132 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
133 if (hFile != INVALID_HANDLE_VALUE)
134 {
db63f792 135 static const char *comments = "Version=" NETXMS_VERSION_STRING_A "; BuildTag=" NETXMS_BUILD_TAG_A;
3af85b5e 136 MINIDUMP_USER_STREAM us;
db63f792 137 us.Type = CommentStreamA;
3af85b5e 138 us.Buffer = (void*)comments;
db63f792 139 us.BufferSize = static_cast<ULONG>(strlen(comments) + 1);
3af85b5e
VK
140
141 MINIDUMP_USER_STREAM_INFORMATION usi;
142 usi.UserStreamCount = 1;
143 usi.UserStreamArray = &us;
144
8f413137
VK
145 if (MiniDumpWriteDump(hProcess, pid, hFile,
146 static_cast<MINIDUMP_TYPE>(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithProcessThreadData),
147 NULL, &usi, NULL))
148 {
149 CloseHandle(hFile);
150 if (DeflateFile(fname))
151 DeleteFile(fname);
152 _tprintf(_T("INFO: Minidump created successfully\n"));
153 }
154 else
155 {
156 CloseHandle(hFile);
157 DeleteFile(fname);
158 _tprintf(_T("INFO: Minidump creation failed\n"));
159 }
5039dede
AK
160 }
161 else
162 {
e0f99bf0 163 _tprintf(_T("ERROR: cannot create file for minidump (%s)\n"), GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
164 }
165 }
166 else
167 {
e0f99bf0 168 _tprintf(_T("ERROR: cannot open process %d (%s)\n"), pid, GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
169 }
170}
171
172#endif
173
5039dede 174#ifdef _WIN32
7bdb43c3 175#define VALID_OPTIONS "c:CdD:ehIL:P:qRsSv"
5039dede 176#else
7bdb43c3 177#define VALID_OPTIONS "c:CdD:ehp:qv"
5039dede
AK
178#endif
179
d78be64d
VK
180/**
181 * Parse command line
182 * Returns TRUE on success and FALSE on failure
183 */
5039dede
AK
184static BOOL ParseCommandLine(int argc, char *argv[])
185{
186 int ch;
e0f99bf0 187 char *eptr;
5039dede 188#ifdef _WIN32
e0f99bf0
VK
189 TCHAR login[256] = _T(""), password[256] = _T("");
190 char exePath[MAX_PATH], dllPath[MAX_PATH], *ptr;
5039dede
AK
191 BOOL useLogin = FALSE;
192#endif
193#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
194 static struct option longOptions[] =
195 {
0b8da006
AK
196 { (char *)"check-db", 0, NULL, 'e' },
197 { (char *)"config", 1, NULL, 'c' },
198 { (char *)"daemon", 0, NULL, 'd' },
199 { (char *)"debug", 1, NULL, 'D' },
200 { (char *)"help", 0, NULL, 'h' },
8782d780 201 { (char *)"quiet", 1, NULL, 'q' },
5039dede 202#ifdef _WIN32
0b8da006
AK
203 { (char *)"check-service", 0, NULL, '!' },
204 { (char *)"dump", 1, NULL, '~' },
3af85b5e 205 { (char *)"dump-dir", 1, NULL, '@' },
0b8da006
AK
206 { (char *)"install", 0, NULL, 'I' },
207 { (char *)"login", 1, NULL, 'L' },
208 { (char *)"password", 1, NULL, 'P' },
209 { (char *)"remove", 0, NULL, 'R' },
210 { (char *)"start", 0, NULL, 's' },
211 { (char *)"stop", 0, NULL, 'S' },
5039dede 212#else
0b8da006 213 { (char *)"pid-file", 1, NULL, 'p' },
5039dede
AK
214#endif
215 { NULL, 0, 0, 0 }
216 };
217#endif
218
219#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
220 while((ch = getopt_long(argc, argv, VALID_OPTIONS, longOptions, NULL)) != -1)
221#else
222 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
223#endif
224 {
225 switch(ch)
226 {
227 case 'h':
e0f99bf0 228 _tprintf(help_text);
5039dede
AK
229 return FALSE;
230 case 'v':
e5390fb5 231 {
f62b7eab 232 _tprintf(_T("NetXMS Server Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n"));
e5390fb5
VK
233 String ciphers = NXCPGetSupportedCiphersAsText();
234 _tprintf(_T("NXCP: %d.%d.%d.%d (%s)\n"),
235 NXCP_VERSION, CLIENT_PROTOCOL_VERSION_BASE, CLIENT_PROTOCOL_VERSION_MOBILE, CLIENT_PROTOCOL_VERSION_FULL,
236 ciphers.isEmpty() ? _T("NO ENCRYPTION") : ciphers.getBuffer());
ef6a61aa 237 _tprintf(_T("Built with: %hs\n"), CPP_COMPILER_VERSION);
e5390fb5 238 }
5039dede
AK
239 return FALSE;
240 case 'c':
e0f99bf0
VK
241#ifdef UNICODE
242 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
243 g_szConfigFile[MAX_PATH - 1] = 0;
244#else
5039dede 245 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
e0f99bf0 246#endif
5039dede
AK
247 break;
248 case 'C': // Check config
c8076b19 249 g_flags &= ~AF_DAEMON;
e0f99bf0 250 _tprintf(_T("Checking configuration file (%s):\n\n"), g_szConfigFile);
2df047f4 251 LoadConfig(&s_debugLevel);
5039dede
AK
252 return FALSE;
253 case 'd':
c8076b19 254 g_flags |= AF_DAEMON;
5039dede
AK
255 break;
256 case 'D': // Debug level
2df047f4
VK
257 s_debugLevel = strtoul(optarg, &eptr, 0);
258 if ((*eptr != 0) || (s_debugLevel > 9))
5039dede 259 {
e0f99bf0 260 _tprintf(_T("Invalid debug level \"%hs\" - should be in range 0..9\n"), optarg);
2df047f4 261 s_debugLevel = 0;
5039dede
AK
262 }
263 break;
3a82d5ae 264 case 'q': // disable interactive console
c8076b19 265 g_flags |= AF_DEBUG_CONSOLE_DISABLED;
3a82d5ae 266 break;
5039dede
AK
267 case 'e':
268 g_bCheckDB = TRUE;
269 break;
270#ifdef _WIN32
271 case 'L':
e0f99bf0
VK
272#ifdef UNICODE
273 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, login, 256);
274 login[255] = 0;
275#else
5039dede 276 nx_strncpy(login, optarg, 256);
e0f99bf0 277#endif
5039dede
AK
278 useLogin = TRUE;
279 break;
0df29c69
VK
280#ifndef _WIN32
281 case 'p': // PID file
282#ifdef UNICODE
283 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPIDFile, MAX_PATH);
284 g_szPIDFile[MAX_PATH - 1] = 0;
285#else
286 nx_strncpy(g_szPIDFile, optarg, MAX_PATH);
287#endif
288 break;
289#endif
5039dede 290 case 'P':
e0f99bf0
VK
291#ifdef UNICODE
292 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, password, 256);
293 password[255] = 0;
294#else
5039dede 295 nx_strncpy(password, optarg, 256);
e0f99bf0 296#endif
5039dede
AK
297 break;
298 case 'I': // Install service
299 ptr = strrchr(argv[0], '\\');
300 if (ptr != NULL)
301 ptr++;
302 else
303 ptr = argv[0];
304
305 _fullpath(exePath, ptr, 255);
306
307 if (stricmp(&exePath[strlen(exePath)-4], ".exe"))
308 strcat(exePath, ".exe");
309 strcpy(dllPath, exePath);
310 ptr = strrchr(dllPath, '\\');
311 if (ptr != NULL) // Shouldn't be NULL
312 {
313 ptr++;
314 strcpy(ptr, "libnxsrv.dll");
315 }
e0f99bf0
VK
316#ifdef UNICODE
317 WCHAR wexePath[MAX_PATH], wdllPath[MAX_PATH];
318 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, exePath, -1, wexePath, MAX_PATH);
319 wexePath[MAX_PATH - 1] = 0;
320 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, dllPath, -1, wdllPath, MAX_PATH);
321 wdllPath[MAX_PATH - 1] = 0;
322 InstallService(wexePath, wdllPath, useLogin ? login : NULL, useLogin ? password : NULL);
323#else
5039dede 324 InstallService(exePath, dllPath, useLogin ? login : NULL, useLogin ? password : NULL);
e0f99bf0 325#endif
5039dede
AK
326 return FALSE;
327 case 'R': // Remove service
328 RemoveService();
329 return FALSE;
330 case 's': // Start service
331 StartCoreService();
332 return FALSE;
333 case 'S': // Stop service
334 StopCoreService();
335 return FALSE;
336 case '!': // Check service configuration (for migration from pre-0.2.20)
337 CheckServiceConfig();
338 return FALSE;
3af85b5e
VK
339 case '@':
340#ifdef UNICODE
341 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szDumpDir, MAX_PATH);
342#else
343 strlcpy(g_szDumpDir, optarg, MAX_PATH);
344#endif
345 if (g_szDumpDir[_tcslen(g_szDumpDir) - 1] == _T('\\'))
346 {
347 g_szDumpDir[_tcslen(g_szDumpDir) - 1] = 0;
348 }
349 break;
5039dede 350 case '~':
e0f99bf0 351 CreateMiniDump(strtoul(optarg, NULL, 0));
5039dede
AK
352 return FALSE;
353#endif
354 default:
355 break;
356 }
357 }
358 return TRUE;
359}
360
d78be64d
VK
361/**
362 * Startup code
363 */
06b383fe 364int main(int argc, char* argv[])
5039dede 365{
d37e7f10 366 InitNetXMSProcess(false);
5039dede 367
5039dede
AK
368 // Check for alternate config file location
369#ifdef _WIN32
fe8ea784 370 HKEY hKey;
42acd918 371 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
5039dede 372 {
fe8ea784 373 DWORD dwSize = MAX_PATH * sizeof(TCHAR);
5039dede
AK
374 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
375 RegCloseKey(hKey);
376 }
377#else
38ffd4b0
VK
378 const TCHAR *configEnv = _tgetenv(_T("NETXMSD_CONFIG"));
379 if ((configEnv != NULL) && (*configEnv != 0))
380 {
a2ef43f7 381 nx_strncpy(g_szConfigFile, configEnv, MAX_PATH);
38ffd4b0 382 }
5039dede
AK
383#endif
384
1e2b0526
VP
385 if (!ParseCommandLine(argc, argv))
386 return 1;
5039dede 387
2df047f4 388 if (!LoadConfig(&s_debugLevel))
5039dede
AK
389 {
390 if (IsStandalone())
e0f99bf0 391 _tprintf(_T("Error loading configuration file\n"));
5039dede
AK
392 return 1;
393 }
394
2df047f4
VK
395 if (s_debugLevel == NXCONFIG_UNINITIALIZED_VALUE)
396 s_debugLevel = 0;
397 nxlog_set_debug_level(s_debugLevel);
b41dff2a 398 free(s_debugTags);
e2e66145 399
5039dede
AK
400 // Set exception handler
401#ifdef _WIN32
c8076b19 402 if (g_flags & AF_CATCH_EXCEPTIONS)
4262c0dc 403 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter,
c8076b19 404 g_szDumpDir, _T("netxmsd"), MSG_EXCEPTION, (g_flags & AF_WRITE_FULL_DUMP) ? true : false, IsStandalone());
5039dede
AK
405 __try {
406#endif
407
408 // Check database before start if requested
409 if (g_bCheckDB)
410 {
411 char szCmd[MAX_PATH + 128], *pszSep;
412
413 strncpy(szCmd, argv[0], MAX_PATH);
414 pszSep = strrchr(szCmd, FS_PATH_SEPARATOR[0]);
415 if (pszSep != NULL)
416 pszSep++;
417 else
418 pszSep = szCmd;
e0f99bf0
VK
419#ifdef UNICODE
420 snprintf(pszSep, 128, "nxdbmgr -c \"%S\" -f check", g_szConfigFile);
421#else
422 snprintf(pszSep, 128, "nxdbmgr -c \"%s\" -f check", g_szConfigFile);
423#endif
5039dede
AK
424 if (!ExecAndWait(szCmd))
425 if (IsStandalone())
e0f99bf0 426 _tprintf(_T("ERROR: Failed to execute command \"%hs\"\n"), szCmd);
5039dede
AK
427 }
428
429#ifdef _WIN32
430 if (!IsStandalone())
431 {
432 InitService();
433 }
434 else
435 {
436 if (!Initialize())
437 {
e0f99bf0 438 _tprintf(_T("NetXMS Core initialization failed\n"));
5039dede
AK
439
440 // Remove database lock
c8076b19 441 if (g_flags & AF_DB_LOCKED)
5039dede
AK
442 {
443 UnlockDB();
444 ShutdownDB();
445 }
c370cbf9 446 nxlog_close();
5039dede
AK
447 return 3;
448 }
449 Main(NULL);
450 }
451#else /* not _WIN32 */
452 if (!IsStandalone())
453 {
454 if (daemon(0, 0) == -1)
455 {
456 perror("Call to daemon() failed");
457 return 2;
458 }
459 }
460
461 // Write PID file
fe8ea784 462 FILE *fp = _tfopen(g_szPIDFile, _T("w"));
5039dede
AK
463 if (fp != NULL)
464 {
e0f99bf0 465 _ftprintf(fp, _T("%d"), getpid());
5039dede
AK
466 fclose(fp);
467 }
468
469 // Initialize server
470 if (!Initialize())
471 {
472 // Remove database lock
c8076b19 473 if (g_flags & AF_DB_LOCKED)
5039dede
AK
474 {
475 UnlockDB();
476 ShutdownDB();
477 }
c370cbf9 478 nxlog_close();
5039dede
AK
479 return 3;
480 }
481
482 // Everything is OK, start common main loop
483 StartMainLoop(SignalHandler, Main);
484#endif /* _WIN32 */
485
486#ifdef _WIN32
487 LIBNETXMS_EXCEPTION_HANDLER
488#endif
489 return 0;
490}