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