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