89b34ac2c260d98f81904234dbbc8a07e630e0b8
[public/netxms.git] / src / agent / core / nxagentd.cpp
1 /*
2 ** NetXMS multiplatform core agent
3 ** Copyright (C) 2003-2017 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
25 #if defined(_WIN32)
26 #include <shlobj.h>
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 #ifdef _WITH_ENCRYPTION
46 #include <openssl/ssl.h>
47 #endif
48
49 #if HAVE_GRP_H
50 #include <grp.h>
51 #endif
52
53 #if HAVE_PWD_H
54 #include <pwd.h>
55 #endif
56
57 #define DEFAULT_CONFIG_SECTION _T("CORE")
58
59 /**
60 * Externals
61 */
62 THREAD_RESULT THREAD_CALL ListenerThread(void *);
63 THREAD_RESULT THREAD_CALL SessionWatchdog(void *);
64 THREAD_RESULT THREAD_CALL TrapSender(void *);
65 THREAD_RESULT THREAD_CALL MasterAgentListener(void *arg);
66 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
67 THREAD_RESULT THREAD_CALL SNMPTrapSender(void *);
68 THREAD_RESULT THREAD_CALL SyslogReceiver(void *);
69 THREAD_RESULT THREAD_CALL SyslogSender(void *);
70 THREAD_RESULT THREAD_CALL TunnelManager(void *);
71
72 void ShutdownTrapSender();
73 void ShutdownSNMPTrapSender();
74
75 void ShutdownSyslogSender();
76
77 void StartLocalDataCollector();
78 void ShutdownLocalDataCollector();
79
80 void StartWatchdog();
81 void StopWatchdog();
82 int WatchdogMain(DWORD pid);
83
84 void InitSessionList();
85 void DestroySessionList();
86
87 BOOL RegisterOnServer(const TCHAR *pszServer);
88
89 void UpdatePolicyInventory();
90
91 void ParseTunnelList(TCHAR *list);
92
93 #if !defined(_WIN32)
94 void InitStaticSubagents();
95 #endif
96
97 #ifdef _WIN32
98 extern TCHAR g_windowsServiceName[];
99 extern TCHAR g_windowsServiceDisplayName[];
100 #endif
101
102 void LIBNXAGENT_EXPORTABLE InitSubAgentAPI(void (* writeLog)(int, int, const TCHAR *),
103 void (* sendTrap1)(UINT32, const TCHAR *, const char *, va_list),
104 void (* sendTrap2)(UINT32, const TCHAR *, int, TCHAR **),
105 bool (* enumerateSessions)(EnumerationCallbackResult (*)(AbstractCommSession *, void *), void*),
106 AbstractCommSession *(* findServerSession)(UINT64),
107 bool (* sendFile)(void *, UINT32, const TCHAR *, long, bool),
108 bool (* pushData)(const TCHAR *, const TCHAR *, UINT32, time_t),
109 DB_HANDLE (* getLocalDatabaseHandle)(),
110 CONDITION shutdownCondition, const TCHAR *dataDirectory);
111
112 /**
113 * Messages generated by mc.pl (for UNIX version only)
114 */
115 #ifndef _WIN32
116 extern unsigned int g_dwNumMessages;
117 extern const TCHAR *g_szMessages[];
118 #endif
119
120 /**
121 * OpenSSL APPLINK
122 */
123 #ifdef _WIN32
124 #include <openssl/applink.c>
125 #endif
126
127 /**
128 * Valid options for getopt()
129 */
130 #if defined(_WIN32)
131 #define VALID_OPTIONS "c:CdD:e:EfG:hHiIKM:n:N:P:r:RsSUvW:X:Z:"
132 #else
133 #define VALID_OPTIONS "c:CdD:fg:G:hKM:p:P:r:u:vW:X:Z:"
134 #endif
135
136 /**
137 * Actions
138 */
139 #define ACTION_NONE 0
140 #define ACTION_RUN_AGENT 1
141 #define ACTION_INSTALL_SERVICE 2
142 #define ACTION_REMOVE_SERVICE 3
143 #define ACTION_START_SERVICE 4
144 #define ACTION_STOP_SERVICE 5
145 #define ACTION_CHECK_CONFIG 6
146 #define ACTION_INSTALL_EVENT_SOURCE 7
147 #define ACTION_REMOVE_EVENT_SOURCE 8
148 #define ACTION_CREATE_CONFIG 9
149 #define ACTION_HELP 10
150 #define ACTION_RUN_WATCHDOG 11
151 #define ACTION_SHUTDOWN_EXT_AGENTS 12
152
153 /**
154 * Global variables
155 */
156 UINT32 g_dwFlags = AF_ENABLE_ACTIONS | AF_ENABLE_AUTOLOAD | AF_WRITE_FULL_DUMP;
157 UINT32 g_failFlags = 0;
158 TCHAR g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
159 TCHAR g_szSharedSecret[MAX_SECRET_LENGTH] = _T("admin");
160 TCHAR g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
161 TCHAR g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
162 TCHAR g_szDataDirectory[MAX_PATH] = AGENT_DEFAULT_DATA_DIR;
163 TCHAR g_szPlatformSuffix[MAX_PSUFFIX_LENGTH] = _T("");
164 TCHAR g_szConfigServer[MAX_DB_STRING] = _T("not_set");
165 TCHAR g_szRegistrar[MAX_DB_STRING] = _T("not_set");
166 TCHAR g_szListenAddress[MAX_PATH] = _T("*");
167 TCHAR g_szConfigIncludeDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
168 TCHAR g_szConfigPolicyDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
169 TCHAR g_szLogParserDirectory[MAX_PATH] = _T("");
170 TCHAR g_certificateDirectory[MAX_PATH] = _T("");
171 TCHAR g_masterAgent[MAX_PATH] = _T("not_set");
172 TCHAR g_szSNMPTrapListenAddress[MAX_PATH] = _T("*");
173 UINT16 g_wListenPort = AGENT_LISTEN_PORT;
174 TCHAR g_systemName[MAX_OBJECT_NAME] = _T("");
175 ObjectArray<ServerInfo> g_serverList(8, 8, true);
176 UINT32 g_execTimeout = 2000; // External process execution timeout in milliseconds
177 UINT32 g_eppTimeout = 30; // External parameter processor timeout in seconds
178 UINT32 g_snmpTimeout = 1000;
179 UINT16 g_snmpTrapPort = 162;
180 time_t g_tmAgentStartTime;
181 UINT32 g_dwStartupDelay = 0;
182 UINT32 g_dwMaxSessions = 0;
183 UINT32 g_longRunningQueryThreshold = 250;
184 UINT32 g_dcReconciliationBlockSize = 1024;
185 UINT32 g_dcReconciliationTimeout = 15000;
186 UINT32 g_dcMaxCollectorPoolSize = 64;
187 UINT32 g_zoneUIN = 0;
188 UINT32 g_tunnelKeepaliveInterval = 30;
189 UINT16 g_syslogListenPort = 514;
190 #ifdef _WIN32
191 UINT16 g_sessionAgentPort = 28180;
192 #else
193 UINT16 g_sessionAgentPort = 0;
194 #endif
195 Config *g_config = NULL;
196 UINT32 g_dwIdleTimeout = 120; // Session idle timeout
197
198 #if !defined(_WIN32)
199 TCHAR g_szPidFile[MAX_PATH] = _T("/var/run/nxagentd.pid");
200 #endif
201
202 /**
203 * Static variables
204 */
205 static TCHAR *m_pszActionList = NULL;
206 static TCHAR *m_pszShellActionList = NULL;
207 static TCHAR *m_pszServerList = NULL;
208 static TCHAR *m_pszControlServerList = NULL;
209 static TCHAR *m_pszMasterServerList = NULL;
210 static TCHAR *m_pszSubagentList = NULL;
211 static TCHAR *s_externalParametersConfig = NULL;
212 static TCHAR *s_externalShellExecParametersConfig = NULL;
213 static TCHAR *s_externalParameterProvidersConfig = NULL;
214 static TCHAR *s_externalListsConfig = NULL;
215 static TCHAR *s_externalTablesConfig = NULL;
216 static TCHAR *s_externalSubAgentsList = NULL;
217 static TCHAR *s_appAgentsList = NULL;
218 static TCHAR *s_serverConnectionList = NULL;
219 static UINT32 s_enabledCiphers = 0xFFFF;
220 static THREAD s_sessionWatchdogThread = INVALID_THREAD_HANDLE;
221 static THREAD s_listenerThread = INVALID_THREAD_HANDLE;
222 static THREAD s_eventSenderThread = INVALID_THREAD_HANDLE;
223 static THREAD s_snmpTrapReceiverThread = INVALID_THREAD_HANDLE;
224 static THREAD s_snmpTrapSenderThread = INVALID_THREAD_HANDLE;
225 static THREAD s_syslogReceiverThread = INVALID_THREAD_HANDLE;
226 static THREAD s_syslogSenderThread = INVALID_THREAD_HANDLE;
227 static THREAD s_masterAgentListenerThread = INVALID_THREAD_HANDLE;
228 static THREAD s_tunnelManagerThread = INVALID_THREAD_HANDLE;
229 static TCHAR s_processToWaitFor[MAX_PATH] = _T("");
230 static TCHAR s_dumpDir[MAX_PATH] = _T("C:\\");
231 static UINT64 s_maxLogSize = 16384 * 1024;
232 static UINT32 s_logHistorySize = 4;
233 static UINT32 s_logRotationMode = NXLOG_ROTATION_BY_SIZE;
234 static TCHAR s_dailyLogFileSuffix[64] = _T("");
235 static TCHAR s_executableName[MAX_PATH];
236 static UINT32 s_debugLevel = (UINT32)NXCONFIG_UNINITIALIZED_VALUE;
237 static TCHAR *s_debugTags = NULL;
238
239 static CONDITION s_subAgentsStopCondition = INVALID_CONDITION_HANDLE;
240 #if defined(_WIN32)
241 static CONDITION s_shutdownCondition = INVALID_CONDITION_HANDLE;
242 #endif
243
244 #if !defined(_WIN32)
245 static pid_t s_pid;
246 #endif
247
248 /**
249 * Configuration file template
250 */
251 static NX_CFG_TEMPLATE m_cfgTemplate[] =
252 {
253 { _T("Action"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList, NULL },
254 { _T("ActionShellExec"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShellActionList, NULL },
255 { _T("AppAgent"), CT_STRING_LIST, '\n', 0, 0, 0, &s_appAgentsList, NULL },
256 { _T("BackgroundLogWriter"), CT_BOOLEAN, 0, 0, AF_BACKGROUND_LOG_WRITER, 0, &g_dwFlags, NULL },
257 { _T("ControlServers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszControlServerList, NULL },
258 { _T("CreateCrashDumps"), CT_BOOLEAN, 0, 0, AF_CATCH_EXCEPTIONS, 0, &g_dwFlags, NULL },
259 { _T("DataCollectionThreadPoolSize"), CT_LONG, 0, 0, 0, 0, &g_dcMaxCollectorPoolSize, NULL },
260 { _T("DataDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, g_szDataDirectory, NULL },
261 { _T("DataReconciliationBlockSize"), CT_LONG, 0, 0, 0, 0, &g_dcReconciliationBlockSize, NULL },
262 { _T("DataReconciliationTimeout"), CT_LONG, 0, 0, 0, 0, &g_dcReconciliationTimeout, NULL },
263 { _T("DailyLogFileSuffix"), CT_STRING, 0, 0, 64, 0, s_dailyLogFileSuffix, NULL },
264 { _T("DebugLevel"), CT_LONG, 0, 0, 0, 0, &s_debugLevel, &s_debugLevel },
265 { _T("DebugTags"), CT_STRING_LIST, ',', 0, 0, 0, &s_debugTags, NULL },
266 { _T("DisableIPv4"), CT_BOOLEAN, 0, 0, AF_DISABLE_IPV4, 0, &g_dwFlags, NULL },
267 { _T("DisableIPv6"), CT_BOOLEAN, 0, 0, AF_DISABLE_IPV6, 0, &g_dwFlags, NULL },
268 { _T("DumpDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, s_dumpDir, NULL },
269 { _T("EnableActions"), CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags, NULL },
270 { _T("EnabledCiphers"), CT_LONG, 0, 0, 0, 0, &s_enabledCiphers, NULL },
271 { _T("EnableControlConnector"), CT_BOOLEAN, 0, 0, AF_ENABLE_CONTROL_CONNECTOR, 0, &g_dwFlags, NULL },
272 { _T("EnableProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_PROXY, 0, &g_dwFlags, NULL },
273 { _T("EnableSNMPProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_PROXY, 0, &g_dwFlags, NULL },
274 { _T("EnableSNMPTrapProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_TRAP_PROXY, 0, &g_dwFlags, NULL },
275 { _T("EnableSyslogProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SYSLOG_PROXY, 0, &g_dwFlags, NULL },
276 { _T("EnableSubagentAutoload"), CT_BOOLEAN, 0, 0, AF_ENABLE_AUTOLOAD, 0, &g_dwFlags, NULL },
277 { _T("EnableWatchdog"), CT_BOOLEAN, 0, 0, AF_ENABLE_WATCHDOG, 0, &g_dwFlags, NULL },
278 { _T("EncryptedSharedSecret"), CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret, NULL },
279 { _T("ExecTimeout"), CT_LONG, 0, 0, 0, 0, &g_execTimeout, NULL },
280 { _T("ExternalList"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalListsConfig, NULL },
281 { _T("ExternalMasterAgent"), CT_STRING, 0, 0, MAX_PATH, 0, g_masterAgent, NULL },
282 { _T("ExternalParameter"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalParametersConfig, NULL },
283 { _T("ExternalParameterShellExec"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalShellExecParametersConfig, NULL },
284 { _T("ExternalParametersProvider"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalParameterProvidersConfig, NULL },
285 { _T("ExternalParameterProviderTimeout"), CT_LONG, 0, 0, 0, 0, &g_eppTimeout, NULL },
286 { _T("ExternalSubagent"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalSubAgentsList, NULL },
287 { _T("ExternalTable"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalTablesConfig, NULL },
288 { _T("FileStore"), CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore, NULL },
289 { _T("FullCrashDumps"), CT_BOOLEAN, 0, 0, AF_WRITE_FULL_DUMP, 0, &g_dwFlags, NULL },
290 { _T("ListenAddress"), CT_STRING, 0, 0, MAX_PATH, 0, g_szListenAddress, NULL },
291 { _T("ListenPort"), CT_WORD, 0, 0, 0, 0, &g_wListenPort, NULL },
292 { _T("LogFile"), CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile, NULL },
293 { _T("LogHistorySize"), CT_LONG, 0, 0, 0, 0, &s_logHistorySize, NULL },
294 { _T("LogRotationMode"), CT_LONG, 0, 0, 0, 0, &s_logRotationMode, NULL },
295 { _T("LogUnresolvedSymbols"), CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags, NULL },
296 { _T("LongRunningQueryThreshold"), CT_LONG, 0, 0, 0, 0, &g_longRunningQueryThreshold, NULL },
297 { _T("MasterServers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszMasterServerList, NULL },
298 { _T("MaxLogSize"), CT_SIZE_BYTES, 0, 0, 0, 0, &s_maxLogSize, NULL },
299 { _T("MaxSessions"), CT_LONG, 0, 0, 0, 0, &g_dwMaxSessions, NULL },
300 { _T("PlatformSuffix"), CT_STRING, 0, 0, MAX_PSUFFIX_LENGTH, 0, g_szPlatformSuffix, NULL },
301 { _T("RequireAuthentication"), CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags, NULL },
302 { _T("RequireEncryption"), CT_BOOLEAN, 0, 0, AF_REQUIRE_ENCRYPTION, 0, &g_dwFlags, NULL },
303 { _T("ServerConnection"), CT_STRING_LIST, '\n', 0, 0, 0, &s_serverConnectionList, NULL },
304 { _T("Servers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList, NULL },
305 { _T("SessionIdleTimeout"), CT_LONG, 0, 0, 0, 0, &g_dwIdleTimeout, NULL },
306 { _T("SessionAgentPort"), CT_WORD, 0, 0, 0, 0, &g_sessionAgentPort, NULL },
307 { _T("SharedSecret"), CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret, NULL },
308 { _T("SNMPTimeout"), CT_LONG, 0, 0, 0, 0, &g_snmpTimeout, NULL },
309 { _T("SNMPTrapListenAddress"), CT_STRING, 0, 0, MAX_PATH, 0, &g_szSNMPTrapListenAddress, NULL },
310 { _T("SNMPTrapPort"), CT_WORD, 0, 0, 0, 0, &g_snmpTrapPort, NULL },
311 { _T("StartupDelay"), CT_LONG, 0, 0, 0, 0, &g_dwStartupDelay, NULL },
312 { _T("SubAgent"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList, NULL },
313 { _T("SyslogListenPort"), CT_WORD, 0, 0, 0, 0, &g_syslogListenPort, NULL },
314 { _T("SystemName"), CT_STRING, 0, 0, MAX_OBJECT_NAME, 0, g_systemName, NULL },
315 { _T("TimeOut"), CT_IGNORE, 0, 0, 0, 0, NULL, NULL },
316 { _T("TunnelKeepaliveInterval"), CT_LONG, 0, 0, 0, 0, &g_tunnelKeepaliveInterval, NULL },
317 { _T("WaitForProcess"), CT_STRING, 0, 0, MAX_PATH, 0, s_processToWaitFor, NULL },
318 { _T("ZoneId"), CT_LONG, 0, 0, 0, 0, &g_zoneUIN, NULL }, // for backward compatibility
319 { _T("ZoneUIN"), CT_LONG, 0, 0, 0, 0, &g_zoneUIN, NULL },
320 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL, NULL }
321 };
322
323 /**
324 * Help text
325 */
326 static TCHAR m_szHelpText[] =
327 _T("Usage: nxagentd [options]\n")
328 _T("Where valid options are:\n")
329 _T(" -c <file> : Use configuration file <file> (default ") AGENT_DEFAULT_CONFIG _T(")\n")
330 _T(" -C : Load configuration file, dump resulting configuration, and exit\n")
331 _T(" -d : Run as daemon/service\n")
332 _T(" -D <level> : Set debug level (0..9)\n")
333 #ifdef _WIN32
334 _T(" -e <name> : Windows event source name\n")
335 #endif
336 _T(" -f : Run in foreground\n")
337 #if !defined(_WIN32)
338 _T(" -g <gid> : Change group ID to <gid> after start\n")
339 #endif
340 _T(" -G <name> : Use alternate global section <name> in configuration file\n")
341 _T(" -h : Display help and exit\n")
342 #ifdef _WIN32
343 _T(" -H : Hide agent's window when in standalone mode\n")
344 _T(" -i : Installed Windows service must be interactive\n")
345 _T(" -I : Install Windows service\n")
346 #endif
347 _T(" -K : Shutdown all connected external sub-agents\n")
348 _T(" -M <addr> : Download config from management server <addr>\n")
349 #ifdef _WIN32
350 _T(" -n <name> : Service name\n")
351 _T(" -N <name> : Service display name\n")
352 #endif
353 #if !defined(_WIN32)
354 _T(" -p : Path to pid file (default: /var/run/nxagentd.pid)\n")
355 #endif
356 _T(" -P <text> : Set platform suffix to <text>\n")
357 _T(" -r <addr> : Register agent on management server <addr>\n")
358 #ifdef _WIN32
359 _T(" -R : Remove Windows service\n")
360 _T(" -s : Start Windows servive\n")
361 _T(" -S : Stop Windows service\n")
362 #endif
363 #if !defined(_WIN32)
364 _T(" -u <uid> : Chhange user ID to <uid> after start\n")
365 #endif
366 _T(" -v : Display version and exit\n")
367 _T("\n");
368
369 /**
370 * Server info: constructor
371 */
372 ServerInfo::ServerInfo(const TCHAR *name, bool control, bool master)
373 {
374 #ifdef UNICODE
375 m_name = MBStringFromWideString(name);
376 #else
377 m_name = strdup(name);
378 #endif
379
380 char *p = strchr(m_name, '/');
381 if (p != NULL)
382 {
383 *p = 0;
384 p++;
385 m_address = InetAddress::resolveHostName(m_name);
386 if (m_address.isValid())
387 {
388 int bits = strtol(p, NULL, 10);
389 if ((bits >= 0) && (bits <= ((m_address.getFamily() == AF_INET) ? 32 : 128)))
390 m_address.setMaskBits(bits);
391 }
392 m_redoResolve = false;
393 }
394 else
395 {
396 m_address = InetAddress::resolveHostName(m_name);
397 m_redoResolve = true;
398 }
399
400 m_control = control;
401 m_master = master;
402 m_lastResolveTime = time(NULL);
403 m_mutex = MutexCreate();
404 }
405
406 /**
407 * Server info: destructor
408 */
409 ServerInfo::~ServerInfo()
410 {
411 safe_free(m_name);
412 MutexDestroy(m_mutex);
413 }
414
415 /**
416 * Server info: resolve hostname if needed
417 */
418 void ServerInfo::resolve()
419 {
420 time_t now = time(NULL);
421 time_t age = now - m_lastResolveTime;
422 if ((age >= 3600) || ((age > 300) && !m_address.isValid()))
423 {
424 m_address = InetAddress::resolveHostName(m_name);
425 m_lastResolveTime = now;
426 }
427 }
428
429 /**
430 * Server info: match address
431 */
432 bool ServerInfo::match(const InetAddress &addr)
433 {
434 MutexLock(m_mutex);
435 if (m_redoResolve)
436 resolve();
437 bool result = m_address.isValid() ? m_address.contain(addr) : false;
438 MutexUnlock(m_mutex);
439 return result;
440 }
441
442 #ifdef _WIN32
443
444 /**
445 * Get our own console window handle (an alternative to Microsoft's GetConsoleWindow)
446 */
447 static HWND GetConsoleHWND()
448 {
449 DWORD cpid = GetCurrentProcessId();
450 HWND hWnd = NULL;
451 while(true)
452 {
453 hWnd = FindWindowEx(NULL, hWnd, _T("ConsoleWindowClass"), NULL);
454 if (hWnd == NULL)
455 break;
456
457 DWORD wpid;
458 GetWindowThreadProcessId(hWnd, &wpid);
459 if (cpid == wpid)
460 break;
461 }
462
463 return hWnd;
464 }
465
466 /**
467 * Get proc address and write log file
468 */
469 static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
470 {
471 FARPROC ptr;
472
473 ptr = GetProcAddress(hModule, procName);
474 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
475 nxlog_write(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
476 return ptr;
477 }
478
479 /**
480 * Shutdown thread (created by H_RestartAgent)
481 */
482 static THREAD_RESULT THREAD_CALL ShutdownThread(void *pArg)
483 {
484 DebugPrintf(1, _T("Shutdown thread started"));
485 Shutdown();
486 ExitProcess(0);
487 return THREAD_OK; // Never reached
488 }
489
490 #endif /* _WIN32 */
491
492 /**
493 * Restart agent
494 */
495 LONG RestartAgent()
496 {
497 nxlog_debug(1, _T("RestartAgent() called"));
498
499 RestartExtSubagents();
500
501 TCHAR platformSuffixOption[MAX_PSUFFIX_LENGTH + 16];
502 if (g_szPlatformSuffix[0] != 0)
503 {
504 _sntprintf(platformSuffixOption, MAX_PSUFFIX_LENGTH + 16, _T("-P \"%s\" "), g_szPlatformSuffix);
505 }
506 else
507 {
508 platformSuffixOption[0] = 0;
509 }
510
511 const TCHAR *configSection = g_config->getAlias(_T("agent"));
512 TCHAR configSectionOption[256];
513 if ((configSection != NULL) && (*configSection != 0))
514 {
515 _sntprintf(configSectionOption, 256, _T("-G %s "), configSection);
516 }
517 else
518 {
519 configSectionOption[0] = 0;
520 }
521
522 TCHAR szCmdLine[4096];
523 #ifdef _WIN32
524 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" -n \"%s\" -e \"%s\" %s%s%s%s%s-D %d %s%s-X %u"), s_executableName,
525 g_szConfigFile, g_windowsServiceName, g_windowsEventSourceName,
526 (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
527 (g_dwFlags & AF_HIDE_WINDOW) ? _T("-H ") : _T(""),
528 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
529 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
530 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
531 s_debugLevel, platformSuffixOption, configSectionOption,
532 (g_dwFlags & AF_DAEMON) ? 0 : GetCurrentProcessId());
533 DebugPrintf(1, _T("Restarting agent with command line '%s'"), szCmdLine);
534
535 DWORD dwResult;
536 STARTUPINFO si;
537 PROCESS_INFORMATION pi;
538
539 // Fill in process startup info structure
540 memset(&si, 0, sizeof(STARTUPINFO));
541 si.cb = sizeof(STARTUPINFO);
542
543 // Create new process
544 if (!CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE,
545 (g_dwFlags & AF_DAEMON) ? (CREATE_NO_WINDOW | DETACHED_PROCESS) : (CREATE_NEW_CONSOLE),
546 NULL, NULL, &si, &pi))
547 {
548 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", szCmdLine, GetLastError());
549 dwResult = ERR_EXEC_FAILED;
550 }
551 else
552 {
553 // Close all handles
554 CloseHandle(pi.hThread);
555 CloseHandle(pi.hProcess);
556 dwResult = ERR_SUCCESS;
557 }
558 if ((dwResult == ERR_SUCCESS) && (!(g_dwFlags & AF_DAEMON)))
559 {
560 if (g_dwFlags & AF_HIDE_WINDOW)
561 {
562 ConditionSet(s_shutdownCondition);
563 }
564 else
565 {
566 ThreadCreate(ShutdownThread, 0, NULL);
567 }
568 }
569 return dwResult;
570 #else
571 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" %s%s%s%s-D %d %s%s-X %lu"), s_executableName,
572 g_szConfigFile, (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
573 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
574 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
575 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
576 (int)s_debugLevel, platformSuffixOption, configSectionOption,
577 (unsigned long)s_pid);
578 DebugPrintf(1, _T("Restarting agent with command line '%s'"), szCmdLine);
579 return ExecuteCommand(szCmdLine, NULL, NULL);
580 #endif
581 }
582
583 /**
584 * Handler for Agent.Restart action
585 */
586 static LONG H_RestartAgent(const TCHAR *action, StringList *args, const TCHAR *data, AbstractCommSession *session)
587 {
588 return RestartAgent();
589 }
590
591 /**
592 * This function writes message from subagent to agent's log
593 */
594 static void WriteSubAgentMsg(int logLevel, int debugLevel, const TCHAR *pszMsg)
595 {
596 if (logLevel == EVENTLOG_DEBUG_TYPE)
597 {
598 if (debugLevel <= (int)s_debugLevel)
599 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", pszMsg);
600 }
601 else
602 {
603 nxlog_write(MSG_SUBAGENT_MSG, logLevel, "s", pszMsg);
604 }
605 }
606
607 /**
608 * Signal handler for UNIX platforms
609 */
610 #if !defined(_WIN32)
611
612 static THREAD_RESULT THREAD_CALL SignalHandler(void *pArg)
613 {
614 sigset_t signals;
615 sigemptyset(&signals);
616 sigaddset(&signals, SIGTERM);
617 sigaddset(&signals, SIGINT);
618 sigaddset(&signals, SIGSEGV);
619 sigaddset(&signals, SIGHUP);
620 sigaddset(&signals, SIGUSR1);
621 sigaddset(&signals, SIGUSR2);
622 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
623 sigaddset(&signals, SIGPIPE);
624 #endif
625
626 while(true)
627 {
628 int nSignal;
629 if (sigwait(&signals, &nSignal) == 0)
630 {
631 switch(nSignal)
632 {
633 case SIGTERM:
634 case SIGINT:
635 goto stop_handler;
636 case SIGSEGV:
637 abort();
638 break;
639 default:
640 break;
641 }
642 }
643 else
644 {
645 ThreadSleepMs(100);
646 }
647 }
648
649 stop_handler:
650 sigprocmask(SIG_UNBLOCK, &signals, NULL);
651 return THREAD_OK;
652 }
653
654 #endif
655
656 /**
657 * Load platform subagent
658 */
659 static void LoadPlatformSubagent()
660 {
661 #ifdef _WIN32
662 LoadSubAgent(_T("WINNT.NSM"));
663 #else
664 #if HAVE_SYS_UTSNAME_H && !defined(_STATIC_AGENT)
665 struct utsname un;
666 TCHAR szName[MAX_PATH];
667 int i;
668
669 if (uname(&un) != -1)
670 {
671 // Convert system name to lowercase
672 for(i = 0; un.sysname[i] != 0; i++)
673 un.sysname[i] = tolower(un.sysname[i]);
674 if (!strcmp(un.sysname, "hp-ux"))
675 strcpy(un.sysname, "hpux");
676 _sntprintf(szName, MAX_PATH, _T("%hs.nsm"), un.sysname);
677 LoadSubAgent(szName);
678 }
679 #endif
680 #endif
681 }
682
683 /**
684 * Send file to server (subagent API)
685 */
686 static bool SendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset, bool allowCompression)
687 {
688 if (session == NULL)
689 return false;
690 return ((CommSession *)session)->sendFile(requestId, file, offset, allowCompression);
691 }
692
693 /**
694 * Parser server list
695 */
696 static void ParseServerList(TCHAR *serverList, bool isControl, bool isMaster)
697 {
698 TCHAR *pItem, *pEnd;
699
700 for(pItem = pEnd = serverList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
701 {
702 pEnd = _tcschr(pItem, _T(','));
703 if (pEnd != NULL)
704 *pEnd = 0;
705 StrStrip(pItem);
706
707 g_serverList.add(new ServerInfo(pItem, isControl, isMaster));
708 }
709 free(serverList);
710 }
711
712 /**
713 * Agent initialization
714 */
715 BOOL Initialize()
716 {
717 TCHAR *pItem, *pEnd;
718
719 if (s_debugLevel == (UINT32)NXCONFIG_UNINITIALIZED_VALUE)
720 s_debugLevel = 0;
721
722 // Open log file
723 if (!nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NXAGENTD_SYSLOG_NAME : g_szLogFile,
724 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
725 ((g_dwFlags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
726 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
727 _T("NXAGENTD.EXE"),
728 #ifdef _WIN32
729 0, NULL, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG))
730 #else
731 g_dwNumMessages, g_szMessages, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG))
732 #endif
733 {
734 //TODO: set flag that log have been opened with errors
735 s_debugLevel = 1;
736 g_failFlags |= FAIL_OPEN_LOG;
737 nxlog_open(NXAGENTD_SYSLOG_NAME, NXLOG_USE_SYSLOG |
738 ((g_dwFlags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
739 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
740 _T("NXAGENTD.EXE"),
741 #ifdef _WIN32
742 0, NULL, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG);
743 #else
744 g_dwNumMessages, g_szMessages, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG);
745 #endif
746 _ftprintf(stderr, _T("ERROR: Cannot open log file, logs will be written to syslog with debug level 1\n"));
747
748 }
749 else
750 {
751 if (!(g_dwFlags & AF_USE_SYSLOG))
752 {
753 if (!nxlog_set_rotation_policy((int)s_logRotationMode, s_maxLogSize, (int)s_logHistorySize, s_dailyLogFileSuffix))
754 if (!(g_dwFlags & AF_DAEMON))
755 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
756 }
757 }
758 nxlog_write(MSG_AGENT_VERSION, NXLOG_INFO, "s", NETXMS_BUILD_TAG);
759 nxlog_write(MSG_USE_CONFIG_D, NXLOG_INFO, "s", g_szConfigIncludeDir);
760 nxlog_write(MSG_DEBUG_LEVEL, NXLOG_INFO, "d", s_debugLevel);
761
762 if (s_debugTags != NULL)
763 {
764 int count;
765 TCHAR **tagList = SplitString(s_debugTags, _T(','), &count);
766 if (tagList != NULL)
767 {
768 for(int i = 0; i < count; i++)
769 {
770 TCHAR *level = _tcschr(tagList[i], _T(':'));
771 if (level != NULL)
772 {
773 *level = 0;
774 level++;
775 Trim(tagList[i]);
776 nxlog_set_debug_level_tag(tagList[i], _tcstol(level, NULL, 0));
777 }
778 free(tagList[i]);
779 }
780 free(tagList);
781 }
782 free(s_debugTags);
783 }
784
785 nxlog_set_debug_level(s_debugLevel);
786
787 if (_tcscmp(g_masterAgent, _T("not_set")))
788 {
789 g_dwFlags |= AF_SUBAGENT_LOADER;
790 DebugPrintf(1, _T("Switched to external subagent loader mode, master agent address is %s"), g_masterAgent);
791 }
792
793 DebugPrintf(1, _T("Data directory: %s"), g_szDataDirectory);
794 CreateFolder(g_szDataDirectory);
795
796 DebugPrintf(1, _T("File store: %s"), g_szFileStore);
797 CreateFolder(g_szFileStore);
798
799 #ifndef _WIN32
800 nxlog_debug(2, _T("Effective user ID %d"), (int)geteuid());
801 nxlog_debug(2, _T("Effective group ID %d"), (int)getegid());
802 #endif
803
804 // Initialize log parser policy folder
805 TCHAR tail = g_szDataDirectory[_tcslen(g_szDataDirectory) - 1];
806 _sntprintf(g_szLogParserDirectory, MAX_PATH, _T("%s%s%s"), g_szDataDirectory,
807 ((tail != '\\') && (tail != '/')) ? FS_PATH_SEPARATOR : _T(""),
808 LOGPARSER_AP_FOLDER FS_PATH_SEPARATOR);
809 nxlog_debug(6, _T("Log parser policy directory: %s"), g_szLogParserDirectory);
810 CreateFolder(g_szLogParserDirectory);
811
812 // Initialize certificate directory
813 _sntprintf(g_certificateDirectory, MAX_PATH, _T("%s%scertificates") FS_PATH_SEPARATOR, g_szDataDirectory,
814 ((tail != '\\') && (tail != '/')) ? FS_PATH_SEPARATOR : _T(""));
815 nxlog_debug(6, _T("Certificate directory: %s"), g_certificateDirectory);
816 CreateFolder(g_certificateDirectory);
817
818 nxlog_debug(6, _T("Configuration policy directory: %s"), g_szConfigPolicyDir);
819
820 #ifdef _WIN32
821 WSADATA wsaData;
822 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
823 if (wrc != 0)
824 {
825 nxlog_write(MSG_WSASTARTUP_FAILED, NXLOG_ERROR, "e", wrc);
826 return FALSE;
827 }
828 #endif
829
830 // Initialize API for subagents
831 s_subAgentsStopCondition = ConditionCreate(TRUE);
832 InitSubAgentAPI(WriteSubAgentMsg, SendTrap, SendTrap, EnumerateSessions, FindServerSession,
833 SendFileToServer, PushData, GetLocalDatabaseHandle, s_subAgentsStopCondition, g_szDataDirectory);
834 nxlog_debug(1, _T("Subagent API initialized"));
835
836 // Initialize cryptografy
837 if (!InitCryptoLib(s_enabledCiphers))
838 {
839 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
840 return FALSE;
841 }
842
843 // Initialize libssl - it is not used by core agent
844 // but may be needed by some subagents. Allowing first load of libssl by
845 // subagent via dlopen() may lead to undesired side effects
846 #ifdef _WITH_ENCRYPTION
847 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
848 OPENSSL_init_ssl(0, NULL);
849 #else
850 SSL_library_init();
851 SSL_load_error_strings();
852 #endif
853 #endif
854
855 DBInit(MSG_DB_LIBRARY, MSG_SQL_ERROR);
856
857 if (!OpenLocalDatabase())
858 {
859 nxlog_write(MSG_LOCAL_DB_OPEN_FAILED, NXLOG_ERROR, NULL);
860 }
861
862 // Set system name to host name if not set in config
863 if (g_systemName[0] == 0)
864 GetLocalHostName(g_systemName, MAX_OBJECT_NAME, false);
865 nxlog_debug(2, _T("Using system name \"%s\""), g_systemName);
866
867 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
868 {
869 g_commThreadPool = ThreadPoolCreate(2, 32, _T("COMM"));
870 if (g_dwFlags & AF_ENABLE_SNMP_PROXY)
871 {
872 g_snmpProxyThreadPool = ThreadPoolCreate(2, 128, _T("SNMPPROXY"));
873 }
874 InitSessionList();
875
876 // Initialize built-in parameters
877 if (!InitParameterList())
878 return FALSE;
879
880 // Parse outgoing server connection (tunnel) list
881 if (s_serverConnectionList != NULL)
882 ParseTunnelList(s_serverConnectionList);
883
884 // Parse server lists
885 if (m_pszMasterServerList != NULL)
886 ParseServerList(m_pszMasterServerList, true, true);
887 if (m_pszControlServerList != NULL)
888 ParseServerList(m_pszControlServerList, true, false);
889 if (m_pszServerList != NULL)
890 ParseServerList(m_pszServerList, false, false);
891
892 // Add built-in actions
893 AddAction(_T("Agent.Restart"), AGENT_ACTION_SUBAGENT, NULL, H_RestartAgent, _T("CORE"), _T("Restart agent"));
894
895 // Load platform subagents
896 #if !defined(_WIN32)
897 InitStaticSubagents();
898 #endif
899 if (g_dwFlags & AF_ENABLE_AUTOLOAD)
900 {
901 LoadPlatformSubagent();
902 }
903 }
904
905 // Wait for external process if requested
906 if (s_processToWaitFor[0] != 0)
907 {
908 DebugPrintf(1, _T("Waiting for process %s"), s_processToWaitFor);
909 if (!WaitForProcess(s_processToWaitFor))
910 nxlog_write(MSG_WAITFORPROCESS_FAILED, EVENTLOG_ERROR_TYPE, "s", s_processToWaitFor);
911 }
912
913 // Load other subagents
914 if (m_pszSubagentList != NULL)
915 {
916 for(pItem = pEnd = m_pszSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
917 {
918 pEnd = _tcschr(pItem, _T('\n'));
919 if (pEnd != NULL)
920 *pEnd = 0;
921 Trim(pItem);
922 LoadSubAgent(pItem);
923 }
924 free(m_pszSubagentList);
925 }
926
927 // Parse action list
928 if (m_pszActionList != NULL)
929 {
930 for(pItem = pEnd = m_pszActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
931 {
932 pEnd = _tcschr(pItem, _T('\n'));
933 if (pEnd != NULL)
934 *pEnd = 0;
935 Trim(pItem);
936 if (!AddActionFromConfig(pItem, FALSE))
937 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
938 }
939 free(m_pszActionList);
940 }
941 if (m_pszShellActionList != NULL)
942 {
943 for(pItem = pEnd = m_pszShellActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
944 {
945 pEnd = _tcschr(pItem, _T('\n'));
946
947 if (pEnd != NULL)
948 *pEnd = 0;
949 Trim(pItem);
950 if (!AddActionFromConfig(pItem, TRUE))
951 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
952 }
953 free(m_pszShellActionList);
954 }
955
956 // Parse external parameters list
957 if (s_externalParametersConfig != NULL)
958 {
959 for(pItem = pEnd = s_externalParametersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
960 {
961 pEnd = _tcschr(pItem, _T('\n'));
962 if (pEnd != NULL)
963 *pEnd = 0;
964 Trim(pItem);
965 if (!AddExternalParameter(pItem, FALSE, FALSE))
966 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
967 }
968 free(s_externalParametersConfig);
969 }
970 if (s_externalShellExecParametersConfig != NULL)
971 {
972 for(pItem = pEnd = s_externalShellExecParametersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
973 {
974 pEnd = _tcschr(pItem, _T('\n'));
975 if (pEnd != NULL)
976 *pEnd = 0;
977 Trim(pItem);
978 if (!AddExternalParameter(pItem, TRUE, FALSE))
979 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
980 }
981 free(s_externalShellExecParametersConfig);
982 }
983
984 // Parse external lists
985 if (s_externalListsConfig != NULL)
986 {
987 for(pItem = pEnd = s_externalListsConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
988 {
989 pEnd = _tcschr(pItem, _T('\n'));
990 if (pEnd != NULL)
991 *pEnd = 0;
992 Trim(pItem);
993 if (!AddExternalParameter(pItem, FALSE, TRUE))
994 nxlog_write(MSG_ADD_EXT_LIST_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
995 }
996 free(s_externalListsConfig);
997 }
998
999 // Parse external tables
1000 if (s_externalTablesConfig != NULL)
1001 {
1002 for(pItem = pEnd = s_externalTablesConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1003 {
1004 pEnd = _tcschr(pItem, _T('\n'));
1005 if (pEnd != NULL)
1006 *pEnd = 0;
1007 Trim(pItem);
1008 if (!AddExternalTable(pItem, false))
1009 nxlog_write(MSG_ADD_EXT_TABLE_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1010 }
1011 free(s_externalTablesConfig);
1012 }
1013
1014 // Parse external parameters providers list
1015 if (s_externalParameterProvidersConfig != NULL)
1016 {
1017 for(pItem = pEnd = s_externalParameterProvidersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1018 {
1019 pEnd = _tcschr(pItem, _T('\n'));
1020 if (pEnd != NULL)
1021 *pEnd = 0;
1022 Trim(pItem);
1023 if (!AddParametersProvider(pItem))
1024 nxlog_write(MSG_ADD_PARAM_PROVIDER_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1025 }
1026 free(s_externalParameterProvidersConfig);
1027 }
1028
1029 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
1030 {
1031 // Parse external subagents list
1032 if (!(g_dwFlags & AF_SUBAGENT_LOADER) && (s_externalSubAgentsList != NULL))
1033 {
1034 for(pItem = pEnd = s_externalSubAgentsList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1035 {
1036 pEnd = _tcschr(pItem, _T('\n'));
1037 if (pEnd != NULL)
1038 *pEnd = 0;
1039 Trim(pItem);
1040 if (!AddExternalSubagent(pItem))
1041 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1042 }
1043 free(s_externalSubAgentsList);
1044 }
1045
1046 // Additional external subagents implicitly defined by EXT:* config sections
1047 ObjectArray<ConfigEntry> *entries = g_config->getSubEntries(_T("/"), _T("EXT:*"));
1048 for(int i = 0; i < entries->size(); i++)
1049 {
1050 const TCHAR *name = entries->get(i)->getName() + 4;
1051 if (!AddExternalSubagent(name))
1052 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", name);
1053 }
1054 delete entries;
1055
1056 // Parse application agents list
1057 if (!(g_dwFlags & AF_SUBAGENT_LOADER) && (s_appAgentsList != NULL))
1058 {
1059 for(pItem = pEnd = s_appAgentsList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1060 {
1061 pEnd = _tcschr(pItem, _T('\n'));
1062 if (pEnd != NULL)
1063 *pEnd = 0;
1064 Trim(pItem);
1065 RegisterApplicationAgent(pItem);
1066 }
1067 free(s_appAgentsList);
1068 }
1069 }
1070
1071 ThreadSleep(1);
1072
1073 // If StartupDelay is greater than zero, then wait
1074 if (g_dwStartupDelay > 0)
1075 {
1076 if (g_dwFlags & AF_DAEMON)
1077 {
1078 ThreadSleep(g_dwStartupDelay);
1079 }
1080 else
1081 {
1082 UINT32 i;
1083
1084 _tprintf(_T("XXXXXX%*s]\rWAIT ["), g_dwStartupDelay, _T(" "));
1085 fflush(stdout);
1086 for(i = 0; i < g_dwStartupDelay; i++)
1087 {
1088 ThreadSleep(1);
1089 _puttc(_T('.'), stdout);
1090 fflush(stdout);
1091 }
1092 _tprintf(_T("\n"));
1093 }
1094 }
1095
1096 StartParamProvidersPoller();
1097
1098 // Agent start time
1099 g_tmAgentStartTime = time(NULL);
1100
1101 s_eventSenderThread = ThreadCreateEx(TrapSender, 0, NULL);
1102
1103 if (g_dwFlags & AF_ENABLE_SNMP_TRAP_PROXY)
1104 {
1105 s_snmpTrapSenderThread = ThreadCreateEx(SNMPTrapSender, 0, NULL);
1106 s_snmpTrapReceiverThread = ThreadCreateEx(SNMPTrapReceiver, 0, NULL);
1107 }
1108
1109 if (g_dwFlags & AF_ENABLE_SYSLOG_PROXY)
1110 {
1111 s_syslogSenderThread = ThreadCreateEx(SyslogSender, 0, NULL);
1112 s_syslogReceiverThread = ThreadCreateEx(SyslogReceiver, 0, NULL);
1113 }
1114
1115 if (g_dwFlags & AF_SUBAGENT_LOADER)
1116 {
1117 s_masterAgentListenerThread = ThreadCreateEx(MasterAgentListener, 0, NULL);
1118 }
1119 else
1120 {
1121 // Start network listener and session watchdog
1122 StartLocalDataCollector();
1123 s_listenerThread = ThreadCreateEx(ListenerThread, 0, NULL);
1124 s_sessionWatchdogThread = ThreadCreateEx(SessionWatchdog, 0, NULL);
1125 StartPushConnector();
1126 StartSessionAgentConnector();
1127 if (g_dwFlags & AF_ENABLE_CONTROL_CONNECTOR)
1128 {
1129 StartControlConnector();
1130 }
1131
1132 if (g_dwFlags & AF_REGISTER)
1133 {
1134 RegisterOnServer(g_szRegistrar);
1135 }
1136
1137 s_tunnelManagerThread = ThreadCreateEx(TunnelManager, 0, NULL);
1138 }
1139
1140 #if defined(_WIN32)
1141 s_shutdownCondition = ConditionCreate(TRUE);
1142 #endif
1143 ThreadSleep(1);
1144
1145 // Start watchdog process
1146 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
1147 {
1148 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1149 StartWatchdog();
1150 }
1151
1152 // Delete file used for upgrade if exists
1153 TCHAR upgradeFileName[MAX_PATH];
1154 ReadRegistryAsString(_T("upgrade.file"), upgradeFileName, MAX_PATH, _T(""));
1155 if(upgradeFileName[0] != 0)
1156 {
1157 _tremove(upgradeFileName);
1158 DeleteRegistryEntry(_T("upgrade.file"));
1159 }
1160
1161 //Update policy inventory according to files that exist on file system
1162 UpdatePolicyInventory();
1163
1164 return TRUE;
1165 }
1166
1167 /**
1168 * Shutdown agent
1169 */
1170 void Shutdown()
1171 {
1172 DebugPrintf(2, _T("Shutdown() called"));
1173 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1174 StopWatchdog();
1175
1176 g_dwFlags |= AF_SHUTDOWN;
1177 ConditionSet(s_subAgentsStopCondition);
1178
1179 if (g_dwFlags & AF_SUBAGENT_LOADER)
1180 {
1181 // TODO: shall we inform master agent listener about shutdown?
1182 //ThreadJoin(s_masterAgentListenerThread);
1183 }
1184 else
1185 {
1186 ShutdownLocalDataCollector();
1187 ShutdownTrapSender();
1188 ThreadJoin(s_sessionWatchdogThread);
1189 ThreadJoin(s_listenerThread);
1190 ThreadJoin(s_tunnelManagerThread);
1191 }
1192 ThreadJoin(s_eventSenderThread);
1193 if (g_dwFlags & AF_ENABLE_SNMP_TRAP_PROXY)
1194 {
1195 ShutdownSNMPTrapSender();
1196 ThreadJoin(s_snmpTrapReceiverThread);
1197 ThreadJoin(s_snmpTrapSenderThread);
1198 }
1199 if (g_dwFlags & AF_ENABLE_SYSLOG_PROXY)
1200 {
1201 ShutdownSyslogSender();
1202 ThreadJoin(s_syslogReceiverThread);
1203 ThreadJoin(s_syslogSenderThread);
1204 }
1205
1206 DestroySessionList();
1207 MsgWaitQueue::shutdown();
1208
1209 if (g_dwFlags & AF_ENABLE_SNMP_PROXY)
1210 {
1211 ThreadPoolDestroy(g_snmpProxyThreadPool);
1212 }
1213 ThreadPoolDestroy(g_commThreadPool);
1214
1215 UnloadAllSubAgents();
1216 CloseLocalDatabase();
1217 nxlog_write(MSG_AGENT_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
1218 nxlog_close();
1219
1220 delete g_config;
1221
1222 // Notify main thread about shutdown
1223 #ifdef _WIN32
1224 ConditionSet(s_shutdownCondition);
1225 #endif
1226
1227 // Remove PID file
1228 #if !defined(_WIN32)
1229 _tremove(g_szPidFile);
1230 #endif
1231 }
1232
1233 /**
1234 * Common Main()
1235 */
1236 void Main()
1237 {
1238 nxlog_write(MSG_AGENT_STARTED, NXLOG_INFO, NULL);
1239
1240 if (g_dwFlags & AF_DAEMON)
1241 {
1242 #if defined(_WIN32)
1243 ConditionWait(s_shutdownCondition, INFINITE);
1244 #else
1245 StartMainLoop(SignalHandler, NULL);
1246 #endif
1247 }
1248 else
1249 {
1250 #if defined(_WIN32)
1251 if (g_dwFlags & AF_HIDE_WINDOW)
1252 {
1253 HWND hWnd;
1254
1255 hWnd = GetConsoleHWND();
1256 if (hWnd != NULL)
1257 ShowWindow(hWnd, SW_HIDE);
1258 ConditionWait(s_shutdownCondition, INFINITE);
1259 ThreadSleep(1);
1260 }
1261 else
1262 {
1263 _tprintf(_T("Agent running. Press ESC to shutdown.\n"));
1264 while(1)
1265 {
1266 if (_getch() == 27)
1267 break;
1268 }
1269 _tprintf(_T("Agent shutting down...\n"));
1270 Shutdown();
1271 }
1272 #else
1273 _tprintf(_T("Agent running. Press Ctrl+C to shutdown.\n"));
1274 StartMainLoop(SignalHandler, NULL);
1275 _tprintf(_T("\nStopping agent...\n"));
1276 #endif
1277 }
1278 }
1279
1280 /**
1281 * Do necessary actions on agent restart
1282 */
1283 static void DoRestartActions(UINT32 dwOldPID)
1284 {
1285 #if defined(_WIN32)
1286 if (dwOldPID == 0)
1287 {
1288 // Service
1289 StopAgentService();
1290 WaitForService(SERVICE_STOPPED);
1291 StartAgentService();
1292 ExitProcess(0);
1293 }
1294 else
1295 {
1296 HANDLE hProcess;
1297
1298 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwOldPID);
1299 if (hProcess != NULL)
1300 {
1301 if (WaitForSingleObject(hProcess, 60000) == WAIT_TIMEOUT)
1302 {
1303 TerminateProcess(hProcess, 0);
1304 }
1305 CloseHandle(hProcess);
1306 }
1307 }
1308 #else
1309 #if WITH_SYSTEMD
1310 if (RestartService(dwOldPID))
1311 {
1312 // successfully restarted agent service using systemd, exit this instance
1313 exit(0);
1314 }
1315 #endif
1316 kill(dwOldPID, SIGTERM);
1317 int i;
1318 for(i = 0; i < 30; i++)
1319 {
1320 sleep(2);
1321 if (kill(dwOldPID, SIGCONT) == -1)
1322 break;
1323 }
1324
1325 // Kill previous instance of agent if it's still running
1326 if (i == 30)
1327 kill(dwOldPID, SIGKILL);
1328 #endif
1329 }
1330
1331 /**
1332 * Create configuration file
1333 */
1334 static int CreateConfig(const char *pszServer, const char *pszLogFile, const char *pszFileStore,
1335 const char *configIncludeDir, int iNumSubAgents, char **ppszSubAgentList)
1336 {
1337 FILE *fp;
1338 time_t currTime;
1339 int i;
1340
1341 if (_taccess(g_szConfigFile, 0) == 0)
1342 return 0; // File already exist, we shouldn't overwrite it
1343
1344 fp = _tfopen(g_szConfigFile, _T("w"));
1345 if (fp != NULL)
1346 {
1347 currTime = time(NULL);
1348 _ftprintf(fp, _T("#\n# NetXMS agent configuration file\n# Created by agent installer at %s#\n\n"),
1349 _tctime(&currTime));
1350 _ftprintf(fp, _T("MasterServers = %hs\nConfigIncludeDir = %hs\nLogFile = %hs\nFileStore = %hs\n"),
1351 pszServer, configIncludeDir, pszLogFile, pszFileStore);
1352
1353 Array extSubAgents;
1354 for(i = 0; i < iNumSubAgents; i++)
1355 {
1356 if (!strnicmp(ppszSubAgentList[i], "EXT:", 4))
1357 {
1358 extSubAgents.add(ppszSubAgentList[i] + 4);
1359 }
1360 else
1361 {
1362 _ftprintf(fp, _T("SubAgent = %hs\n"), ppszSubAgentList[i]);
1363 }
1364 }
1365
1366 for(i = 0; i < extSubAgents.size(); i++)
1367 {
1368 char section[MAX_PATH];
1369 strncpy(section, (const char *)extSubAgents.get(i), MAX_PATH);
1370 char *eptr = strrchr(section, '.');
1371 if (eptr != NULL)
1372 *eptr = 0;
1373 strupr(section);
1374 _ftprintf(fp, _T("\n[EXT:%hs]\n"), section);
1375 _ftprintf(fp, _T("SubAgent = %hs\n"), (const char *)extSubAgents.get(i));
1376 }
1377
1378 fclose(fp);
1379 }
1380 return (fp != NULL) ? 0 : 2;
1381 }
1382
1383 /**
1384 * Init config
1385 */
1386 static void InitConfig(const TCHAR *configSection)
1387 {
1388 g_config = new Config();
1389 g_config->setTopLevelTag(_T("config"));
1390 g_config->setAlias(_T("agent"), configSection);
1391
1392 // Set default data directory on Windows
1393 #ifdef _WIN32
1394 if (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, g_szDataDirectory) == S_OK)
1395 {
1396 _tcscat(g_szDataDirectory, _T("\\nxagentd"));
1397 }
1398 #endif
1399 }
1400
1401 /**
1402 * Initiate shutdown of connected external subagents
1403 */
1404 static void InitiateExtSubagentShutdown()
1405 {
1406 NXCPMessage msg;
1407 msg.setCode(CMD_SHUTDOWN);
1408 if (SendControlMessage(&msg))
1409 _tprintf(_T("Control message sent successfully to master agent\n"));
1410 else
1411 _tprintf(_T("ERROR: Unable to send control message to master agent\n"));
1412 }
1413
1414 #ifndef _WIN32
1415
1416 /**
1417 * Get user ID
1418 */
1419 static int GetUserId(const char *name)
1420 {
1421 char *eptr;
1422 int id = (int)strtol(name, &eptr, 10);
1423 if (*eptr == 0)
1424 return id;
1425
1426 #if HAVE_GETPWNAM
1427 struct passwd *p = getpwnam(name);
1428 if (p == NULL)
1429 {
1430 _tprintf(_T("Invalid user ID \"%hs\"\n"), name);
1431 return 0;
1432 }
1433 return p->pw_uid;
1434 #else
1435 _tprintf(_T("Invalid user ID \"%hs\"\n"), name);
1436 return 0;
1437 #endif
1438 }
1439
1440 /**
1441 * Get group ID
1442 */
1443 static int GetGroupId(const char *name)
1444 {
1445 char *eptr;
1446 int id = (int)strtol(name, &eptr, 10);
1447 if (*eptr == 0)
1448 return id;
1449
1450 #if HAVE_GETGRNAM
1451 struct group *g = getgrnam(name);
1452 if (g == NULL)
1453 {
1454 _tprintf(_T("Invalid group ID \"%hs\"\n"), name);
1455 return 0;
1456 }
1457 return g->gr_gid;
1458 #else
1459 _tprintf(_T("Invalid group ID \"%hs\"\n"), name);
1460 return 0;
1461 #endif
1462 }
1463
1464 #endif
1465
1466 /**
1467 * Update agent environment from config
1468 */
1469 static void UpdateEnvironment()
1470 {
1471 ObjectArray<ConfigEntry> *entrySet = g_config->getSubEntries(_T("/ENV"), _T("*"));
1472 if (entrySet == NULL)
1473 return;
1474 for(int i = 0; i < entrySet->size(); i++)
1475 {
1476 ConfigEntry *e = entrySet->get(i);
1477 size_t len = _tcslen(e->getName()) + _tcslen(e->getValue()) + 2;
1478 char *env = (char *)malloc(len);
1479 if (env != NULL)
1480 {
1481 #ifdef UNICODE
1482 char *mbName = MBStringFromWideString(e->getName());
1483 char *mbValue = MBStringFromWideString(e->getValue());
1484 snprintf(env, len, "%s=%s", mbName, mbValue);
1485 free(mbName);
1486 free(mbValue);
1487 #else
1488 sprintf(env, "%s=%s", e->getName(), e->getValue());
1489 #endif
1490 putenv(env);
1491 }
1492 }
1493 delete entrySet;
1494 }
1495
1496 /**
1497 * Application entry point
1498 */
1499 int main(int argc, char *argv[])
1500 {
1501 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
1502 BOOL bRestart = FALSE;
1503 UINT32 dwOldPID, dwMainPID;
1504 char *eptr;
1505 TCHAR configSection[MAX_DB_STRING] = DEFAULT_CONFIG_SECTION;
1506 #ifdef _WIN32
1507 TCHAR szModuleName[MAX_PATH];
1508 HKEY hKey;
1509 DWORD dwSize;
1510 #else
1511 TCHAR *pszEnv;
1512 int uid = 0, gid = 0;
1513 #endif
1514
1515 InitNetXMSProcess(false);
1516
1517 // Check for alternate config file location
1518 #ifdef _WIN32
1519 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Agent"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
1520 {
1521 dwSize = MAX_PATH * sizeof(TCHAR);
1522 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
1523 dwSize = MAX_PATH * sizeof(TCHAR);
1524 RegQueryValueEx(hKey, _T("ConfigIncludeDir"), NULL, NULL, (BYTE *)g_szConfigIncludeDir, &dwSize);
1525 RegCloseKey(hKey);
1526 }
1527 #else
1528 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG"));
1529 if (pszEnv != NULL)
1530 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
1531
1532 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG_D"));
1533 if (pszEnv != NULL)
1534 nx_strncpy(g_szConfigIncludeDir, pszEnv, MAX_PATH);
1535 #endif
1536
1537 // Parse command line
1538 if (argc == 1)
1539 iAction = ACTION_HELP;
1540 opterr = 1;
1541 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
1542 {
1543 switch(ch)
1544 {
1545 case 'c': // Configuration file
1546 #ifdef UNICODE
1547 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
1548 g_szConfigFile[MAX_PATH - 1] = 0;
1549 #else
1550 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1551 #endif
1552 break;
1553 case 'C': // Configuration check only
1554 iAction = ACTION_CHECK_CONFIG;
1555 break;
1556 case 'd': // Run as daemon
1557 g_dwFlags |= AF_DAEMON;
1558 break;
1559 case 'D': // Turn on debug output
1560 s_debugLevel = strtoul(optarg, &eptr, 0);
1561 if ((*eptr != 0) || (s_debugLevel > 9))
1562 {
1563 fprintf(stderr, "Invalid debug level: %s\n", optarg);
1564 iAction = -1;
1565 iExitCode = 1;
1566 }
1567 break;
1568 case 'f': // Run in foreground
1569 g_dwFlags &= ~AF_DAEMON;
1570 break;
1571 #ifndef _WIN32
1572 case 'g': // set group ID
1573 gid = GetGroupId(optarg);
1574 break;
1575 #endif
1576 case 'G':
1577 #ifdef UNICODE
1578 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, configSection, MAX_DB_STRING);
1579 configSection[MAX_DB_STRING - 1] = 0;
1580 #else
1581 nx_strncpy(configSection, optarg, MAX_DB_STRING);
1582 #endif
1583 break;
1584 case 'h': // Display help and exit
1585 iAction = ACTION_HELP;
1586 break;
1587 #ifdef _WIN32
1588 case 'H': // Hide window
1589 g_dwFlags |= AF_HIDE_WINDOW;
1590 break;
1591 #endif
1592 case 'K': // Shutdown external sub-agents
1593 iAction = ACTION_SHUTDOWN_EXT_AGENTS;
1594 break;
1595 #ifndef _WIN32
1596 case 'p': // PID file
1597 #ifdef UNICODE
1598 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPidFile, MAX_PATH);
1599 g_szPidFile[MAX_PATH - 1] = 0;
1600 #else
1601 nx_strncpy(g_szPidFile, optarg, MAX_PATH);
1602 #endif
1603 break;
1604 #endif
1605 case 'M':
1606 g_dwFlags |= AF_CENTRAL_CONFIG;
1607 #ifdef UNICODE
1608 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigServer, MAX_DB_STRING);
1609 g_szConfigServer[MAX_DB_STRING - 1] = 0;
1610 #else
1611 nx_strncpy(g_szConfigServer, optarg, MAX_DB_STRING);
1612 #endif
1613 break;
1614 case 'r':
1615 g_dwFlags |= AF_REGISTER;
1616 #ifdef UNICODE
1617 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szRegistrar, MAX_DB_STRING);
1618 g_szRegistrar[MAX_DB_STRING - 1] = 0;
1619 #else
1620 nx_strncpy(g_szRegistrar, optarg, MAX_DB_STRING);
1621 #endif
1622 break;
1623 case 'P': // Platform suffix
1624 #ifdef UNICODE
1625 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPlatformSuffix, MAX_PSUFFIX_LENGTH);
1626 g_szPlatformSuffix[MAX_PSUFFIX_LENGTH - 1] = 0;
1627 #else
1628 nx_strncpy(g_szPlatformSuffix, optarg, MAX_PSUFFIX_LENGTH);
1629 #endif
1630 break;
1631 #ifndef _WIN32
1632 case 'u': // set user ID
1633 uid = GetUserId(optarg);
1634 break;
1635 #endif
1636 case 'v': // Print version and exit
1637 _tprintf(_T("NetXMS Core Agent Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n"));
1638 iAction = ACTION_NONE;
1639 break;
1640 case 'W': // Watchdog process
1641 iAction = ACTION_RUN_WATCHDOG;
1642 dwMainPID = strtoul(optarg, NULL, 10);
1643 break;
1644 case 'X': // Agent is being restarted
1645 bRestart = TRUE;
1646 dwOldPID = strtoul(optarg, NULL, 10);
1647 break;
1648 case 'Z': // Create configuration file
1649 iAction = ACTION_CREATE_CONFIG;
1650 #ifdef UNICODE
1651 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
1652 g_szConfigFile[MAX_PATH - 1] = 0;
1653 #else
1654 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1655 #endif
1656 break;
1657 #ifdef _WIN32
1658 case 'i':
1659 g_dwFlags |= AF_INTERACTIVE_SERVICE;
1660 break;
1661 case 'I': // Install Windows service
1662 iAction = ACTION_INSTALL_SERVICE;
1663 break;
1664 case 'R': // Remove Windows service
1665 iAction = ACTION_REMOVE_SERVICE;
1666 break;
1667 case 's': // Start Windows service
1668 iAction = ACTION_START_SERVICE;
1669 break;
1670 case 'S': // Stop Windows service
1671 iAction = ACTION_STOP_SERVICE;
1672 break;
1673 case 'E': // Install Windows event source
1674 iAction = ACTION_INSTALL_EVENT_SOURCE;
1675 break;
1676 case 'U': // Remove Windows event source
1677 iAction = ACTION_REMOVE_EVENT_SOURCE;
1678 break;
1679 case 'e': // Event source name
1680 #ifdef UNICODE
1681 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsEventSourceName, MAX_PATH);
1682 g_windowsEventSourceName[MAX_PATH - 1] = 0;
1683 #else
1684 nx_strncpy(g_windowsEventSourceName, optarg, MAX_PATH);
1685 #endif
1686 break;
1687 case 'n': // Service name
1688 #ifdef UNICODE
1689 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsServiceName, MAX_PATH);
1690 g_windowsServiceName[MAX_PATH - 1] = 0;
1691 #else
1692 nx_strncpy(g_windowsServiceName, optarg, MAX_PATH);
1693 #endif
1694 break;
1695 case 'N': // Service display name
1696 #ifdef UNICODE
1697 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsServiceDisplayName, MAX_PATH);
1698 g_windowsServiceDisplayName[MAX_PATH - 1] = 0;
1699 #else
1700 nx_strncpy(g_windowsServiceDisplayName, optarg, MAX_PATH);
1701 #endif
1702 break;
1703 #endif
1704 case '?':
1705 iAction = ACTION_HELP;
1706 iExitCode = 1;
1707 break;
1708 default:
1709 break;
1710 }
1711 }
1712
1713 #if !defined(_WIN32)
1714 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1715 {
1716 TCHAR path[MAX_PATH] = _T("");
1717 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
1718 if (homeDir != NULL)
1719 {
1720 _sntprintf(path, MAX_PATH, _T("%s/etc/nxagentd.conf"), homeDir);
1721 }
1722 if ((path[0] != 0) && (_taccess(path, 4) == 0))
1723 {
1724 _tcscpy(g_szConfigFile, path);
1725 }
1726 else if (_taccess(PREFIX _T("/etc/nxagentd.conf"), 4) == 0)
1727 {
1728 _tcscpy(g_szConfigFile, PREFIX _T("/etc/nxagentd.conf"));
1729 }
1730 else if (_taccess(_T("/Database/etc/nxagentd.conf"), 4) == 0) // for ZeroShell
1731 {
1732 _tcscpy(g_szConfigFile, _T("/Database/etc/nxagentd.conf"));
1733 }
1734 else if (_taccess(_T("/usr/etc/nxagentd.conf"), 4) == 0)
1735 {
1736 _tcscpy(g_szConfigFile, _T("/usr/etc/nxagentd.conf"));
1737 }
1738 else
1739 {
1740 _tcscpy(g_szConfigFile, _T("/etc/nxagentd.conf"));
1741 }
1742 }
1743 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
1744 {
1745 TCHAR path[MAX_PATH] = _T("");
1746 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
1747 if (homeDir != NULL)
1748 {
1749 _sntprintf(path, MAX_PATH, _T("%s/etc/nxagentd.conf.d"), homeDir);
1750 }
1751 if ((path[0] != 0) && (_taccess(path, 4) == 0))
1752 {
1753 _tcscpy(g_szConfigIncludeDir, path);
1754 }
1755 else if (_taccess(PREFIX _T("/etc/nxagentd.conf.d"), 4) == 0)
1756 {
1757 _tcscpy(g_szConfigIncludeDir, PREFIX _T("/etc/nxagentd.conf.d"));
1758 }
1759 else if (_taccess(_T("/Database/etc/nxagentd.conf.d"), 4) == 0)
1760 {
1761 _tcscpy(g_szConfigIncludeDir, _T("/Database/etc/nxagentd.conf.d"));
1762 }
1763 else if (_taccess(_T("/usr/etc/nxagentd.conf.d"), 4) == 0)
1764 {
1765 _tcscpy(g_szConfigIncludeDir, _T("/usr/etc/nxagentd.conf.d"));
1766 }
1767 else
1768 {
1769 _tcscpy(g_szConfigIncludeDir, _T("/etc/nxagentd.conf.d"));
1770 }
1771 }
1772 #else
1773 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1774 {
1775 TCHAR path[MAX_PATH];
1776 GetNetXMSDirectory(nxDirEtc, path);
1777 _tcscat(path, _T("\\nxagentd.conf"));
1778 if (_taccess(path, 4) == 0)
1779 {
1780 _tcscpy(g_szConfigFile, path);
1781 }
1782 else
1783 {
1784 _tcscpy(g_szConfigFile, _T("C:\\nxagentd.conf"));
1785 }
1786 }
1787 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
1788 {
1789 TCHAR path[MAX_PATH];
1790 GetNetXMSDirectory(nxDirEtc, path);
1791 _tcscat(path, _T("\\nxagentd.conf.d"));
1792 if (_taccess(path, 4) == 0)
1793 {
1794 _tcscpy(g_szConfigIncludeDir, path);
1795 }
1796 else
1797 {
1798 _tcscpy(g_szConfigIncludeDir, _T("C:\\nxagentd.conf.d"));
1799 }
1800 }
1801 #endif
1802
1803 if (!_tcscmp(g_szDataDirectory, _T("{default}")))
1804 {
1805 GetNetXMSDirectory(nxDirData, g_szDataDirectory);
1806 }
1807
1808 // Initialize config policy folder and load policy based configs
1809 nx_strncpy(g_szConfigPolicyDir, g_szDataDirectory, MAX_PATH - 16);
1810 if (g_szConfigPolicyDir[_tcslen(g_szConfigPolicyDir) - 1] != FS_PATH_SEPARATOR_CHAR)
1811 _tcscat(g_szConfigPolicyDir, FS_PATH_SEPARATOR);
1812 _tcscat(g_szConfigPolicyDir, CONFIG_AP_FOLDER FS_PATH_SEPARATOR);
1813 CreateFolder(g_szConfigPolicyDir);
1814
1815 if (bRestart)
1816 DoRestartActions(dwOldPID);
1817
1818 InitConfig(configSection);
1819
1820 // Do requested action
1821 switch(iAction)
1822 {
1823 case ACTION_RUN_AGENT:
1824 // Set default value for session idle timeout based on
1825 // connect() timeout, if possible
1826 #if HAVE_SYSCTLBYNAME && !defined(_IPSO)
1827 {
1828 LONG nVal;
1829 size_t nSize;
1830
1831 nSize = sizeof(nVal);
1832 if (sysctlbyname("net.inet.tcp.keepinit", &nVal, &nSize, NULL, 0) == 0)
1833 {
1834 g_dwIdleTimeout = nVal / 1000 + 15;
1835 }
1836 }
1837 #endif
1838
1839 if (g_dwFlags & AF_CENTRAL_CONFIG)
1840 {
1841 if (s_debugLevel > 0)
1842 _tprintf(_T("Downloading configuration from %s...\n"), g_szConfigServer);
1843 if (DownloadConfig(g_szConfigServer))
1844 {
1845 if (s_debugLevel > 0)
1846 _tprintf(_T("Configuration downloaded successfully\n"));
1847 }
1848 else
1849 {
1850 if (s_debugLevel > 0)
1851 _tprintf(_T("Configuration download failed\n"));
1852 }
1853 }
1854
1855 if (g_config->loadConfig(g_szConfigFile, DEFAULT_CONFIG_SECTION))
1856 {
1857 const TCHAR *dir = g_config->getValue(_T("/%agent/ConfigIncludeDir"));
1858 if (dir != NULL)
1859 nx_strncpy(g_szConfigIncludeDir, dir, MAX_PATH);
1860 g_config->loadConfigDirectory(g_szConfigIncludeDir, DEFAULT_CONFIG_SECTION);
1861
1862 g_config->loadConfigDirectory(g_szConfigPolicyDir, DEFAULT_CONFIG_SECTION);
1863
1864 // Check if master section starts with EXT:
1865 // If yes, switch to external subagent mode
1866 if (!_tcsnicmp(configSection, _T("EXT:"), 4))
1867 {
1868 nx_strncpy(g_masterAgent, &configSection[4], MAX_PATH);
1869 }
1870
1871 if (g_config->parseTemplate(configSection, m_cfgTemplate))
1872 {
1873 DecryptPassword(_T("netxms"), g_szSharedSecret, g_szSharedSecret, MAX_SECRET_LENGTH);
1874
1875 // try to guess executable path
1876 #ifdef _WIN32
1877 GetModuleFileName(GetModuleHandle(NULL), s_executableName, MAX_PATH);
1878 #else
1879 #ifdef UNICODE
1880 char __buffer[PATH_MAX];
1881 #else
1882 #define __buffer s_executableName
1883 #endif
1884 if (realpath(argv[0], __buffer) == NULL)
1885 {
1886 // fallback
1887 TCHAR *path = _tgetenv(_T("NETXMS_HOME"));
1888 if (path != NULL)
1889 {
1890 nx_strncpy(s_executableName, path, sizeof(s_executableName) / sizeof(s_executableName[0]));
1891 }
1892 else
1893 {
1894 nx_strncpy(s_executableName, PREFIX, sizeof(s_executableName) / sizeof(s_executableName[0]));
1895 }
1896 _tcsncat(s_executableName, _T("/bin/nxagentd"), sizeof(s_executableName) / sizeof(s_executableName[0]));
1897 }
1898 else
1899 {
1900 #ifdef UNICODE
1901 int len = strlen(__buffer);
1902 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, __buffer, len, s_executableName, MAX_PATH);
1903 #endif
1904 }
1905 #endif
1906
1907 // Set exception handler
1908 #ifdef _WIN32
1909 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
1910 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter, s_dumpDir,
1911 _T("nxagentd"), MSG_EXCEPTION, g_dwFlags & AF_WRITE_FULL_DUMP, !(g_dwFlags & AF_DAEMON));
1912 __try {
1913 #endif
1914 if ((!_tcsicmp(g_szLogFile, _T("{syslog}"))) ||
1915 (!_tcsicmp(g_szLogFile, _T("{eventlog}"))))
1916 g_dwFlags |= AF_USE_SYSLOG;
1917
1918 #ifdef _WIN32
1919 if (g_dwFlags & AF_DAEMON)
1920 {
1921 InitService();
1922 }
1923 else
1924 {
1925 if (Initialize())
1926 {
1927 Main();
1928 }
1929 else
1930 {
1931 ConsolePrintf(_T("Agent initialization failed\n"));
1932 nxlog_close();
1933 iExitCode = 3;
1934 }
1935 }
1936 #else /* _WIN32 */
1937 if (g_dwFlags & AF_DAEMON)
1938 {
1939 if (daemon(0, 0) == -1)
1940 {
1941 perror("Unable to setup itself as a daemon");
1942 iExitCode = 4;
1943 }
1944 }
1945 if (iExitCode == 0)
1946 {
1947 #ifndef _WIN32
1948 if (gid == 0)
1949 {
1950 const TCHAR *v = g_config->getValue(_T("/%agent/GroupId"));
1951 if (v != NULL)
1952 {
1953 #ifdef UNICODE
1954 char vmb[64];
1955 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, v, -1, vmb, 64, NULL, NULL);
1956 vmb[63] = 0;
1957 gid = GetGroupId(vmb);
1958 #else
1959 gid = GetGroupId(v);
1960 #endif
1961 }
1962 }
1963 if (uid == 0)
1964 {
1965 const TCHAR *v = g_config->getValue(_T("/%agent/UserId"));
1966 if (v != NULL)
1967 {
1968 #ifdef UNICODE
1969 char vmb[64];
1970 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, v, -1, vmb, 64, NULL, NULL);
1971 vmb[63] = 0;
1972 uid = GetUserId(vmb);
1973 #else
1974 uid = GetUserId(v);
1975 #endif
1976 }
1977 }
1978
1979 if (gid > 0)
1980 {
1981 if (setgid(gid) != 0)
1982 _tprintf(_T("setgid(%d) call failed (%s)\n"), gid, _tcserror(errno));
1983 }
1984 if (uid > 0)
1985 {
1986 if (setuid(uid) != 0)
1987 _tprintf(_T("setuid(%d) call failed (%s)\n"), uid, _tcserror(errno));
1988 }
1989 #endif
1990
1991 UpdateEnvironment();
1992
1993 s_pid = getpid();
1994 if (Initialize())
1995 {
1996 FILE *fp;
1997
1998 // Write PID file
1999 fp = _tfopen(g_szPidFile, _T("w"));
2000 if (fp != NULL)
2001 {
2002 _ftprintf(fp, _T("%d"), s_pid);
2003 fclose(fp);
2004 }
2005 Main();
2006 Shutdown();
2007 }
2008 else
2009 {
2010 ConsolePrintf(_T("Agent initialization failed\n"));
2011 nxlog_close();
2012 iExitCode = 3;
2013 }
2014 }
2015 #endif /* _WIN32 */
2016
2017 #if defined(_WIN32)
2018 if (s_shutdownCondition != INVALID_CONDITION_HANDLE)
2019 ConditionDestroy(s_shutdownCondition);
2020 #endif
2021 #ifdef _WIN32
2022 LIBNETXMS_EXCEPTION_HANDLER
2023 #endif
2024 }
2025 else
2026 {
2027 ConsolePrintf(_T("Error parsing configuration file\n"));
2028 iExitCode = 2;
2029 }
2030 }
2031 else
2032 {
2033 ConsolePrintf(_T("Error loading configuration file\n"));
2034 iExitCode = 2;
2035 }
2036 break;
2037 case ACTION_CHECK_CONFIG:
2038 {
2039 bool validConfig = g_config->loadConfig(g_szConfigFile, DEFAULT_CONFIG_SECTION, false);
2040 if (validConfig)
2041 {
2042 const TCHAR *dir = g_config->getValue(_T("/%agent/ConfigIncludeDir"));
2043 if (dir != NULL)
2044 {
2045 validConfig = g_config->loadConfigDirectory(dir, DEFAULT_CONFIG_SECTION, false);
2046 if (!validConfig)
2047 {
2048 ConsolePrintf(_T("Error reading additional configuration files from \"%s\"\n"), dir);
2049 }
2050 }
2051 }
2052
2053 if (validConfig)
2054 {
2055 validConfig = g_config->loadConfigDirectory(g_szConfigPolicyDir, DEFAULT_CONFIG_SECTION);
2056 if (!validConfig)
2057 {
2058 ConsolePrintf(_T("Error reading additional configuration files from \"%s\"\n"), g_szConfigPolicyDir);
2059 }
2060 }
2061
2062 if (validConfig)
2063 {
2064 g_config->print(stdout);
2065 validConfig = g_config->parseTemplate(configSection, m_cfgTemplate);
2066 }
2067
2068 if (!validConfig)
2069 {
2070 ConsolePrintf(_T("Configuration file check failed\n"));
2071 iExitCode = 2;
2072 }
2073 }
2074 break;
2075 case ACTION_RUN_WATCHDOG:
2076 iExitCode = WatchdogMain(dwMainPID);
2077 break;
2078 case ACTION_CREATE_CONFIG:
2079 iExitCode = CreateConfig(CHECK_NULL_A(argv[optind]), CHECK_NULL_A(argv[optind + 1]),
2080 CHECK_NULL_A(argv[optind + 2]), CHECK_NULL_A(argv[optind + 3]),
2081 argc - optind - 4, &argv[optind + 4]);
2082 break;
2083 case ACTION_SHUTDOWN_EXT_AGENTS:
2084 InitiateExtSubagentShutdown();
2085 iExitCode = 0;
2086 break;
2087 #ifdef _WIN32
2088 case ACTION_INSTALL_SERVICE:
2089 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
2090 InstallService(szModuleName, g_szConfigFile, s_debugLevel);
2091 break;
2092 case ACTION_REMOVE_SERVICE:
2093 RemoveService();
2094 break;
2095 case ACTION_INSTALL_EVENT_SOURCE:
2096 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
2097 InstallEventSource(szModuleName);
2098 break;
2099 case ACTION_REMOVE_EVENT_SOURCE:
2100 RemoveEventSource();
2101 break;
2102 case ACTION_START_SERVICE:
2103 StartAgentService();
2104 break;
2105 case ACTION_STOP_SERVICE:
2106 StopAgentService();
2107 break;
2108 #endif
2109 case ACTION_HELP:
2110 _fputts(m_szHelpText, stdout);
2111 break;
2112 default:
2113 break;
2114 }
2115
2116 return iExitCode;
2117 }