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