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