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