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