154a4587136d482c895252ed0b47b3da8aa7ec25
[public/netxms.git] / src / agent / core / nxagentd.cpp
1 /*
2 ** NetXMS multiplatform core agent
3 ** Copyright (C) 2003, 2004 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 usefu,,
11 ** but ITHOUT 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 ** $module: nxagentd.cpp
20 **
21 **/
22
23 #include "nxagentd.h"
24
25 #ifdef _WIN32
26 #include <conio.h>
27 #endif
28
29 #ifdef _NETWARE
30 #include <screen.h>
31 #endif
32
33
34 //
35 // Externals
36 //
37
38 THREAD_RESULT THREAD_CALL ListenerThread(void *);
39
40
41 //
42 // Valid options for getopt()
43 //
44
45 #if defined(_WIN32)
46 #define VALID_OPTIONS "c:CdDhIRsSv"
47 #elif defined(_NETWARE)
48 #define VALID_OPTIONS "c:CDhv"
49 #else
50 #define VALID_OPTIONS "c:CdDhp:v"
51 #endif
52
53
54 //
55 // Actions
56 //
57
58 #define ACTION_NONE 0
59 #define ACTION_RUN_AGENT 1
60 #define ACTION_INSTALL_SERVICE 2
61 #define ACTION_REMOVE_SERVICE 3
62 #define ACTION_START_SERVICE 4
63 #define ACTION_STOP_SERVICE 5
64 #define ACTION_CHECK_CONFIG 6
65
66
67 //
68 // Global variables
69 //
70
71 DWORD g_dwFlags = 0;
72 char g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
73 char g_szSharedSecret[MAX_SECRET_LENGTH] = "admin";
74 char g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
75 WORD g_wListenPort = AGENT_LISTEN_PORT;
76 DWORD g_dwServerAddr[MAX_SERVERS];
77 DWORD g_dwServerCount = 0;
78 DWORD g_dwTimeOut = 5000; // Request timeout in milliseconds
79 time_t g_dwAgentStartTime;
80
81 #if !defined(_WIN32) && !defined(_NETWARE)
82 char g_szPidFile[MAX_PATH] = "/var/run/nxagentd.pid";
83 #endif
84
85 #ifdef _WIN32
86 DWORD (__stdcall *imp_GetGuiResources)(HANDLE, DWORD);
87 BOOL (__stdcall *imp_GetProcessIoCounters)(HANDLE, PIO_COUNTERS);
88 BOOL (__stdcall *imp_GetPerformanceInfo)(PPERFORMANCE_INFORMATION, DWORD);
89 BOOL (__stdcall *imp_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
90 DWORD (__stdcall *imp_HrLanConnectionNameFromGuidOrPath)(LPWSTR, LPWSTR, LPWSTR, LPDWORD);
91 #endif /* _WIN32 */
92
93
94 //
95 // Static variables
96 //
97
98 static char *m_pszActionList = NULL;
99 static char *m_pszServerList = NULL;
100 static char *m_pszSubagentList = NULL;
101 static char *m_pszExtParamList = NULL;
102 static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
103 static DWORD m_dwStartupDelay = 0;
104
105
106 //
107 // Configuration file template
108 //
109
110 static NX_CFG_TEMPLATE cfgTemplate[] =
111 {
112 { "Action", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList },
113 { "EnableActions", CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags },
114 { "ExternalParameter", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtParamList },
115 { "ListenPort", CT_WORD, 0, 0, 0, 0, &g_wListenPort },
116 { "LogFile", CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile },
117 { "LogUnresolvedSymbols", CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags },
118 { "RequireAuthentication", CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags },
119 { "Servers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList },
120 { "SharedSecret", CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret },
121 { "StartupDelay", CT_LONG, 0, 0, 0, 0, &m_dwStartupDelay },
122 { "SubAgent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList },
123 { "Timeout", CT_LONG, 0, 0, 0, 0, &g_dwTimeOut },
124 { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
125 };
126
127
128 //
129 // Help text
130 //
131
132 static char m_szHelpText[] =
133 "Usage: nxagentd [options]\n"
134 "Where valid options are:\n"
135 " -c <file> : Use configuration file <file> (default " AGENT_DEFAULT_CONFIG ")\n"
136 " -C : Check configuration file and exit\n"
137 #ifndef _NETWARE
138 " -d : Run as daemon/service\n"
139 #endif
140 " -D : Turn on debug output\n"
141 " -h : Display help and exit\n"
142 #ifdef _WIN32
143 " -I : Install Windows service\n"
144 " -R : Remove Windows service\n"
145 " -s : Start Windows servive\n"
146 " -S : Stop Windows service\n"
147 #endif
148 " -v : Display version and exit\n"
149 "\n";
150
151
152 #ifdef _WIN32
153
154 //
155 // Get proc address and write log file
156 //
157
158 static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
159 {
160 FARPROC ptr;
161
162 ptr = GetProcAddress(hModule, procName);
163 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
164 WriteLog(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
165 return ptr;
166 }
167
168
169 //
170 // Import symbols
171 //
172
173 static void ImportSymbols(void)
174 {
175 HMODULE hModule;
176
177 // USER32.DLL
178 hModule = GetModuleHandle("USER32.DLL");
179 if (hModule != NULL)
180 {
181 imp_GetGuiResources = (DWORD (__stdcall *)(HANDLE, DWORD))GetProcAddressAndLog(hModule,"GetGuiResources");
182 }
183 else
184 {
185 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "USER32.DLL");
186 }
187
188 // KERNEL32.DLL
189 hModule = GetModuleHandle("KERNEL32.DLL");
190 if (hModule != NULL)
191 {
192 imp_GetProcessIoCounters = (BOOL (__stdcall *)(HANDLE, PIO_COUNTERS))GetProcAddressAndLog(hModule,"GetProcessIoCounters");
193 imp_GlobalMemoryStatusEx = (BOOL (__stdcall *)(LPMEMORYSTATUSEX))GetProcAddressAndLog(hModule,"GlobalMemoryStatusEx");
194 }
195 else
196 {
197 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "KERNEL32.DLL");
198 }
199
200 // PSAPI.DLL
201 hModule = GetModuleHandle("PSAPI.DLL");
202 if (hModule != NULL)
203 {
204 imp_GetPerformanceInfo = (BOOL (__stdcall *)(PPERFORMANCE_INFORMATION, DWORD))GetProcAddressAndLog(hModule,"GetPerformanceInfo");
205 }
206 else
207 {
208 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "PSAPI.DLL");
209 }
210
211 // NETMAN.DLL
212 hModule = LoadLibrary("NETMAN.DLL");
213 if (hModule != NULL)
214 {
215 imp_HrLanConnectionNameFromGuidOrPath =
216 (DWORD (__stdcall *)(LPWSTR, LPWSTR, LPWSTR, LPDWORD))GetProcAddressAndLog(hModule,
217 "HrLanConnectionNameFromGuidOrPath");
218 }
219 else
220 {
221 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "NETMAN.DLL");
222 }
223 }
224
225 #endif /* _WIN32 */
226
227
228 //
229 // This function writes message from subagent to agent's log
230 //
231
232 static void WriteSubAgentMsg(int iLevel, TCHAR *pszMsg)
233 {
234 WriteLog(MSG_SUBAGENT_MSG, iLevel, "s", pszMsg);
235 }
236
237
238 //
239 // Initialization routine
240 //
241
242 BOOL Initialize(void)
243 {
244 char *pItem, *pEnd;
245
246 // Open log file
247 InitLog();
248
249 #ifdef _WIN32
250 WSADATA wsaData;
251 if (WSAStartup(2, &wsaData) != 0)
252 {
253 WriteLog(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
254 return FALSE;
255 }
256 #endif
257
258 // Initialize logger for subagents
259 InitSubAgentsLogger(WriteSubAgentMsg);
260
261 // Initialize built-in parameters
262 if (!InitParameterList())
263 return FALSE;
264
265 #ifdef _WIN32
266 // Dynamically import functions that may not be presented in all Windows versions
267 ImportSymbols();
268 #endif
269
270 // Parse server list
271 if (m_pszServerList != NULL)
272 {
273 for(pItem = m_pszServerList; *pItem != 0; pItem = pEnd + 1)
274 {
275 pEnd = strchr(pItem, ',');
276 if (pEnd != NULL)
277 *pEnd = 0;
278 StrStrip(pItem);
279 g_dwServerAddr[g_dwServerCount] = inet_addr(pItem);
280 if ((g_dwServerAddr[g_dwServerCount] == INADDR_NONE) ||
281 (g_dwServerAddr[g_dwServerCount] == INADDR_ANY))
282 {
283 if (!(g_dwFlags & AF_DAEMON))
284 printf("Invalid server address '%s'\n", pItem);
285 }
286 else
287 {
288 g_dwServerCount++;
289 }
290 }
291 free(m_pszServerList);
292 }
293
294 // Load subagents
295 if (m_pszSubagentList != NULL)
296 {
297 for(pItem = m_pszSubagentList; *pItem != 0; pItem = pEnd + 1)
298 {
299 pEnd = strchr(pItem, '\n');
300 if (pEnd != NULL)
301 *pEnd = 0;
302 StrStrip(pItem);
303 LoadSubAgent(pItem);
304 }
305 free(m_pszSubagentList);
306 }
307
308 // Parse action list
309 if (m_pszActionList != NULL)
310 {
311 for(pItem = m_pszActionList; *pItem != 0; pItem = pEnd + 1)
312 {
313 pEnd = strchr(pItem, '\n');
314 if (pEnd != NULL)
315 *pEnd = 0;
316 StrStrip(pItem);
317 if (!AddActionFromConfig(pItem))
318 WriteLog(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
319 }
320 free(m_pszActionList);
321 }
322
323 // Parse external parameters list
324 if (m_pszExtParamList != NULL)
325 {
326 for(pItem = m_pszExtParamList; *pItem != 0; pItem = pEnd + 1)
327 {
328 pEnd = strchr(pItem, '\n');
329 if (pEnd != NULL)
330 *pEnd = 0;
331 StrStrip(pItem);
332 if (!AddExternalParameter(pItem))
333 WriteLog(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
334 }
335 free(m_pszExtParamList);
336 }
337
338 // Agent start time
339 g_dwAgentStartTime = time(NULL);
340
341 // Start network listener
342 ThreadCreate(ListenerThread, 0, NULL);
343
344 m_hCondShutdown = ConditionCreate(FALSE);
345 ThreadSleep(1);
346
347 return TRUE;
348 }
349
350
351 //
352 // Shutdown routine
353 //
354
355 void Shutdown(void)
356 {
357 // Set shutdowm flag and sleep for some time
358 // to allow other threads to finish
359 g_dwFlags |= AF_SHUTDOWN;
360 ThreadSleep(5);
361
362 UnloadAllSubAgents();
363 CloseLog();
364
365 // Notify main thread about shutdown
366 ConditionSet(m_hCondShutdown);
367
368 // Remove PID file
369 #if !defined(_WIN32) && !defined(_NETWARE)
370 remove(g_szPidFile);
371 #endif
372 }
373
374
375 //
376 // Common Main()
377 //
378
379 void Main(void)
380 {
381 if (m_dwStartupDelay > 0)
382 {
383 if (g_dwFlags & AF_DAEMON)
384 {
385 ThreadSleep(m_dwStartupDelay);
386 }
387 else
388 {
389 DWORD i;
390
391 printf("XXXXXX%*s]\rWAIT [", m_dwStartupDelay, " ");
392 fflush(stdout);
393 for(i = 0; i < m_dwStartupDelay; i++)
394 {
395 ThreadSleep(1);
396 putc('.', stdout);
397 fflush(stdout);
398 }
399 printf("\n");
400 }
401 }
402 WriteLog(MSG_AGENT_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
403
404 if (g_dwFlags & AF_DAEMON)
405 {
406 ConditionWait(m_hCondShutdown, INFINITE);
407 }
408 else
409 {
410 #if defined(_WIN32)
411 printf("Agent running. Press ESC to shutdown.\n");
412 while(1)
413 {
414 if (getch() == 27)
415 break;
416 }
417 printf("Agent shutting down...\n");
418 Shutdown();
419 #elif defined(_NETWARE)
420 printf("Agent running. Type UNLOAD NXAGENTD on the system console for shutdown.\n");
421 ConditionWait(m_hCondShutdown, INFINITE);
422 #else
423 printf("Agent running. Press Ctrl+C to shutdown.\n");
424 ConditionWait(m_hCondShutdown, INFINITE);
425 #endif
426 }
427 }
428
429
430 //
431 // Startup
432 //
433
434 int main(int argc, char *argv[])
435 {
436 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
437 #ifdef _WIN32
438 char szModuleName[MAX_PATH];
439 #endif
440
441 #ifdef _NETWARE
442 setscreenmode(SCR_AUTOCLOSE_ON_EXIT | SCR_COLOR_ATTRS);
443 #endif
444
445 // Parse command line
446 opterr = 1;
447 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
448 {
449 switch(ch)
450 {
451 case 'h': // Display help and exit
452 printf(m_szHelpText);
453 iAction = ACTION_NONE;
454 break;
455 case 'd': // Run as daemon
456 g_dwFlags |= AF_DAEMON;
457 break;
458 case 'D': // Turn on debug output
459 g_dwFlags |= AF_DEBUG;
460 break;
461 case 'c': // Configuration file
462 strncpy(g_szConfigFile, optarg, MAX_PATH - 1);
463 break;
464 #if !defined(_WIN32) && !defined(_NETWARE)
465 case 'p': // PID file
466 strncpy(g_szPidFile, optarg, MAX_PATH - 1);
467 break;
468 #endif
469 case 'C': // Configuration check only
470 iAction = ACTION_CHECK_CONFIG;
471 break;
472 case 'v': // Print version and exit
473 printf("NetXMS Core Agent Version " AGENT_VERSION_STRING "\n");
474 iAction = ACTION_NONE;
475 break;
476 #ifdef _WIN32
477 case 'I': // Install Windows service
478 iAction = ACTION_INSTALL_SERVICE;
479 break;
480 case 'R': // Remove Windows service
481 iAction = ACTION_REMOVE_SERVICE;
482 break;
483 case 's': // Start Windows service
484 iAction = ACTION_START_SERVICE;
485 break;
486 case 'S': // Stop Windows service
487 iAction = ACTION_STOP_SERVICE;
488 break;
489 #endif
490 case '?':
491 iAction = ACTION_NONE;
492 iExitCode = 1;
493 break;
494 default:
495 break;
496 }
497 }
498
499 // Do requested action
500 switch(iAction)
501 {
502 case ACTION_RUN_AGENT:
503 if (NxLoadConfig(g_szConfigFile, "", cfgTemplate, !(g_dwFlags & AF_DAEMON)) == NXCFG_ERR_OK)
504 {
505 if ((!stricmp(g_szLogFile, "{syslog}")) ||
506 (!stricmp(g_szLogFile, "{eventlog}")))
507 g_dwFlags |= AF_USE_SYSLOG;
508
509 #ifdef _WIN32
510 if (g_dwFlags & AF_DAEMON)
511 {
512 InitService();
513 }
514 else
515 {
516 if (Initialize())
517 {
518 Main();
519 }
520 else
521 {
522 ConsolePrintf("Agent initialization failed\n");
523 CloseLog();
524 iExitCode = 3;
525 }
526 }
527 #else /* _WIN32 */
528 #ifndef _NETWARE
529 if (g_dwFlags & AF_DAEMON)
530 if (daemon(0, 0) == -1)
531 {
532 perror("Unable to setup itself as a daemon");
533 iExitCode = 4;
534 }
535 #endif
536 if (iExitCode == 0)
537 {
538 if (Initialize())
539 {
540 #ifndef _NETWARE
541 FILE *fp;
542
543 // Write PID file
544 fp = fopen(g_szPidFile, "w");
545 if (fp != NULL)
546 {
547 fprintf(fp, "%d", getpid());
548 fclose(fp);
549 }
550 #endif
551 Main();
552 }
553 else
554 {
555 ConsolePrintf("Agent initialization failed\n");
556 CloseLog();
557 iExitCode = 3;
558 }
559 }
560 #endif /* _WIN32 */
561
562 if (m_hCondShutdown != INVALID_CONDITION_HANDLE)
563 ConditionDestroy(m_hCondShutdown);
564 }
565 else
566 {
567 ConsolePrintf("Error loading configuration file\n");
568 iExitCode = 2;
569 }
570 break;
571 case ACTION_CHECK_CONFIG:
572 if (NxLoadConfig(g_szConfigFile, "", cfgTemplate, !(g_dwFlags & AF_DAEMON)) != NXCFG_ERR_OK)
573 {
574 ConsolePrintf("Configuration file check failed\n");
575 iExitCode = 2;
576 }
577 break;
578 #ifdef _WIN32
579 case ACTION_INSTALL_SERVICE:
580 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
581 InstallService(szModuleName, g_szConfigFile);
582 break;
583 case ACTION_REMOVE_SERVICE:
584 RemoveService();
585 break;
586 case ACTION_START_SERVICE:
587 StartAgentService();
588 break;
589 case ACTION_STOP_SERVICE:
590 StopAgentService();
591 break;
592 #endif
593 default:
594 break;
595 }
596
597 #ifdef _NETWARE
598 if ((iExitCode != 0) || (iAction == ACTION_NONE) ||
599 (iAction == ACTION_CHECK_CONFIG))
600 setscreenmode(SCR_NO_MODE);
601 #endif
602
603 return iExitCode;
604 }