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