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