Protect from null pointer exception
[public/netxms.git] / src / server / netxmsd / netxmsd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** Server startup module
fe8ea784 4** Copyright (C) 2003-2016 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
94fbffb1 34#ifndef _WIN32
44cdd527
VK
35#include <signal.h>
36#endif
37
d27e7413 38#if defined(_WITH_ENCRYPTION) && defined(_WIN32)
958a9397
VK
39#include <openssl/applink.c>
40#endif
41
f375019e
VK
42/**
43 * Global data
44 */
5039dede
AK
45BOOL g_bCheckDB = FALSE;
46
f375019e
VK
47/**
48 * Help text
49 */
f62b7eab 50static 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 51 _T("Copyright (c) 2003-2015 Raden Solutions\n\n")
e0f99bf0
VK
52 _T("Usage: netxmsd [<options>]\n\n")
53 _T("Valid options are:\n")
54 _T(" -e : Run database check on startup\n")
55 _T(" -c <file> : Set non-default configuration file\n")
e0f99bf0
VK
56 _T(" -d : Run as daemon/service\n")
57 _T(" -D <level> : Set debug level (valid levels are 0..9)\n")
58 _T(" -h : Display help and exit\n")
5039dede 59#ifdef _WIN32
e0f99bf0
VK
60 _T(" -I : Install Windows service\n")
61 _T(" -L <user> : Login name for service account.\n")
62 _T(" -P <passwd> : Password for service account.\n")
5039dede 63#else
e0f99bf0 64 _T(" -p <file> : Specify pid file.\n")
5039dede 65#endif
7bdb43c3 66 _T(" -q : Disable interactive console\n")
5039dede 67#ifdef _WIN32
e0f99bf0
VK
68 _T(" -R : Remove Windows service\n")
69 _T(" -s : Start Windows service\n")
70 _T(" -S : Stop Windows service\n")
5039dede 71#endif
e0f99bf0
VK
72 _T(" -v : Display version and exit\n")
73 _T("\n");
5039dede 74
f375019e
VK
75/**
76 * Execute command and wait
77 */
e0f99bf0 78static BOOL ExecAndWait(char *pszCommand)
5039dede
AK
79{
80 BOOL bSuccess = TRUE;
81
82#ifdef _WIN32
e0f99bf0 83 STARTUPINFOA si;
5039dede
AK
84 PROCESS_INFORMATION pi;
85
86 // Fill in process startup info structure
977e7d9d
VK
87 memset(&si, 0, sizeof(STARTUPINFOA));
88 si.cb = sizeof(STARTUPINFOA);
5039dede
AK
89 si.dwFlags = 0;
90
91 // Create new process
e0f99bf0
VK
92 if (!CreateProcessA(NULL, pszCommand, NULL, NULL, FALSE,
93 IsStandalone() ? 0 : CREATE_NO_WINDOW | DETACHED_PROCESS,
94 NULL, NULL, &si, &pi))
5039dede
AK
95 {
96 bSuccess = FALSE;
97 }
98 else
99 {
100 WaitForSingleObject(pi.hProcess, INFINITE);
101
102 // Close all handles
103 CloseHandle(pi.hThread);
104 CloseHandle(pi.hProcess);
105 }
106#else
107 bSuccess = (system(pszCommand) != -1);
108#endif
109
110 return bSuccess;
111}
112
41767c3d
VP
113/**
114 * Create minidump of given process
115 */
5039dede
AK
116
117#ifdef _WIN32
118
119static void CreateMiniDump(DWORD pid)
120{
121 HANDLE hFile, hProcess;
122 TCHAR error[256];
123
e0f99bf0 124 _tprintf(_T("INFO: Starting minidump for process %d\n"), pid);
5039dede
AK
125 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
126 if (hProcess != NULL)
127 {
128 hFile = CreateFile(_T("C:\\netxmsd.mdmp"), GENERIC_WRITE, 0, NULL,
129 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
130 if (hFile != INVALID_HANDLE_VALUE)
131 {
132 MiniDumpWriteDump(hProcess, pid, hFile, MiniDumpNormal, NULL, NULL, NULL);
133 CloseHandle(hFile);
e0f99bf0 134 _tprintf(_T("INFO: Minidump created successfully\n"));
5039dede
AK
135 }
136 else
137 {
e0f99bf0 138 _tprintf(_T("ERROR: cannot create file for minidump (%s)\n"), GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
139 }
140 }
141 else
142 {
e0f99bf0 143 _tprintf(_T("ERROR: cannot open process %d (%s)\n"), pid, GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
144 }
145}
146
147#endif
148
5039dede 149#ifdef _WIN32
7bdb43c3 150#define VALID_OPTIONS "c:CdD:ehIL:P:qRsSv"
5039dede 151#else
7bdb43c3 152#define VALID_OPTIONS "c:CdD:ehp:qv"
5039dede
AK
153#endif
154
d78be64d
VK
155/**
156 * Parse command line
157 * Returns TRUE on success and FALSE on failure
158 */
5039dede
AK
159static BOOL ParseCommandLine(int argc, char *argv[])
160{
161 int ch;
e0f99bf0 162 char *eptr;
5039dede 163#ifdef _WIN32
e0f99bf0
VK
164 TCHAR login[256] = _T(""), password[256] = _T("");
165 char exePath[MAX_PATH], dllPath[MAX_PATH], *ptr;
5039dede
AK
166 BOOL useLogin = FALSE;
167#endif
168#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
169 static struct option longOptions[] =
170 {
0b8da006
AK
171 { (char *)"check-db", 0, NULL, 'e' },
172 { (char *)"config", 1, NULL, 'c' },
173 { (char *)"daemon", 0, NULL, 'd' },
174 { (char *)"debug", 1, NULL, 'D' },
175 { (char *)"help", 0, NULL, 'h' },
8782d780 176 { (char *)"quiet", 1, NULL, 'q' },
5039dede 177#ifdef _WIN32
0b8da006
AK
178 { (char *)"check-service", 0, NULL, '!' },
179 { (char *)"dump", 1, NULL, '~' },
180 { (char *)"install", 0, NULL, 'I' },
181 { (char *)"login", 1, NULL, 'L' },
182 { (char *)"password", 1, NULL, 'P' },
183 { (char *)"remove", 0, NULL, 'R' },
184 { (char *)"start", 0, NULL, 's' },
185 { (char *)"stop", 0, NULL, 'S' },
5039dede 186#else
0b8da006 187 { (char *)"pid-file", 1, NULL, 'p' },
5039dede
AK
188#endif
189 { NULL, 0, 0, 0 }
190 };
191#endif
192
193#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
194 while((ch = getopt_long(argc, argv, VALID_OPTIONS, longOptions, NULL)) != -1)
195#else
196 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
197#endif
198 {
199 switch(ch)
200 {
201 case 'h':
e0f99bf0 202 _tprintf(help_text);
5039dede
AK
203 return FALSE;
204 case 'v':
e5390fb5 205 {
f62b7eab 206 _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
207 String ciphers = NXCPGetSupportedCiphersAsText();
208 _tprintf(_T("NXCP: %d.%d.%d.%d (%s)\n"),
209 NXCP_VERSION, CLIENT_PROTOCOL_VERSION_BASE, CLIENT_PROTOCOL_VERSION_MOBILE, CLIENT_PROTOCOL_VERSION_FULL,
210 ciphers.isEmpty() ? _T("NO ENCRYPTION") : ciphers.getBuffer());
211 }
5039dede
AK
212 return FALSE;
213 case 'c':
e0f99bf0
VK
214#ifdef UNICODE
215 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
216 g_szConfigFile[MAX_PATH - 1] = 0;
217#else
5039dede 218 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
e0f99bf0 219#endif
5039dede
AK
220 break;
221 case 'C': // Check config
c8076b19 222 g_flags &= ~AF_DAEMON;
e0f99bf0 223 _tprintf(_T("Checking configuration file (%s):\n\n"), g_szConfigFile);
5039dede
AK
224 LoadConfig();
225 return FALSE;
226 case 'd':
c8076b19 227 g_flags |= AF_DAEMON;
5039dede
AK
228 break;
229 case 'D': // Debug level
ed950274
VK
230 g_debugLevel = strtoul(optarg, &eptr, 0);
231 if ((*eptr != 0) || (g_debugLevel > 9))
5039dede 232 {
e0f99bf0 233 _tprintf(_T("Invalid debug level \"%hs\" - should be in range 0..9\n"), optarg);
ed950274 234 g_debugLevel = 0;
5039dede
AK
235 }
236 break;
3a82d5ae 237 case 'q': // disable interactive console
c8076b19 238 g_flags |= AF_DEBUG_CONSOLE_DISABLED;
3a82d5ae 239 break;
5039dede
AK
240 case 'e':
241 g_bCheckDB = TRUE;
242 break;
243#ifdef _WIN32
244 case 'L':
e0f99bf0
VK
245#ifdef UNICODE
246 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, login, 256);
247 login[255] = 0;
248#else
5039dede 249 nx_strncpy(login, optarg, 256);
e0f99bf0 250#endif
5039dede
AK
251 useLogin = TRUE;
252 break;
0df29c69
VK
253#ifndef _WIN32
254 case 'p': // PID file
255#ifdef UNICODE
256 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPIDFile, MAX_PATH);
257 g_szPIDFile[MAX_PATH - 1] = 0;
258#else
259 nx_strncpy(g_szPIDFile, optarg, MAX_PATH);
260#endif
261 break;
262#endif
5039dede 263 case 'P':
e0f99bf0
VK
264#ifdef UNICODE
265 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, password, 256);
266 password[255] = 0;
267#else
5039dede 268 nx_strncpy(password, optarg, 256);
e0f99bf0 269#endif
5039dede
AK
270 break;
271 case 'I': // Install service
272 ptr = strrchr(argv[0], '\\');
273 if (ptr != NULL)
274 ptr++;
275 else
276 ptr = argv[0];
277
278 _fullpath(exePath, ptr, 255);
279
280 if (stricmp(&exePath[strlen(exePath)-4], ".exe"))
281 strcat(exePath, ".exe");
282 strcpy(dllPath, exePath);
283 ptr = strrchr(dllPath, '\\');
284 if (ptr != NULL) // Shouldn't be NULL
285 {
286 ptr++;
287 strcpy(ptr, "libnxsrv.dll");
288 }
e0f99bf0
VK
289#ifdef UNICODE
290 WCHAR wexePath[MAX_PATH], wdllPath[MAX_PATH];
291 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, exePath, -1, wexePath, MAX_PATH);
292 wexePath[MAX_PATH - 1] = 0;
293 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, dllPath, -1, wdllPath, MAX_PATH);
294 wdllPath[MAX_PATH - 1] = 0;
295 InstallService(wexePath, wdllPath, useLogin ? login : NULL, useLogin ? password : NULL);
296#else
5039dede 297 InstallService(exePath, dllPath, useLogin ? login : NULL, useLogin ? password : NULL);
e0f99bf0 298#endif
5039dede
AK
299 return FALSE;
300 case 'R': // Remove service
301 RemoveService();
302 return FALSE;
303 case 's': // Start service
304 StartCoreService();
305 return FALSE;
306 case 'S': // Stop service
307 StopCoreService();
308 return FALSE;
309 case '!': // Check service configuration (for migration from pre-0.2.20)
310 CheckServiceConfig();
311 return FALSE;
312 case '~':
e0f99bf0 313 CreateMiniDump(strtoul(optarg, NULL, 0));
5039dede
AK
314 return FALSE;
315#endif
316 default:
317 break;
318 }
319 }
320 return TRUE;
321}
322
d78be64d
VK
323/**
324 * Startup code
325 */
06b383fe 326int main(int argc, char* argv[])
5039dede 327{
fe8ea784 328 InitNetXMSProcess();
5039dede 329
440c555e 330#ifndef _WIN32
44cdd527
VK
331 signal(SIGPIPE, SIG_IGN);
332 signal(SIGHUP, SIG_IGN);
44cdd527
VK
333 signal(SIGQUIT, SIG_IGN);
334 signal(SIGUSR1, SIG_IGN);
335 signal(SIGUSR2, SIG_IGN);
336#endif
337
5039dede
AK
338 // Check for alternate config file location
339#ifdef _WIN32
fe8ea784 340 HKEY hKey;
42acd918 341 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
5039dede 342 {
fe8ea784 343 DWORD dwSize = MAX_PATH * sizeof(TCHAR);
5039dede
AK
344 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
345 RegCloseKey(hKey);
346 }
347#else
38ffd4b0
VK
348 const TCHAR *configEnv = _tgetenv(_T("NETXMSD_CONFIG"));
349 if ((configEnv != NULL) && (*configEnv != 0))
350 {
a2ef43f7 351 nx_strncpy(g_szConfigFile, configEnv, MAX_PATH);
38ffd4b0 352 }
5039dede
AK
353#endif
354
1e2b0526
VP
355 if (!ParseCommandLine(argc, argv))
356 return 1;
5039dede
AK
357
358 if (!LoadConfig())
359 {
360 if (IsStandalone())
e0f99bf0 361 _tprintf(_T("Error loading configuration file\n"));
5039dede
AK
362 return 1;
363 }
364
e2e66145
VK
365 if (g_debugLevel == NXCONFIG_UNINITIALIZED_VALUE)
366 g_debugLevel = 0;
367
5039dede
AK
368 // Set exception handler
369#ifdef _WIN32
c8076b19 370 if (g_flags & AF_CATCH_EXCEPTIONS)
4262c0dc 371 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter,
c8076b19 372 g_szDumpDir, _T("netxmsd"), MSG_EXCEPTION, (g_flags & AF_WRITE_FULL_DUMP) ? true : false, IsStandalone());
5039dede
AK
373 __try {
374#endif
375
376 // Check database before start if requested
377 if (g_bCheckDB)
378 {
379 char szCmd[MAX_PATH + 128], *pszSep;
380
381 strncpy(szCmd, argv[0], MAX_PATH);
382 pszSep = strrchr(szCmd, FS_PATH_SEPARATOR[0]);
383 if (pszSep != NULL)
384 pszSep++;
385 else
386 pszSep = szCmd;
e0f99bf0
VK
387#ifdef UNICODE
388 snprintf(pszSep, 128, "nxdbmgr -c \"%S\" -f check", g_szConfigFile);
389#else
390 snprintf(pszSep, 128, "nxdbmgr -c \"%s\" -f check", g_szConfigFile);
391#endif
5039dede
AK
392 if (!ExecAndWait(szCmd))
393 if (IsStandalone())
e0f99bf0 394 _tprintf(_T("ERROR: Failed to execute command \"%hs\"\n"), szCmd);
5039dede
AK
395 }
396
397#ifdef _WIN32
398 if (!IsStandalone())
399 {
400 InitService();
401 }
402 else
403 {
404 if (!Initialize())
405 {
e0f99bf0 406 _tprintf(_T("NetXMS Core initialization failed\n"));
5039dede
AK
407
408 // Remove database lock
c8076b19 409 if (g_flags & AF_DB_LOCKED)
5039dede
AK
410 {
411 UnlockDB();
412 ShutdownDB();
413 }
414 return 3;
415 }
416 Main(NULL);
417 }
418#else /* not _WIN32 */
419 if (!IsStandalone())
420 {
421 if (daemon(0, 0) == -1)
422 {
423 perror("Call to daemon() failed");
424 return 2;
425 }
426 }
427
428 // Write PID file
fe8ea784 429 FILE *fp = _tfopen(g_szPIDFile, _T("w"));
5039dede
AK
430 if (fp != NULL)
431 {
e0f99bf0 432 _ftprintf(fp, _T("%d"), getpid());
5039dede
AK
433 fclose(fp);
434 }
435
436 // Initialize server
437 if (!Initialize())
438 {
439 // Remove database lock
c8076b19 440 if (g_flags & AF_DB_LOCKED)
5039dede
AK
441 {
442 UnlockDB();
443 ShutdownDB();
444 }
445 return 3;
446 }
447
448 // Everything is OK, start common main loop
449 StartMainLoop(SignalHandler, Main);
450#endif /* _WIN32 */
451
452#ifdef _WIN32
453 LIBNETXMS_EXCEPTION_HANDLER
454#endif
455 return 0;
456}