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