SNMP library and tools on WIndows made UNICODE-only
[public/netxms.git] / src / agent / core / nxagentd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS multiplatform core agent
0bd64860 3** Copyright (C) 2003-2012 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
f0c1d2a4
AK
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
5039dede
AK
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: nxagentd.cpp
20**
21**/
22
23#include "nxagentd.h"
b34606f7 24#include <nxdbapi.h>
5039dede
AK
25
26#if defined(_WIN32)
27#include <conio.h>
28#include <locale.h>
5039dede
AK
29#else
30#include <signal.h>
31#include <sys/wait.h>
32#endif
33
34#if HAVE_SYS_UTSNAME_H
35#include <sys/utsname.h>
36#endif
37
38#if HAVE_SYS_SYSCTL_H
39#include <sys/sysctl.h>
40#endif
41
42
43//
44// Externals
45//
46
47THREAD_RESULT THREAD_CALL ListenerThread(void *);
48THREAD_RESULT THREAD_CALL SessionWatchdog(void *);
49THREAD_RESULT THREAD_CALL TrapSender(void *);
0df58041 50THREAD_RESULT THREAD_CALL MasterAgentListener(void *arg);
5039dede
AK
51
52void ShutdownTrapSender();
53
9fcdfda7
VK
54void StartWatchdog();
55void StopWatchdog();
56int WatchdogMain(DWORD pid);
57
6aab2983
AK
58void InitSessionList();
59
968031d9 60#if !defined(_WIN32)
203e9d8a 61void InitStaticSubagents();
5039dede
AK
62#endif
63
9f96fc88
VK
64#ifdef _WIN32
65extern TCHAR g_windowsServiceName[];
66extern TCHAR g_windowsServiceDisplayName[];
67#endif
68
5039dede
AK
69
70//
71// Messages generated by mc.pl (for UNIX version only)
72//
73
74#ifndef _WIN32
75extern unsigned int g_dwNumMessages;
76extern const TCHAR *g_szMessages[];
77#endif
78
79
80//
81// Valid options for getopt()
82//
83
84#if defined(_WIN32)
175bd003 85#define VALID_OPTIONS "c:CdD:e:EfhHiIM:n:N:P:r:RsSUvX:W:Z:"
5039dede 86#else
c303351c 87#define VALID_OPTIONS "c:CdD:fhM:p:P:r:vX:W:Z:"
5039dede
AK
88#endif
89
90
91//
92// Actions
93//
94
95#define ACTION_NONE 0
96#define ACTION_RUN_AGENT 1
97#define ACTION_INSTALL_SERVICE 2
98#define ACTION_REMOVE_SERVICE 3
99#define ACTION_START_SERVICE 4
100#define ACTION_STOP_SERVICE 5
101#define ACTION_CHECK_CONFIG 6
102#define ACTION_INSTALL_EVENT_SOURCE 7
103#define ACTION_REMOVE_EVENT_SOURCE 8
104#define ACTION_CREATE_CONFIG 9
105#define ACTION_HELP 10
9fcdfda7 106#define ACTION_RUN_WATCHDOG 11
5039dede
AK
107
108
109//
110// Global variables
111//
112
113DWORD g_dwFlags = AF_ENABLE_ACTIONS | AF_ENABLE_AUTOLOAD;
bf3b7f79
VK
114TCHAR g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
115TCHAR g_szSharedSecret[MAX_SECRET_LENGTH] = _T("admin");
116TCHAR g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
117TCHAR g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
118TCHAR g_szDataDirectory[MAX_PATH] = AGENT_DEFAULT_DATA_DIR;
119TCHAR g_szPlatformSuffix[MAX_PSUFFIX_LENGTH] = _T("");
120TCHAR g_szConfigServer[MAX_DB_STRING] = _T("not_set");
121TCHAR g_szRegistrar[MAX_DB_STRING] = _T("not_set");
122TCHAR g_szListenAddress[MAX_PATH] = _T("*");
6ca3b41c 123TCHAR g_szConfigIncludeDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
0df58041 124TCHAR g_masterAgent[MAX_PATH] = _T("not_set");
5039dede
AK
125WORD g_wListenPort = AGENT_LISTEN_PORT;
126SERVER_INFO g_pServerList[MAX_SERVERS];
127DWORD g_dwServerCount = 0;
128DWORD g_dwExecTimeout = 2000; // External process execution timeout in milliseconds
129DWORD g_dwSNMPTimeout = 3000;
130time_t g_tmAgentStartTime;
131DWORD g_dwStartupDelay = 0;
132DWORD g_dwMaxSessions = 32;
15e11d1b 133DWORD g_debugLevel = 0;
e6c91aac 134Config *g_config;
5039dede
AK
135#ifdef _WIN32
136DWORD g_dwIdleTimeout = 60; // Session idle timeout
137#else
138DWORD g_dwIdleTimeout = 120; // Session idle timeout
139#endif
140
968031d9 141#if !defined(_WIN32)
bf3b7f79 142TCHAR g_szPidFile[MAX_PATH] = _T("/var/run/nxagentd.pid");
5039dede
AK
143#endif
144
145#ifdef _WIN32
5039dede
AK
146DWORD (__stdcall *imp_HrLanConnectionNameFromGuidOrPath)(LPWSTR, LPWSTR, LPWSTR, LPDWORD);
147#endif /* _WIN32 */
148
5039dede
AK
149
150//
151// Static variables
152//
153
bf3b7f79
VK
154static TCHAR *m_pszActionList = NULL;
155static TCHAR *m_pszShellActionList = NULL;
156static TCHAR *m_pszServerList = NULL;
157static TCHAR *m_pszControlServerList = NULL;
158static TCHAR *m_pszMasterServerList = NULL;
159static TCHAR *m_pszSubagentList = NULL;
160static TCHAR *m_pszExtParamList = NULL;
161static TCHAR *m_pszShExtParamList = NULL;
9795113c 162static TCHAR *m_pszParamProviderList = NULL;
204ace22 163static TCHAR *m_pszExtSubagentList = NULL;
5039dede
AK
164static DWORD m_dwEnabledCiphers = 0xFFFF;
165static THREAD m_thSessionWatchdog = INVALID_THREAD_HANDLE;
166static THREAD m_thListener = INVALID_THREAD_HANDLE;
167static THREAD m_thTrapSender = INVALID_THREAD_HANDLE;
0df58041 168static THREAD m_thMasterAgentListener = INVALID_THREAD_HANDLE;
bf3b7f79
VK
169static TCHAR m_szProcessToWait[MAX_PATH] = _T("");
170static TCHAR m_szDumpDir[MAX_PATH] = _T("C:\\");
4addc3a3
VK
171static DWORD m_dwMaxLogSize = 16384 * 1024;
172static DWORD m_dwLogHistorySize = 4;
5945d50a
VK
173static DWORD m_dwLogRotationMode = NXLOG_ROTATION_BY_SIZE;
174static TCHAR m_szDailyLogFileSuffix[64] = _T("");
203e9d8a 175static Config *s_registry = NULL;
5039dede 176
968031d9 177#if defined(_WIN32)
5039dede
AK
178static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
179#endif
180
968031d9 181#if !defined(_WIN32)
5039dede
AK
182static pid_t m_pid;
183#endif
184
185
186//
187// Configuration file template
188//
189
190static NX_CFG_TEMPLATE m_cfgTemplate[] =
191{
192 { "Action", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList },
193 { "ActionShellExec", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShellActionList },
194 { "ControlServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszControlServerList },
9fcdfda7 195 { "CreateCrashDumps", CT_BOOLEAN, 0, 0, AF_CATCH_EXCEPTIONS, 0, &g_dwFlags },
203e9d8a 196 { "DataDirectory", CT_STRING, 0, 0, MAX_PATH, 0, g_szDataDirectory },
5945d50a 197 { "DailyLogFileSuffix", CT_STRING, 0, 0, MAX_PATH, 0, m_szDailyLogFileSuffix },
15e11d1b 198 { "DebugLevel", CT_LONG, 0, 0, 0, 0, &g_debugLevel },
9fcdfda7 199 { "DumpDirectory", CT_STRING, 0, 0, MAX_PATH, 0, m_szDumpDir },
5039dede 200 { "EnableActions", CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags },
1a5e0c22 201 { "EnableArbitraryFileUpload", CT_BOOLEAN, 0, 0, AF_ARBITRARY_FILE_UPLOAD, 0, &g_dwFlags },
5039dede
AK
202 { "EnabledCiphers", CT_LONG, 0, 0, 0, 0, &m_dwEnabledCiphers },
203 { "EnableProxy", CT_BOOLEAN, 0, 0, AF_ENABLE_PROXY, 0, &g_dwFlags },
204 { "EnableSNMPProxy", CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_PROXY, 0, &g_dwFlags },
205 { "EnableSubagentAutoload", CT_BOOLEAN, 0, 0, AF_ENABLE_AUTOLOAD, 0, &g_dwFlags },
9fcdfda7 206 { "EnableWatchdog", CT_BOOLEAN, 0, 0, AF_ENABLE_WATCHDOG, 0, &g_dwFlags },
5039dede 207 { "ExecTimeout", CT_LONG, 0, 0, 0, 0, &g_dwExecTimeout },
0df58041 208 { "ExternalMasterAgent", CT_STRING, 0, 0, MAX_PATH, 0, g_masterAgent },
5039dede
AK
209 { "ExternalParameter", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtParamList },
210 { "ExternalParameterShellExec", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShExtParamList },
9795113c 211 { "ExternalParametersProvider", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszParamProviderList },
204ace22 212 { "ExternalSubagent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtSubagentList },
5039dede 213 { "FileStore", CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore },
cbc777ee 214 { "FullCrashDumps", CT_BOOLEAN, 0, 0, AF_WRITE_FULL_DUMP, 0, &g_dwFlags },
5039dede
AK
215 { "ListenAddress", CT_STRING, 0, 0, MAX_PATH, 0, g_szListenAddress },
216 { "ListenPort", CT_WORD, 0, 0, 0, 0, &g_wListenPort },
217 { "LogFile", CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile },
4addc3a3 218 { "LogHistorySize", CT_LONG, 0, 0, 0, 0, &m_dwLogHistorySize },
5945d50a 219 { "LogRotationMode", CT_LONG, 0, 0, 0, 0, &m_dwLogRotationMode },
5039dede
AK
220 { "LogUnresolvedSymbols", CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags },
221 { "MasterServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszMasterServerList },
4addc3a3 222 { "MaxLogSize", CT_LONG, 0, 0, 0, 0, &m_dwMaxLogSize },
5039dede
AK
223 { "MaxSessions", CT_LONG, 0, 0, 0, 0, &g_dwMaxSessions },
224 { "PlatformSuffix", CT_STRING, 0, 0, MAX_PSUFFIX_LENGTH, 0, g_szPlatformSuffix },
225 { "RequireAuthentication", CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags },
226 { "RequireEncryption", CT_BOOLEAN, 0, 0, AF_REQUIRE_ENCRYPTION, 0, &g_dwFlags },
227 { "Servers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList },
228 { "SessionIdleTimeout", CT_LONG, 0, 0, 0, 0, &g_dwIdleTimeout },
229 { "SharedSecret", CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret },
230 { "StartupDelay", CT_LONG, 0, 0, 0, 0, &g_dwStartupDelay },
231 { "SubAgent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList },
232 { "TimeOut", CT_IGNORE, 0, 0, 0, 0, NULL },
233 { "WaitForProcess", CT_STRING, 0, 0, MAX_PATH, 0, m_szProcessToWait },
234 { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
235};
236
237
238//
239// Help text
240//
241
242static char m_szHelpText[] =
243 "Usage: nxagentd [options]\n"
244 "Where valid options are:\n"
245 " -c <file> : Use configuration file <file> (default " AGENT_DEFAULT_CONFIG ")\n"
246 " -C : Check configuration file and exit\n"
5039dede 247 " -d : Run as daemon/service\n"
c303351c 248 " -D <level> : Set debug level (0..9)\n"
9f96fc88
VK
249#ifdef _WIN32
250 " -e <name> : Windows event source name\n"
251#endif
5039dede
AK
252 " -f : Run in foreground\n"
253 " -h : Display help and exit\n"
254#ifdef _WIN32
255 " -H : Hide agent's window when in standalone mode\n"
175bd003 256 " -i : Installed Windows service must be interactive\n"
5039dede
AK
257 " -I : Install Windows service\n"
258#endif
259 " -M <addr> : Download config from management server <addr>\n"
9f96fc88
VK
260#ifdef _WIN32
261 " -n <name> : Service name\n"
262 " -N <name> : Service display name\n"
263#endif
968031d9 264#if !defined(_WIN32)
5039dede
AK
265 " -p : Path to pid file (default: /var/run/nxagentd.pid)\n"
266#endif
267 " -P <text> : Set platform suffix to <text>\n"
268 " -r <addr> : Register agent on management server <addr>\n"
269#ifdef _WIN32
270 " -R : Remove Windows service\n"
271 " -s : Start Windows servive\n"
272 " -S : Stop Windows service\n"
273#endif
274 " -v : Display version and exit\n"
275 "\n";
276
277
203e9d8a
VK
278//
279// Save registry
280//
281
282static void SaveRegistry()
283{
284 TCHAR regPath[MAX_PATH];
285 nx_strncpy(regPath, g_szDataDirectory, MAX_PATH - _tcslen(REGISTRY_FILE_NAME) - 1);
286 if (regPath[_tcslen(regPath) - 1] != FS_PATH_SEPARATOR_CHAR)
287 _tcscat(regPath, FS_PATH_SEPARATOR);
288 _tcscat(regPath, REGISTRY_FILE_NAME);
289
290 String xml = s_registry->createXml();
291 FILE *f = _tfopen(regPath, _T("w"));
292 if (f != NULL)
293 {
294 _fputts((const TCHAR *)xml, f);
295 fclose(f);
296 }
297 else
298 {
299 nxlog_write(MSG_REGISTRY_SAVE_FAILED, NXLOG_ERROR, "ss", regPath, strerror(errno));
300 }
301}
302
303
304//
305// Open registry
306//
307
308Config *OpenRegistry()
309{
310 s_registry->lock();
311 return s_registry;
312}
313
314
315//
316// Close registry
317//
318
319void CloseRegistry(bool modified)
320{
321 if (modified)
322 SaveRegistry();
323 s_registry->unlock();
324}
325
326
5039dede
AK
327#ifdef _WIN32
328
329//
330// Get our own console window handle (an alternative to Microsoft's GetConsoleWindow)
331//
332
203e9d8a 333static HWND GetConsoleHWND()
5039dede
AK
334{
335 HWND hWnd;
336 DWORD wpid, cpid;
337
338 cpid = GetCurrentProcessId();
339 while(1)
340 {
341 hWnd = FindWindowEx(NULL, NULL, _T("ConsoleWindowClass"), NULL);
342 if (hWnd == NULL)
343 break;
344
345 GetWindowThreadProcessId(hWnd, &wpid);
346 if (cpid == wpid)
347 break;
348 }
349
350 return hWnd;
351}
352
353
354//
355// Get proc address and write log file
356//
357
358static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
359{
360 FARPROC ptr;
361
362 ptr = GetProcAddress(hModule, procName);
363 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
364 nxlog_write(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
365 return ptr;
366}
367
368
369//
370// Import symbols
371//
372
203e9d8a 373static void ImportSymbols()
5039dede
AK
374{
375 HMODULE hModule;
376
5039dede 377 // NETMAN.DLL
bf3b7f79 378 hModule = LoadLibrary(_T("NETMAN.DLL"));
5039dede
AK
379 if (hModule != NULL)
380 {
381 imp_HrLanConnectionNameFromGuidOrPath =
382 (DWORD (__stdcall *)(LPWSTR, LPWSTR, LPWSTR, LPDWORD))GetProcAddressAndLog(hModule,
383 "HrLanConnectionNameFromGuidOrPath");
384 }
385 else
386 {
387 nxlog_write(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "NETMAN.DLL");
388 }
389}
390
391
392//
393// Shutdown thread (created by H_RestartAgent)
394//
395
396static THREAD_RESULT THREAD_CALL ShutdownThread(void *pArg)
397{
8e954797 398 DebugPrintf(INVALID_INDEX, 1, _T("Shutdown thread started"));
5039dede
AK
399 Shutdown();
400 ExitProcess(0);
401 return THREAD_OK; // Never reached
402}
403
404#endif /* _WIN32 */
405
406
407//
408// Restart agent
409//
410
6173bea8 411static LONG H_RestartAgent(const TCHAR *action, StringList *args, const TCHAR *data)
5039dede 412{
5b2c3e2d
VK
413 DebugPrintf(INVALID_INDEX, 1, _T("H_RestartAgent() called"));
414
5039dede
AK
415 TCHAR szCmdLine[4096], szPlatformSuffixOption[MAX_PSUFFIX_LENGTH + 16];
416#ifdef _WIN32
417 TCHAR szExecName[MAX_PATH];
418 DWORD dwResult;
419 STARTUPINFO si;
420 PROCESS_INFORMATION pi;
421
422 GetModuleFileName(GetModuleHandle(NULL), szExecName, MAX_PATH);
423#else
424 TCHAR szExecName[MAX_PATH] = PREFIX _T("/bin/nxagentd");
425#endif
426
427 if (g_szPlatformSuffix[0] != 0)
428 {
429 _sntprintf(szPlatformSuffixOption, MAX_PSUFFIX_LENGTH + 16, _T("-P \"%s\" "), g_szPlatformSuffix);
430 }
431 else
432 {
433 szPlatformSuffixOption[0] = 0;
434 }
435
436#ifdef _WIN32
c303351c 437 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" -n \"%s\" -e \"%s\" %s%s%s%s%s-D %d %s-X %u"), szExecName,
93599cfd
VK
438 g_szConfigFile, g_windowsServiceName, g_windowsEventSourceName,
439 (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
5039dede
AK
440 (g_dwFlags & AF_HIDE_WINDOW) ? _T("-H ") : _T(""),
441 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
442 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
443 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
c303351c 444 g_debugLevel, szPlatformSuffixOption,
5039dede 445 (g_dwFlags & AF_DAEMON) ? 0 : GetCurrentProcessId());
c303351c 446 DebugPrintf(INVALID_INDEX, 1, _T("Restarting agent with command line '%s'"), szCmdLine);
5039dede 447
5039dede
AK
448 // Fill in process startup info structure
449 memset(&si, 0, sizeof(STARTUPINFO));
450 si.cb = sizeof(STARTUPINFO);
451
452 // Create new process
453 if (!CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE,
454 (g_dwFlags & AF_DAEMON) ? (CREATE_NO_WINDOW | DETACHED_PROCESS) : (CREATE_NEW_CONSOLE),
455 NULL, NULL, &si, &pi))
456 {
457 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", szCmdLine, GetLastError());
458 dwResult = ERR_EXEC_FAILED;
459 }
460 else
461 {
462 // Close all handles
463 CloseHandle(pi.hThread);
464 CloseHandle(pi.hProcess);
465 dwResult = ERR_SUCCESS;
466 }
467 if ((dwResult == ERR_SUCCESS) && (!(g_dwFlags & AF_DAEMON)))
468 {
469 if (g_dwFlags & AF_HIDE_WINDOW)
470 {
471 ConditionSet(m_hCondShutdown);
472 }
473 else
474 {
475 ThreadCreate(ShutdownThread, 0, NULL);
476 }
477 }
478 return dwResult;
479#else
c303351c 480 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" %s%s%s%s-D %d %s-X %lu"), szExecName,
5039dede
AK
481 g_szConfigFile, (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
482 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
483 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
484 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
53c96e2d 485 (int)g_debugLevel, szPlatformSuffixOption,
5039dede 486 (unsigned long)m_pid);
5b2c3e2d 487 DebugPrintf(INVALID_INDEX, 1, _T("Restarting agent with command line '%s'"), szCmdLine);
9fcdfda7 488 return ExecuteCommand(szCmdLine, NULL, NULL);
5039dede 489#endif
5039dede
AK
490}
491
492
493//
494// This function writes message from subagent to agent's log
495//
496
c303351c 497static void WriteSubAgentMsg(int logLevel, int debugLevel, const TCHAR *pszMsg)
5039dede 498{
c303351c 499 if (logLevel == EVENTLOG_DEBUG_TYPE)
5039dede 500 {
15e11d1b 501 if (debugLevel <= (int)g_debugLevel)
c303351c 502 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", pszMsg);
5039dede
AK
503 }
504 else
505 {
c303351c 506 nxlog_write(MSG_SUBAGENT_MSG, logLevel, "s", pszMsg);
5039dede
AK
507 }
508}
509
510
511//
512// Signal handler for UNIX platforms
513//
514
968031d9 515#if !defined(_WIN32)
5039dede
AK
516
517static THREAD_RESULT THREAD_CALL SignalHandler(void *pArg)
518{
519 sigset_t signals;
520 int nSignal;
521
522 sigemptyset(&signals);
523 sigaddset(&signals, SIGTERM);
524 sigaddset(&signals, SIGINT);
525 sigaddset(&signals, SIGPIPE);
526 sigaddset(&signals, SIGSEGV);
527 sigaddset(&signals, SIGHUP);
528 sigaddset(&signals, SIGUSR1);
529 sigaddset(&signals, SIGUSR2);
530
531 sigprocmask(SIG_BLOCK, &signals, NULL);
532
533 while(1)
534 {
535 if (sigwait(&signals, &nSignal) == 0)
536 {
537 switch(nSignal)
538 {
539 case SIGTERM:
540 case SIGINT:
541 goto stop_handler;
542 case SIGSEGV:
543 abort();
544 break;
545 default:
546 break;
547 }
548 }
549 else
550 {
551 ThreadSleepMs(100);
552 }
553 }
554
555stop_handler:
556 sigprocmask(SIG_UNBLOCK, &signals, NULL);
557 return THREAD_OK;
558}
559
560#endif
561
562
563//
564// Load subagent for Windows NT or Windows 9x or platform subagent on UNIX
565//
566
567#ifdef _WIN32
568
203e9d8a 569void LoadWindowsSubagent()
5039dede
AK
570{
571 OSVERSIONINFO ver;
572
573 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
574 if (GetVersionEx(&ver))
575 {
576 switch(ver.dwPlatformId)
577 {
578 case VER_PLATFORM_WIN32_WINDOWS: // Windows 9x
579 LoadSubAgent("WIN9X.NSM");
580 break;
581 case VER_PLATFORM_WIN32_NT: // Windows NT or higher
582 LoadSubAgent("WINNT.NSM");
583 break;
584 default:
585 break;
586 }
587 }
588 else
589 {
590 nxlog_write(MSG_GETVERSION_FAILED, EVENTLOG_WARNING_TYPE, "e", GetLastError());
591 }
592}
593
594#else
595
203e9d8a 596void LoadPlatformSubagent()
5039dede 597{
968031d9 598#if HAVE_SYS_UTSNAME_H && !defined(_STATIC_AGENT)
5039dede
AK
599 struct utsname un;
600 char szName[MAX_PATH];
601 int i;
602
603 if (uname(&un) != -1)
604 {
605 // Convert system name to lowercase
606 for(i = 0; un.sysname[i] != 0; i++)
607 un.sysname[i] = tolower(un.sysname[i]);
608 if (!strcmp(un.sysname, "hp-ux"))
609 strcpy(un.sysname, "hpux");
b0032c83 610 snprintf(szName, MAX_PATH, LIBDIR "/libnsm_%s" SHL_SUFFIX, un.sysname);
5039dede
AK
611 LoadSubAgent(szName);
612 }
613#endif
614}
615
616#endif
617
618
6173bea8
VK
619//
620// Send file to server (subagent API)
621//
622
623static BOOL SendFileToServer(void *session, DWORD requestId, const TCHAR *file, long offset)
624{
4685a2ad
VK
625 if (session == NULL)
626 return FALSE;
627
628 return ((CommSession *)session)->sendFile(requestId, file, offset);
6173bea8
VK
629}
630
631
b34606f7
VK
632//
633// Debug callback for DB library
634//
635
636static void DBLibraryDebugCallback(int level, const TCHAR *format, va_list args)
637{
15e11d1b 638 if (level <= (int)g_debugLevel)
b34606f7
VK
639 {
640 TCHAR buffer[4096];
641
642 _vsntprintf(buffer, 4096, format, args);
643 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", buffer);
644 }
645}
646
647
968031d9
VK
648//
649// Parser server list
650//
651
652static void ParseServerList(TCHAR *serverList, BOOL isControl, BOOL isMaster)
653{
654 TCHAR *pItem, *pEnd;
655
656 for(pItem = pEnd = serverList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
657 {
658 pEnd = strchr(pItem, ',');
659 if (pEnd != NULL)
660 *pEnd = 0;
661 StrStrip(pItem);
662
663 DWORD ipAddr, netMask;
664
665 TCHAR *mask = _tcschr(pItem, _T('/'));
666 if (mask != NULL)
667 {
668 *mask = 0;
669 mask++;
670 ipAddr = inet_addr(pItem);
671
672 TCHAR *eptr;
673 int bits = _tcstol(mask, &eptr, 10);
674 if ((*eptr == 0) && (bits >= 0) && (bits <= 32))
675 {
676 netMask = htonl(0xFFFFFFFF << (32 - bits));
677 }
678 else
679 {
680 ipAddr = INADDR_NONE;
681 if (!(g_dwFlags & AF_DAEMON))
682 printf("Invalid network mask %s\n", mask);
683 }
684 }
685 else
686 {
687 ipAddr = ResolveHostName(pItem);
688 if (ipAddr == INADDR_ANY)
689 ipAddr = INADDR_NONE;
690 netMask = 0xFFFFFFFF;
691 }
692 if (g_pServerList[g_dwServerCount].dwIpAddr == INADDR_NONE)
693 {
694 if (!(g_dwFlags & AF_DAEMON))
695 printf("Invalid server address '%s'\n", pItem);
696 }
697 else
698 {
699 DWORD i;
700
701 for(i = 0; i < g_dwServerCount; i++)
702 if ((g_pServerList[i].dwIpAddr == ipAddr) && (g_pServerList[i].dwNetMask == netMask))
703 break;
704 if (i == g_dwServerCount)
705 {
706 g_dwServerCount++;
707 g_pServerList[i].dwIpAddr = ipAddr;
708 g_pServerList[i].dwNetMask = netMask;
709 }
710 g_pServerList[i].bMasterServer = isMaster;
711 g_pServerList[i].bControlServer = isControl;
712 }
713 }
714 free(serverList);
715}
716
717
5039dede
AK
718//
719// Initialization routine
720//
721
c6fbf758 722BOOL Initialize()
5039dede 723{
bf3b7f79
VK
724 TCHAR *pItem, *pEnd;
725 TCHAR regPath[MAX_PATH];
5039dede
AK
726
727 // Open log file
4addc3a3
VK
728 if (!(g_dwFlags & AF_USE_SYSLOG))
729 {
5945d50a 730 if (!nxlog_set_rotation_policy((int)m_dwLogRotationMode, (int)m_dwMaxLogSize, (int)m_dwLogHistorySize, m_szDailyLogFileSuffix))
4addc3a3 731 if (!(g_dwFlags & AF_DAEMON))
bf3b7f79 732 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
4addc3a3 733 }
9fcdfda7
VK
734 if (!nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NXAGENTD_SYSLOG_NAME : g_szLogFile,
735 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
736 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
737 _T("NXAGENTD.EXE"),
5039dede 738#ifdef _WIN32
9fcdfda7 739 0, NULL))
5039dede 740#else
9fcdfda7 741 g_dwNumMessages, g_szMessages))
5039dede 742#endif
9fcdfda7 743 {
bf3b7f79 744 _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
9fcdfda7
VK
745 return FALSE;
746 }
203e9d8a
VK
747 nxlog_write(MSG_USE_CONFIG_D, NXLOG_INFO, "s", g_szConfigIncludeDir);
748 nxlog_write(MSG_DEBUG_LEVEL, NXLOG_INFO, "d", g_debugLevel);
749
0df58041
VK
750 if (_tcscmp(g_masterAgent, _T("not_set")))
751 {
752 g_dwFlags |= AF_SUBAGENT_LOADER;
753 DebugPrintf(INVALID_INDEX, 1, _T("Switched to external subagent loader mode, master agent address is %s"), g_masterAgent);
754 }
755
203e9d8a
VK
756 // Initialize persistent storage
757 s_registry = new Config;
758 s_registry->setTopLevelTag(_T("registry"));
759 nx_strncpy(regPath, g_szDataDirectory, MAX_PATH - _tcslen(REGISTRY_FILE_NAME) - 1);
760 if (regPath[_tcslen(regPath) - 1] != FS_PATH_SEPARATOR_CHAR)
761 _tcscat(regPath, FS_PATH_SEPARATOR);
762 _tcscat(regPath, REGISTRY_FILE_NAME);
763 if (!s_registry->loadXmlConfig(regPath, "registry"))
764 {
765 nxlog_write(MSG_REGISTRY_LOAD_FAILED, NXLOG_ERROR, "s", regPath);
766 SaveRegistry();
767 }
5039dede
AK
768
769#ifdef _WIN32
770 WSADATA wsaData;
1ddf3f0c
VK
771 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
772 if (wrc != 0)
5039dede 773 {
1ddf3f0c 774 nxlog_write(MSG_WSASTARTUP_FAILED, NXLOG_ERROR, "e", wrc);
5039dede
AK
775 return FALSE;
776 }
5039dede
AK
777#endif
778
5039dede 779 // Initialize API for subagents
f480bdd4 780 InitSubAgentAPI(WriteSubAgentMsg, SendTrap, SendTrap, SendFileToServer, PushData);
bf3b7f79 781 DebugPrintf(INVALID_INDEX, 1, _T("Subagent API initialized"));
5039dede
AK
782
783 // Initialize cryptografy
784 if (!InitCryptoLib(m_dwEnabledCiphers))
785 {
786 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
787 return FALSE;
788 }
789
0df58041
VK
790 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
791 {
792 InitSessionList();
793
794 // Initialize built-in parameters
795 if (!InitParameterList())
796 return FALSE;
5039dede
AK
797
798#ifdef _WIN32
0df58041
VK
799 // Dynamically import functions that may not be presented in all Windows versions
800 ImportSymbols();
5039dede
AK
801#endif
802
968031d9 803 // Parse server lists
0df58041 804 if (m_pszServerList != NULL)
968031d9 805 ParseServerList(m_pszServerList, FALSE, FALSE);
0df58041 806 if (m_pszControlServerList != NULL)
968031d9
VK
807 ParseServerList(m_pszControlServerList, TRUE, FALSE);
808 if (m_pszMasterServerList != NULL)
809 ParseServerList(m_pszMasterServerList, TRUE, TRUE);
5039dede 810
0df58041
VK
811 // Add built-in actions
812 AddAction("Agent.Restart", AGENT_ACTION_SUBAGENT, NULL, H_RestartAgent, "CORE", "Restart agent");
5039dede 813
0df58041 814 // Load platform subagents
968031d9 815#if !defined(_WIN32)
0df58041 816 InitStaticSubagents();
5039dede 817#endif
0df58041
VK
818 if (g_dwFlags & AF_ENABLE_AUTOLOAD)
819 {
5039dede 820#ifdef _WIN32
0df58041 821 LoadWindowsSubagent();
5039dede 822#else
0df58041 823 LoadPlatformSubagent();
5039dede 824#endif
0df58041
VK
825 }
826 }
5039dede
AK
827
828 // Wait for external process if requested
829 if (m_szProcessToWait[0] != 0)
830 {
c303351c 831 DebugPrintf(INVALID_INDEX, 1, "Waiting for process %s", m_szProcessToWait);
5039dede
AK
832 if (!WaitForProcess(m_szProcessToWait))
833 nxlog_write(MSG_WAITFORPROCESS_FAILED, EVENTLOG_ERROR_TYPE, "s", m_szProcessToWait);
834 }
835
b34606f7
VK
836 DBSetDebugPrintCallback(DBLibraryDebugCallback);
837 DBInit(MSG_DB_LIBRARY, MSG_SQL_ERROR);
838
5039dede
AK
839 // Load other subagents
840 if (m_pszSubagentList != NULL)
841 {
e6c91aac 842 for(pItem = pEnd = m_pszSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
843 {
844 pEnd = strchr(pItem, '\n');
845 if (pEnd != NULL)
846 *pEnd = 0;
847 StrStrip(pItem);
848 LoadSubAgent(pItem);
849 }
850 free(m_pszSubagentList);
851 }
852
853 // Parse action list
854 if (m_pszActionList != NULL)
855 {
e6c91aac 856 for(pItem = pEnd = m_pszActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
857 {
858 pEnd = strchr(pItem, '\n');
859 if (pEnd != NULL)
860 *pEnd = 0;
861 StrStrip(pItem);
862 if (!AddActionFromConfig(pItem, FALSE))
863 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
864 }
865 free(m_pszActionList);
866 }
867 if (m_pszShellActionList != NULL)
868 {
e6c91aac 869 for(pItem = pEnd = m_pszShellActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede 870 {
bf3b7f79 871 pEnd = _tcschr(pItem, _T('\n'));
5039dede
AK
872
873 if (pEnd != NULL)
874 *pEnd = 0;
875 StrStrip(pItem);
876 if (!AddActionFromConfig(pItem, TRUE))
877 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
878 }
879 free(m_pszShellActionList);
880 }
881
882 // Parse external parameters list
883 if (m_pszExtParamList != NULL)
884 {
e6c91aac 885 for(pItem = pEnd = m_pszExtParamList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
886 {
887 pEnd = strchr(pItem, '\n');
888 if (pEnd != NULL)
889 *pEnd = 0;
890 StrStrip(pItem);
891 if (!AddExternalParameter(pItem, FALSE))
892 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
893 }
894 free(m_pszExtParamList);
895 }
896 if (m_pszShExtParamList != NULL)
897 {
e6c91aac 898 for(pItem = pEnd = m_pszShExtParamList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
899 {
900 pEnd = strchr(pItem, '\n');
901 if (pEnd != NULL)
902 *pEnd = 0;
903 StrStrip(pItem);
904 if (!AddExternalParameter(pItem, TRUE))
905 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
906 }
907 free(m_pszShExtParamList);
908 }
909
9795113c
VK
910 // Parse external parameters providers list
911 if (m_pszParamProviderList != NULL)
912 {
913 for(pItem = pEnd = m_pszParamProviderList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
914 {
915 pEnd = strchr(pItem, '\n');
916 if (pEnd != NULL)
917 *pEnd = 0;
918 StrStrip(pItem);
919 if (!AddParametersProvider(pItem))
920 nxlog_write(MSG_ADD_PARAM_PROVIDER_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
921 }
922 free(m_pszParamProviderList);
923 }
924
204ace22 925 // Parse external subagents list
0df58041 926 if (!(g_dwFlags & AF_SUBAGENT_LOADER) && (m_pszExtSubagentList != NULL))
204ace22
VK
927 {
928 for(pItem = pEnd = m_pszExtSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
929 {
930 pEnd = strchr(pItem, '\n');
931 if (pEnd != NULL)
932 *pEnd = 0;
933 StrStrip(pItem);
934 if (!AddExternalSubagent(pItem))
935 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
936 }
937 free(m_pszExtSubagentList);
938 }
939
5039dede
AK
940 ThreadSleep(1);
941
942 // If StartupDelay is greater than zero, then wait
943 if (g_dwStartupDelay > 0)
944 {
945 if (g_dwFlags & AF_DAEMON)
946 {
947 ThreadSleep(g_dwStartupDelay);
948 }
949 else
950 {
951 DWORD i;
952
953 printf("XXXXXX%*s]\rWAIT [", g_dwStartupDelay, " ");
954 fflush(stdout);
955 for(i = 0; i < g_dwStartupDelay; i++)
956 {
957 ThreadSleep(1);
958 putc('.', stdout);
959 fflush(stdout);
960 }
961 printf("\n");
962 }
963 }
964
9795113c
VK
965 StartParamProvidersPoller();
966
5039dede
AK
967 // Agent start time
968 g_tmAgentStartTime = time(NULL);
969
0df58041
VK
970 if (g_dwFlags & AF_SUBAGENT_LOADER)
971 {
972 m_thMasterAgentListener = ThreadCreateEx(MasterAgentListener, 0, NULL);
973 }
974 else
975 {
976 // Start network listener and session watchdog
977 m_thListener = ThreadCreateEx(ListenerThread, 0, NULL);
978 m_thSessionWatchdog = ThreadCreateEx(SessionWatchdog, 0, NULL);
979 m_thTrapSender = ThreadCreateEx(TrapSender, 0, NULL);
0bd64860 980 StartPushConnector();
0df58041 981 }
5039dede 982
968031d9 983#if defined(_WIN32)
5039dede
AK
984 m_hCondShutdown = ConditionCreate(TRUE);
985#endif
986 ThreadSleep(1);
987
9fcdfda7 988 // Start watchdog process
0df58041
VK
989 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
990 {
991 if (g_dwFlags & AF_ENABLE_WATCHDOG)
992 StartWatchdog();
993 }
9fcdfda7 994
c6fbf758
VK
995 delete g_config;
996
5039dede
AK
997 return TRUE;
998}
999
1000
1001//
1002// Shutdown routine
1003//
1004
0df58041 1005void Shutdown()
5039dede 1006{
8e954797 1007 DebugPrintf(INVALID_INDEX, 2, _T("Shutdown() called"));
9fcdfda7
VK
1008 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1009 StopWatchdog();
1010
5039dede 1011 g_dwFlags |= AF_SHUTDOWN;
0df58041
VK
1012
1013 if (g_dwFlags & AF_SUBAGENT_LOADER)
1014 {
639088d4
VK
1015 // TODO: shall we inform master agent listener about shutdown?
1016 //ThreadJoin(m_thMasterAgentListener);
0df58041
VK
1017 }
1018 else
1019 {
1020 ShutdownTrapSender();
1021 ThreadJoin(m_thSessionWatchdog);
1022 ThreadJoin(m_thListener);
1023 ThreadJoin(m_thTrapSender);
1024 }
5039dede
AK
1025
1026 UnloadAllSubAgents();
1027 nxlog_write(MSG_AGENT_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
1028 nxlog_close();
1029
1030 // Notify main thread about shutdown
1031#ifdef _WIN32
1032 ConditionSet(m_hCondShutdown);
1033#endif
1034
1035 // Remove PID file
968031d9 1036#if !defined(_WIN32)
5039dede
AK
1037 remove(g_szPidFile);
1038#endif
1039}
1040
1041
1042//
1043// Common Main()
1044//
1045
8ec102ac 1046void Main()
5039dede 1047{
8ec102ac 1048 nxlog_write(MSG_AGENT_STARTED, NXLOG_INFO, NULL);
5039dede
AK
1049
1050 if (g_dwFlags & AF_DAEMON)
1051 {
968031d9 1052#if defined(_WIN32)
5039dede
AK
1053 ConditionWait(m_hCondShutdown, INFINITE);
1054#else
1055 StartMainLoop(SignalHandler, NULL);
1056#endif
1057 }
1058 else
1059 {
1060#if defined(_WIN32)
1061 if (g_dwFlags & AF_HIDE_WINDOW)
1062 {
1063 HWND hWnd;
1064
1065 hWnd = GetConsoleHWND();
1066 if (hWnd != NULL)
1067 ShowWindow(hWnd, SW_HIDE);
1068 ConditionWait(m_hCondShutdown, INFINITE);
1069 ThreadSleep(1);
1070 }
1071 else
1072 {
1073 printf("Agent running. Press ESC to shutdown.\n");
1074 while(1)
1075 {
3973f74b 1076 if (_getch() == 27)
5039dede
AK
1077 break;
1078 }
1079 printf("Agent shutting down...\n");
1080 Shutdown();
1081 }
5039dede
AK
1082#else
1083 printf("Agent running. Press Ctrl+C to shutdown.\n");
1084 StartMainLoop(SignalHandler, NULL);
1085 printf("\nStopping agent...\n");
1086#endif
1087 }
1088}
1089
1090
1091//
1092// Do necessary actions on agent restart
1093//
1094
1095static void DoRestartActions(DWORD dwOldPID)
1096{
1097#if defined(_WIN32)
1098 if (dwOldPID == 0)
1099 {
1100 // Service
1101 StopAgentService();
1102 WaitForService(SERVICE_STOPPED);
1103 StartAgentService();
1104 ExitProcess(0);
1105 }
1106 else
1107 {
1108 HANDLE hProcess;
1109
1110 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwOldPID);
1111 if (hProcess != NULL)
1112 {
1113 if (WaitForSingleObject(hProcess, 60000) == WAIT_TIMEOUT)
1114 {
1115 TerminateProcess(hProcess, 0);
1116 }
1117 CloseHandle(hProcess);
1118 }
1119 }
5039dede
AK
1120#else
1121 int i;
1122
1123 kill(dwOldPID, SIGTERM);
1124 for(i = 0; i < 30; i++)
1125 {
1126 sleep(2);
1127 if (kill(dwOldPID, SIGCONT) == -1)
1128 break;
1129 }
1130
1131 // Kill previous instance of agent if it's still running
1132 if (i == 30)
1133 kill(dwOldPID, SIGKILL);
1134#endif
1135}
1136
1137
5039dede
AK
1138//
1139// Create configuration file
1140//
1141
48dd3c80
VK
1142static int CreateConfig(const TCHAR *pszServer, const TCHAR *pszLogFile, const TCHAR *pszFileStore,
1143 const TCHAR *configIncludeDir, int iNumSubAgents, TCHAR **ppszSubAgentList)
5039dede
AK
1144{
1145 FILE *fp;
1146 time_t currTime;
1147 int i;
1148
1149 if (_taccess(g_szConfigFile, 0) == 0)
1150 return 0; // File already exist, we shouldn't overwrite it
1151
1152 fp = _tfopen(g_szConfigFile, _T("w"));
1153 if (fp != NULL)
1154 {
1155 currTime = time(NULL);
1156 _ftprintf(fp, _T("#\n# NetXMS agent configuration file\n# Created by agent installer at %s#\n\n"),
1157 _tctime(&currTime));
48dd3c80
VK
1158 _ftprintf(fp, _T("MasterServers = %s\nConfigIncludeDir = %s\nLogFile = %s\nFileStore = %s\n"),
1159 pszServer, configIncludeDir, pszLogFile, pszFileStore);
5039dede
AK
1160 for(i = 0; i < iNumSubAgents; i++)
1161 _ftprintf(fp, _T("SubAgent = %s\n"), ppszSubAgentList[i]);
1162 fclose(fp);
1163 }
1164 return (fp != NULL) ? 0 : 2;
1165}
1166
1167
e6c91aac
VK
1168//
1169// Init config
1170//
1171
1172static void InitConfig()
1173{
1174 g_config = new Config();
8a0dffac 1175 g_config->setTopLevelTag(_T("config"));
e6c91aac
VK
1176}
1177
1178
5039dede
AK
1179//
1180// Startup
1181//
1182
1183int main(int argc, char *argv[])
1184{
1185 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
1186 BOOL bRestart = FALSE;
9fcdfda7 1187 DWORD dwOldPID, dwMainPID;
bf3b7f79 1188 TCHAR *eptr;
5039dede 1189#ifdef _WIN32
bf3b7f79 1190 TCHAR szModuleName[MAX_PATH];
1621a079
VK
1191 HKEY hKey;
1192 DWORD dwSize;
1193#else
1194 char *pszEnv;
5039dede
AK
1195#endif
1196
1197 InitThreadLibrary();
9fcdfda7
VK
1198
1199#ifdef NETXMS_MEMORY_DEBUG
1200 InitMemoryDebugger();
1201#endif
5039dede 1202
5039dede
AK
1203 // Set locale to C. It shouldn't be needed, according to
1204 // documentation, but I've seen the cases when agent formats
1205 // floating point numbers by sprintf inserting comma in place
1206 // of a dot, as set by system's regional settings.
1207#ifdef _WIN32
1208 setlocale(LC_ALL, "C");
1209#endif
1210
1621a079
VK
1211 // Check for alternate config file location
1212#ifdef _WIN32
1213 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Agent"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
1214 {
1215 dwSize = MAX_PATH * sizeof(TCHAR);
1216 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
42acd918 1217 dwSize = MAX_PATH * sizeof(TCHAR);
1621a079
VK
1218 RegQueryValueEx(hKey, _T("ConfigIncludeDir"), NULL, NULL, (BYTE *)g_szConfigIncludeDir, &dwSize);
1219 RegCloseKey(hKey);
1220 }
1221#else
1222 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG"));
1223 if (pszEnv != NULL)
1224 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
1225
1226 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG_D"));
1227 if (pszEnv != NULL)
1228 nx_strncpy(g_szConfigIncludeDir, pszEnv, MAX_PATH);
1229#endif
1230
5039dede
AK
1231 // Parse command line
1232 if (argc == 1)
1233 iAction = ACTION_HELP;
1234 opterr = 1;
1235 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
1236 {
1237 switch(ch)
1238 {
1239 case 'h': // Display help and exit
1240 iAction = ACTION_HELP;
1241 break;
1242 case 'd': // Run as daemon
1243 g_dwFlags |= AF_DAEMON;
1244 break;
1245 case 'f': // Run in foreground
1246 g_dwFlags &= ~AF_DAEMON;
1247 break;
1248 case 'D': // Turn on debug output
53c96e2d
VK
1249 g_debugLevel = strtoul(optarg, &eptr, 0);
1250 if ((*eptr != 0) || (g_debugLevel > 9))
c303351c
VK
1251 {
1252 fprintf(stderr, "Invalid debug level: %s\n", optarg);
1253 iAction = -1;
1254 iExitCode = 1;
1255 }
5039dede
AK
1256 break;
1257 case 'c': // Configuration file
1258 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1259 break;
968031d9 1260#if !defined(_WIN32)
5039dede
AK
1261 case 'p': // PID file
1262 nx_strncpy(g_szPidFile, optarg, MAX_PATH);
1263 break;
1264#endif
1265 case 'C': // Configuration check only
1266 iAction = ACTION_CHECK_CONFIG;
1267 break;
1268 case 'v': // Print version and exit
1269 printf("NetXMS Core Agent Version " AGENT_VERSION_STRING "\n");
1270 iAction = ACTION_NONE;
1271 break;
1272 case 'M':
1273 g_dwFlags |= AF_CENTRAL_CONFIG;
1274 nx_strncpy(g_szConfigServer, optarg, MAX_DB_STRING);
1275 break;
1276 case 'r':
1277 g_dwFlags |= AF_REGISTER;
1278 nx_strncpy(g_szRegistrar, optarg, MAX_DB_STRING);
1279 break;
1280 case 'P': // Platform suffix
1281 nx_strncpy(g_szPlatformSuffix, optarg, MAX_PSUFFIX_LENGTH);
1282 break;
1283 case 'X': // Agent is being restarted
1284 bRestart = TRUE;
1285 dwOldPID = strtoul(optarg, NULL, 10);
1286 break;
9fcdfda7
VK
1287 case 'W': // Watchdog process
1288 iAction = ACTION_RUN_WATCHDOG;
1289 dwMainPID = strtoul(optarg, NULL, 10);
1290 break;
5039dede
AK
1291 case 'Z': // Create configuration file
1292 iAction = ACTION_CREATE_CONFIG;
1293 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1294 break;
1295#ifdef _WIN32
1296 case 'H': // Hide window
1297 g_dwFlags |= AF_HIDE_WINDOW;
1298 break;
175bd003
VK
1299 case 'i':
1300 g_dwFlags |= AF_INTERACTIVE_SERVICE;
1301 break;
5039dede
AK
1302 case 'I': // Install Windows service
1303 iAction = ACTION_INSTALL_SERVICE;
1304 break;
1305 case 'R': // Remove Windows service
1306 iAction = ACTION_REMOVE_SERVICE;
1307 break;
1308 case 's': // Start Windows service
1309 iAction = ACTION_START_SERVICE;
1310 break;
1311 case 'S': // Stop Windows service
1312 iAction = ACTION_STOP_SERVICE;
1313 break;
1314 case 'E': // Install Windows event source
1315 iAction = ACTION_INSTALL_EVENT_SOURCE;
1316 break;
1317 case 'U': // Remove Windows event source
1318 iAction = ACTION_REMOVE_EVENT_SOURCE;
1319 break;
9f96fc88
VK
1320 case 'e': // Event source name
1321 nx_strncpy(g_windowsEventSourceName, optarg, MAX_PATH);
1322 break;
1323 case 'n': // Service name
1324 nx_strncpy(g_windowsServiceName, optarg, MAX_PATH);
1325 break;
1326 case 'N': // Service display name
1327 nx_strncpy(g_windowsServiceDisplayName, optarg, MAX_PATH);
1328 break;
5039dede
AK
1329#endif
1330 case '?':
1331 iAction = ACTION_HELP;
1332 iExitCode = 1;
1333 break;
1334 default:
1335 break;
1336 }
1337 }
1338
968031d9 1339#if !defined(_WIN32)
5039dede
AK
1340 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1341 {
1342 if (access(PREFIX "/etc/nxagentd.conf", 4) == 0)
1343 {
1344 _tcscpy(g_szConfigFile, PREFIX "/etc/nxagentd.conf");
1345 }
1346 else if (access("/usr/etc/nxagentd.conf", 4) == 0)
1347 {
1348 _tcscpy(g_szConfigFile, "/usr/etc/nxagentd.conf");
1349 }
1350 else
1351 {
1352 _tcscpy(g_szConfigFile, "/etc/nxagentd.conf");
1353 }
1354 }
80ce37ac 1355 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
abf35d58
VK
1356 {
1357 if (access(PREFIX "/etc/nxagentd.conf.d", 4) == 0)
1358 {
80ce37ac 1359 _tcscpy(g_szConfigIncludeDir, PREFIX "/etc/nxagentd.conf.d");
abf35d58
VK
1360 }
1361 else if (access("/usr/etc/nxagentd.conf.d", 4) == 0)
1362 {
80ce37ac 1363 _tcscpy(g_szConfigIncludeDir, "/usr/etc/nxagentd.conf.d");
abf35d58
VK
1364 }
1365 else
1366 {
80ce37ac 1367 _tcscpy(g_szConfigIncludeDir, "/etc/nxagentd.conf.d");
abf35d58
VK
1368 }
1369 }
5039dede
AK
1370#endif
1371
1372 if (bRestart)
1373 DoRestartActions(dwOldPID);
1374
e6c91aac
VK
1375 InitConfig();
1376
5039dede
AK
1377 // Do requested action
1378 switch(iAction)
1379 {
1380 case ACTION_RUN_AGENT:
1381 // Set default value for session idle timeout based on
1382 // connect() timeout, if possible
1383#if HAVE_SYSCTLBYNAME && !defined(_IPSO)
1384 {
1385 LONG nVal;
1386 size_t nSize;
1387
1388 nSize = sizeof(nVal);
1389 if (sysctlbyname("net.inet.tcp.keepinit", &nVal, &nSize, NULL, 0) == 0)
1390 {
1391 g_dwIdleTimeout = nVal / 1000 + 15;
1392 }
1393 }
1394#endif
1395
1396 if (g_dwFlags & AF_CENTRAL_CONFIG)
1397 {
c303351c 1398 if (g_debugLevel > 0)
5039dede
AK
1399 printf("Downloading configuration from %s...\n", g_szConfigServer);
1400 if (DownloadConfig(g_szConfigServer))
1401 {
c303351c 1402 if (g_debugLevel > 0)
5039dede
AK
1403 printf("Configuration downloaded successfully\n");
1404 }
1405 else
1406 {
c303351c 1407 if (g_debugLevel > 0)
5039dede
AK
1408 printf("Configuration download failed\n");
1409 }
1410 }
1411
abf35d58 1412 if (g_config->loadConfig(g_szConfigFile, _T("agent")))
e6c91aac 1413 {
357a2954
AK
1414 const TCHAR *dir = g_config->getValue(_T("/agent/ConfigIncludeDir"));
1415 if (dir != NULL)
6ca3b41c
VK
1416 nx_strncpy(g_szConfigIncludeDir, dir, MAX_PATH);
1417 g_config->loadConfigDirectory(g_szConfigIncludeDir, _T("agent"));
abf35d58
VK
1418 if (g_config->parseTemplate(_T("agent"), m_cfgTemplate))
1419 {
0bbab9d3 1420 // Set exception handler
9fcdfda7 1421#ifdef _WIN32
abf35d58
VK
1422 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
1423 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter, m_szDumpDir,
bf3b7f79 1424 _T("nxagentd"), MSG_EXCEPTION, g_dwFlags & AF_WRITE_FULL_DUMP, !(g_dwFlags & AF_DAEMON));
abf35d58 1425 __try {
9fcdfda7 1426#endif
bf3b7f79
VK
1427 if ((!_tcsicmp(g_szLogFile, _T("{syslog}"))) ||
1428 (!_tcsicmp(g_szLogFile, _T("{eventlog}"))))
abf35d58 1429 g_dwFlags |= AF_USE_SYSLOG;
5039dede
AK
1430
1431#ifdef _WIN32
abf35d58
VK
1432 if (g_dwFlags & AF_DAEMON)
1433 {
1434 InitService();
1435 }
1436 else
1437 {
1438 if (Initialize())
1439 {
1440 Main();
1441 }
1442 else
1443 {
bf3b7f79 1444 ConsolePrintf(_T("Agent initialization failed\n"));
abf35d58
VK
1445 nxlog_close();
1446 iExitCode = 3;
1447 }
1448 }
5039dede 1449#else /* _WIN32 */
abf35d58
VK
1450 if (g_dwFlags & AF_DAEMON)
1451 if (daemon(0, 0) == -1)
1452 {
1453 perror("Unable to setup itself as a daemon");
1454 iExitCode = 4;
1455 }
abf35d58 1456 if (iExitCode == 0)
5039dede 1457 {
abf35d58 1458 m_pid = getpid();
abf35d58
VK
1459 if (Initialize())
1460 {
abf35d58
VK
1461 FILE *fp;
1462
1463 // Write PID file
1464 fp = fopen(g_szPidFile, "w");
1465 if (fp != NULL)
1466 {
1467 fprintf(fp, "%d", m_pid);
1468 fclose(fp);
1469 }
abf35d58
VK
1470 Main();
1471 Shutdown();
1472 }
1473 else
1474 {
bf3b7f79 1475 ConsolePrintf(_T("Agent initialization failed\n"));
abf35d58
VK
1476 nxlog_close();
1477 iExitCode = 3;
1478 }
1479 }
5039dede
AK
1480#endif /* _WIN32 */
1481
968031d9 1482#if defined(_WIN32)
abf35d58
VK
1483 if (m_hCondShutdown != INVALID_CONDITION_HANDLE)
1484 ConditionDestroy(m_hCondShutdown);
9fcdfda7
VK
1485#endif
1486#ifdef _WIN32
abf35d58 1487 LIBNETXMS_EXCEPTION_HANDLER
5039dede 1488#endif
abf35d58
VK
1489 }
1490 else
1491 {
bf3b7f79 1492 ConsolePrintf(_T("Error parsing configuration file\n"));
abf35d58
VK
1493 iExitCode = 2;
1494 }
5039dede
AK
1495 }
1496 else
1497 {
bf3b7f79 1498 ConsolePrintf(_T("Error loading configuration file\n"));
5039dede
AK
1499 iExitCode = 2;
1500 }
1501 break;
1502 case ACTION_CHECK_CONFIG:
97a92859 1503 if (!g_config->loadIniConfig(g_szConfigFile, _T("agent")) || !g_config->parseTemplate(_T("agent"), m_cfgTemplate))
5039dede 1504 {
bf3b7f79 1505 ConsolePrintf(_T("Configuration file check failed\n"));
5039dede
AK
1506 iExitCode = 2;
1507 }
1508 break;
9fcdfda7
VK
1509 case ACTION_RUN_WATCHDOG:
1510 iExitCode = WatchdogMain(dwMainPID);
1511 break;
5039dede
AK
1512 case ACTION_CREATE_CONFIG:
1513 iExitCode = CreateConfig(CHECK_NULL(argv[optind]), CHECK_NULL(argv[optind + 1]),
48dd3c80
VK
1514 CHECK_NULL(argv[optind + 2]), CHECK_NULL(argv[optind + 3]),
1515 argc - optind - 4, &argv[optind + 4]);
5039dede
AK
1516 break;
1517#ifdef _WIN32
1518 case ACTION_INSTALL_SERVICE:
1519 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
1520 InstallService(szModuleName, g_szConfigFile);
1521 break;
1522 case ACTION_REMOVE_SERVICE:
1523 RemoveService();
1524 break;
1525 case ACTION_INSTALL_EVENT_SOURCE:
1526 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
1527 InstallEventSource(szModuleName);
1528 break;
1529 case ACTION_REMOVE_EVENT_SOURCE:
1530 RemoveEventSource();
1531 break;
1532 case ACTION_START_SERVICE:
1533 StartAgentService();
1534 break;
1535 case ACTION_STOP_SERVICE:
1536 StopAgentService();
1537 break;
1538#endif
1539 case ACTION_HELP:
9fcdfda7 1540 fputs(m_szHelpText, stdout);
5039dede
AK
1541 break;
1542 default:
1543 break;
1544 }
1545
5039dede
AK
1546 return iExitCode;
1547}