1ce1a3ae123df9c37f4bb719eefde81dc5a24ddd
[public/netxms.git] / src / agent / core / extagent.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: extagent.cpp
20 **
21 **/
22
23 #include "nxagentd.h"
24
25 /**
26 *Static data
27 */
28 static ObjectArray<ExternalSubagent> s_subagents;
29
30 /**
31 * Constructor
32 */
33 ExternalSubagent::ExternalSubagent(const TCHAR *name, const TCHAR *user)
34 {
35 nx_strncpy(m_name, name, MAX_SUBAGENT_NAME);
36 nx_strncpy(m_user, user, MAX_ESA_USER_NAME);
37 m_connected = false;
38 m_listener = NULL;
39 m_pipe = INVALID_PIPE_HANDLE;
40 m_msgQueue = new MsgWaitQueue();
41 m_requestId = 1;
42 m_mutexPipeWrite = MutexCreate();
43 }
44
45 /**
46 * Destructor
47 */
48 ExternalSubagent::~ExternalSubagent()
49 {
50 delete m_msgQueue;
51 MutexDestroy(m_mutexPipeWrite);
52 delete m_listener;
53 }
54
55 /**
56 * Pipe request handler
57 */
58 static void RequestHandler(NamedPipe *pipe, void *userArg)
59 {
60 static_cast<ExternalSubagent*>(userArg)->connect(pipe->handle());
61 }
62
63 /**
64 * Start listener
65 */
66 void ExternalSubagent::startListener()
67 {
68 TCHAR name[MAX_PIPE_NAME_LEN];
69 _sntprintf(name, MAX_PIPE_NAME_LEN, _T("nxagentd.subagent.%s"), m_name);
70 m_listener = NamedPipe::createListener(name, RequestHandler, this);
71 if (m_listener != NULL)
72 m_listener->startServer();
73 }
74
75 /*
76 * Send message to external subagent
77 */
78 bool ExternalSubagent::sendMessage(NXCPMessage *msg)
79 {
80 TCHAR buffer[256];
81 AgentWriteDebugLog(6, _T("ExternalSubagent::sendMessage(%s): sending message %s"), m_name, NXCPMessageCodeName(msg->getCode(), buffer));
82
83 NXCP_MESSAGE *rawMsg = msg->createMessage();
84 MutexLock(m_mutexPipeWrite);
85 bool success = SendMessageToPipe(m_pipe, rawMsg);
86 MutexUnlock(m_mutexPipeWrite);
87 free(rawMsg);
88 return success;
89 }
90
91 /**
92 * Wait for specific message to arrive
93 */
94 NXCPMessage *ExternalSubagent::waitForMessage(WORD code, UINT32 id)
95 {
96 return m_msgQueue->waitForMessage(code, id, 5000); // 5 sec timeout
97 }
98
99 /**
100 * Main connection thread
101 */
102 void ExternalSubagent::connect(HPIPE hPipe)
103 {
104 TCHAR buffer[256];
105 UINT32 i;
106
107 m_pipe = hPipe;
108 m_connected = true;
109 AgentWriteDebugLog(2, _T("ExternalSubagent(%s): connection established"), m_name);
110 PipeMessageReceiver receiver(hPipe, 8192, 1048576); // 8K initial, 1M max
111 while(true)
112 {
113 MessageReceiverResult result;
114 NXCPMessage *msg = receiver.readMessage(INFINITE, &result);
115 if (msg == NULL)
116 {
117 AgentWriteDebugLog(6, _T("ExternalSubagent(%s): receiver failure (%s)"), m_name, AbstractMessageReceiver::resultToText(result));
118 break;
119 }
120 AgentWriteDebugLog(6, _T("ExternalSubagent(%s): received message %s"), m_name, NXCPMessageCodeName(msg->getCode(), buffer));
121 switch(msg->getCode())
122 {
123 case CMD_PUSH_DCI_DATA:
124 MutexLock(g_hSessionListAccess);
125 for(i = 0; i < g_dwMaxSessions; i++)
126 if (g_pSessionList[i] != NULL)
127 if (g_pSessionList[i]->canAcceptTraps())
128 {
129 g_pSessionList[i]->sendMessage(msg);
130 }
131 MutexUnlock(g_hSessionListAccess);
132 delete msg;
133 break;
134 case CMD_TRAP:
135 ForwardTrap(msg);
136 delete msg;
137 break;
138 default:
139 m_msgQueue->put(msg);
140 break;
141 }
142 }
143
144 AgentWriteDebugLog(2, _T("ExternalSubagent(%s): connection closed"), m_name);
145 m_connected = false;
146 m_msgQueue->clear();
147 }
148
149 /**
150 * Send shutdown command
151 */
152 void ExternalSubagent::shutdown()
153 {
154 NXCPMessage msg(CMD_SHUTDOWN, m_requestId++);
155 sendMessage(&msg);
156 }
157
158 /**
159 * Send restart command
160 */
161 void ExternalSubagent::restart()
162 {
163 NXCPMessage msg(CMD_RESTART, m_requestId++);
164 sendMessage(&msg);
165 }
166
167 /**
168 * Get list of supported parameters
169 */
170 NETXMS_SUBAGENT_PARAM *ExternalSubagent::getSupportedParameters(UINT32 *count)
171 {
172 NXCPMessage msg;
173 NETXMS_SUBAGENT_PARAM *result = NULL;
174
175 msg.setCode(CMD_GET_PARAMETER_LIST);
176 msg.setId(m_requestId++);
177 if (sendMessage(&msg))
178 {
179 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
180 if (response != NULL)
181 {
182 if (response->getFieldAsUInt32(VID_RCC) == ERR_SUCCESS)
183 {
184 *count = response->getFieldAsUInt32(VID_NUM_PARAMETERS);
185 result = (NETXMS_SUBAGENT_PARAM *)malloc(*count * sizeof(NETXMS_SUBAGENT_PARAM));
186 UINT32 varId = VID_PARAM_LIST_BASE;
187 for(UINT32 i = 0; i < *count; i++)
188 {
189 response->getFieldAsString(varId++, result[i].name, MAX_PARAM_NAME);
190 response->getFieldAsString(varId++, result[i].description, MAX_DB_STRING);
191 result[i].dataType = (int)response->getFieldAsUInt16(varId++);
192 }
193 }
194 delete response;
195 }
196 }
197 return result;
198 }
199
200 /**
201 * Get list of supported lists
202 */
203 NETXMS_SUBAGENT_LIST *ExternalSubagent::getSupportedLists(UINT32 *count)
204 {
205 NXCPMessage msg;
206 NETXMS_SUBAGENT_LIST *result = NULL;
207
208 msg.setCode(CMD_GET_ENUM_LIST);
209 msg.setId(m_requestId++);
210 if (sendMessage(&msg))
211 {
212 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
213 if (response != NULL)
214 {
215 if (response->getFieldAsUInt32(VID_RCC) == ERR_SUCCESS)
216 {
217 *count = response->getFieldAsUInt32(VID_NUM_ENUMS);
218 result = (NETXMS_SUBAGENT_LIST *)malloc(*count * sizeof(NETXMS_SUBAGENT_LIST));
219 UINT32 varId = VID_ENUM_LIST_BASE;
220 for(UINT32 i = 0; i < *count; i++)
221 {
222 response->getFieldAsString(varId++, result[i].name, MAX_PARAM_NAME);
223 }
224 }
225 delete response;
226 }
227 }
228 return result;
229 }
230
231 /**
232 * Get list of supported tables
233 */
234 NETXMS_SUBAGENT_TABLE *ExternalSubagent::getSupportedTables(UINT32 *count)
235 {
236 NXCPMessage msg;
237 NETXMS_SUBAGENT_TABLE *result = NULL;
238
239 msg.setCode(CMD_GET_TABLE_LIST);
240 msg.setId(m_requestId++);
241 if (sendMessage(&msg))
242 {
243 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
244 if (response != NULL)
245 {
246 if (response->getFieldAsUInt32(VID_RCC) == ERR_SUCCESS)
247 {
248 *count = response->getFieldAsUInt32(VID_NUM_TABLES);
249 result = (NETXMS_SUBAGENT_TABLE *)malloc(*count * sizeof(NETXMS_SUBAGENT_TABLE));
250 UINT32 varId = VID_TABLE_LIST_BASE;
251 for(UINT32 i = 0; i < *count; i++)
252 {
253 response->getFieldAsString(varId++, result[i].name, MAX_PARAM_NAME);
254 response->getFieldAsString(varId++, result[i].instanceColumns, MAX_COLUMN_NAME * MAX_INSTANCE_COLUMNS);
255 response->getFieldAsString(varId++, result[i].description, MAX_DB_STRING);
256 }
257 }
258 delete response;
259 }
260 }
261 return result;
262 }
263
264 /**
265 * List supported parameters
266 */
267 void ExternalSubagent::listParameters(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
268 {
269 UINT32 paramCount = 0;
270 NETXMS_SUBAGENT_PARAM *list = getSupportedParameters(&paramCount);
271 if (list != NULL)
272 {
273 UINT32 id = *baseId;
274
275 for(UINT32 i = 0; i < paramCount; i++)
276 {
277 msg->setField(id++, list[i].name);
278 msg->setField(id++, list[i].description);
279 msg->setField(id++, (WORD)list[i].dataType);
280 }
281 *baseId = id;
282 *count += paramCount;
283 free(list);
284 }
285 }
286
287 /**
288 * List supported parameters
289 */
290 void ExternalSubagent::listParameters(StringList *list)
291 {
292 UINT32 paramCount = 0;
293 NETXMS_SUBAGENT_PARAM *plist = getSupportedParameters(&paramCount);
294 if (plist != NULL)
295 {
296 for(UINT32 i = 0; i < paramCount; i++)
297 list->add(plist[i].name);
298 free(plist);
299 }
300 }
301
302 /**
303 * List supported lists
304 */
305 void ExternalSubagent::listLists(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
306 {
307 UINT32 paramCount = 0;
308 NETXMS_SUBAGENT_LIST *list = getSupportedLists(&paramCount);
309 if (list != NULL)
310 {
311 UINT32 id = *baseId;
312
313 for(UINT32 i = 0; i < paramCount; i++)
314 {
315 msg->setField(id++, list[i].name);
316 }
317 *baseId = id;
318 *count += paramCount;
319 free(list);
320 }
321 }
322
323 /**
324 * List supported lists
325 */
326 void ExternalSubagent::listLists(StringList *list)
327 {
328 UINT32 paramCount = 0;
329 NETXMS_SUBAGENT_LIST *plist = getSupportedLists(&paramCount);
330 if (plist != NULL)
331 {
332 for(UINT32 i = 0; i < paramCount; i++)
333 list->add(plist[i].name);
334 free(plist);
335 }
336 }
337
338 /**
339 * List supported tables
340 */
341 void ExternalSubagent::listTables(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
342 {
343 UINT32 paramCount = 0;
344 NETXMS_SUBAGENT_TABLE *list = getSupportedTables(&paramCount);
345 if (list != NULL)
346 {
347 UINT32 id = *baseId;
348
349 for(UINT32 i = 0; i < paramCount; i++)
350 {
351 msg->setField(id++, list[i].name);
352 msg->setField(id++, list[i].instanceColumns);
353 msg->setField(id++, list[i].description);
354 }
355 *baseId = id;
356 *count += paramCount;
357 free(list);
358 }
359 }
360
361 /**
362 * List supported tables
363 */
364 void ExternalSubagent::listTables(StringList *list)
365 {
366 UINT32 paramCount = 0;
367 NETXMS_SUBAGENT_TABLE *plist = getSupportedTables(&paramCount);
368 if (plist != NULL)
369 {
370 for(UINT32 i = 0; i < paramCount; i++)
371 list->add(plist[i].name);
372 free(plist);
373 }
374 }
375
376 /**
377 * Get parameter value from external subagent
378 */
379 UINT32 ExternalSubagent::getParameter(const TCHAR *name, TCHAR *buffer)
380 {
381 NXCPMessage msg;
382 UINT32 rcc;
383
384 msg.setCode(CMD_GET_PARAMETER);
385 msg.setId(m_requestId++);
386 msg.setField(VID_PARAMETER, name);
387 if (sendMessage(&msg))
388 {
389 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
390 if (response != NULL)
391 {
392 rcc = response->getFieldAsUInt32(VID_RCC);
393 if (rcc == ERR_SUCCESS)
394 response->getFieldAsString(VID_VALUE, buffer, MAX_RESULT_LENGTH);
395 delete response;
396 }
397 else
398 {
399 rcc = ERR_INTERNAL_ERROR;
400 }
401 }
402 else
403 {
404 rcc = ERR_CONNECTION_BROKEN;
405 }
406 return rcc;
407 }
408
409 /**
410 * Get table value from external subagent
411 */
412 UINT32 ExternalSubagent::getTable(const TCHAR *name, Table *value)
413 {
414 NXCPMessage msg;
415 UINT32 rcc;
416
417 msg.setCode(CMD_GET_TABLE);
418 msg.setId(m_requestId++);
419 msg.setField(VID_PARAMETER, name);
420 if (sendMessage(&msg))
421 {
422 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
423 if (response != NULL)
424 {
425 rcc = response->getFieldAsUInt32(VID_RCC);
426 if (rcc == ERR_SUCCESS)
427 value->updateFromMessage(response);
428 delete response;
429 }
430 else
431 {
432 rcc = ERR_INTERNAL_ERROR;
433 }
434 }
435 else
436 {
437 rcc = ERR_CONNECTION_BROKEN;
438 }
439 return rcc;
440 }
441
442 /**
443 * Get list value from external subagent
444 */
445 UINT32 ExternalSubagent::getList(const TCHAR *name, StringList *value)
446 {
447 NXCPMessage msg;
448 UINT32 rcc;
449
450 msg.setCode(CMD_GET_LIST);
451 msg.setId(m_requestId++);
452 msg.setField(VID_PARAMETER, name);
453 if (sendMessage(&msg))
454 {
455 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
456 if (response != NULL)
457 {
458 rcc = response->getFieldAsUInt32(VID_RCC);
459 if (rcc == ERR_SUCCESS)
460 {
461 UINT32 count = response->getFieldAsUInt32(VID_NUM_STRINGS);
462 for(UINT32 i = 0; i < count; i++)
463 value->addPreallocated(response->getFieldAsString(VID_ENUM_VALUE_BASE + i));
464 }
465 delete response;
466 }
467 else
468 {
469 rcc = ERR_INTERNAL_ERROR;
470 }
471 }
472 else
473 {
474 rcc = ERR_CONNECTION_BROKEN;
475 }
476 return rcc;
477 }
478
479 /*
480 * Send message to external subagent
481 */
482 bool SendMessageToPipe(HPIPE hPipe, NXCP_MESSAGE *msg)
483 {
484 bool success = false;
485
486 #ifdef _WIN32
487 if (hPipe == INVALID_HANDLE_VALUE)
488 return false;
489
490 DWORD bytes = 0;
491 if (WriteFile(hPipe, msg, ntohl(msg->size), &bytes, NULL))
492 {
493 success = (bytes == ntohl(msg->size));
494 }
495 #else
496 if (hPipe == -1)
497 return false;
498
499 int bytes = SendEx(hPipe, msg, ntohl(msg->size), 0, NULL);
500 success = (bytes == ntohl(msg->size));
501 #endif
502 return success;
503 }
504
505 /**
506 * Add external subagent from config.
507 * Each line in config should be in form
508 * name:user
509 * If user part is omited or set to *, connection from any account will be accepted
510 */
511 bool AddExternalSubagent(const TCHAR *config)
512 {
513 TCHAR buffer[1024], user[256] = _T("*");
514
515 nx_strncpy(buffer, config, 1024);
516 TCHAR *ptr = _tcschr(buffer, _T(':'));
517 if (ptr != NULL)
518 {
519 *ptr = 0;
520 ptr++;
521 _tcsncpy(user, ptr, 256);
522 Trim(user);
523 }
524
525 ExternalSubagent *subagent = new ExternalSubagent(buffer, user);
526 s_subagents.add(subagent);
527 subagent->startListener();
528 return true;
529 }
530
531 /**
532 * Add parameters from external providers to NXCP message
533 */
534 void ListParametersFromExtSubagents(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
535 {
536 for(int i = 0; i < s_subagents.size(); i++)
537 {
538 if (s_subagents.get(i)->isConnected())
539 {
540 s_subagents.get(i)->listParameters(msg, baseId, count);
541 }
542 }
543 }
544
545 /**
546 * Add parameters from external subagents to string list
547 */
548 void ListParametersFromExtSubagents(StringList *list)
549 {
550 for(int i = 0; i < s_subagents.size(); i++)
551 {
552 if (s_subagents.get(i)->isConnected())
553 {
554 s_subagents.get(i)->listParameters(list);
555 }
556 }
557 }
558
559 /**
560 * Add lists from external providers to NXCP message
561 */
562 void ListListsFromExtSubagents(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
563 {
564 for(int i = 0; i < s_subagents.size(); i++)
565 {
566 if (s_subagents.get(i)->isConnected())
567 {
568 s_subagents.get(i)->listLists(msg, baseId, count);
569 }
570 }
571 }
572
573 /**
574 * Add lists from external subagents to string list
575 */
576 void ListListsFromExtSubagents(StringList *list)
577 {
578 for(int i = 0; i < s_subagents.size(); i++)
579 {
580 if (s_subagents.get(i)->isConnected())
581 {
582 s_subagents.get(i)->listLists(list);
583 }
584 }
585 }
586
587 /**
588 * Add tables from external providers to NXCP message
589 */
590 void ListTablesFromExtSubagents(NXCPMessage *msg, UINT32 *baseId, UINT32 *count)
591 {
592 for(int i = 0; i < s_subagents.size(); i++)
593 {
594 if (s_subagents.get(i)->isConnected())
595 {
596 s_subagents.get(i)->listTables(msg, baseId, count);
597 }
598 }
599 }
600
601 /**
602 * Add tables from external subagents to string list
603 */
604 void ListTablesFromExtSubagents(StringList *list)
605 {
606 for(int i = 0; i < s_subagents.size(); i++)
607 {
608 if (s_subagents.get(i)->isConnected())
609 {
610 s_subagents.get(i)->listTables(list);
611 }
612 }
613 }
614
615 /**
616 * Get value from external subagent
617 *
618 * @return agent error code
619 */
620 UINT32 GetParameterValueFromExtSubagent(const TCHAR *name, TCHAR *buffer)
621 {
622 UINT32 rc = ERR_UNKNOWN_PARAMETER;
623 for(int i = 0; i < s_subagents.size(); i++)
624 {
625 if (s_subagents.get(i)->isConnected())
626 {
627 rc = s_subagents.get(i)->getParameter(name, buffer);
628 if (rc == ERR_SUCCESS)
629 break;
630 }
631 }
632 return rc;
633 }
634
635 /**
636 * Get table from external subagent
637 *
638 * @return agent error code
639 */
640 UINT32 GetTableValueFromExtSubagent(const TCHAR *name, Table *value)
641 {
642 UINT32 rc = ERR_UNKNOWN_PARAMETER;
643 for(int i = 0; i < s_subagents.size(); i++)
644 {
645 if (s_subagents.get(i)->isConnected())
646 {
647 rc = s_subagents.get(i)->getTable(name, value);
648 if (rc == ERR_SUCCESS)
649 break;
650 }
651 }
652 return rc;
653 }
654
655 /**
656 * Get list from external subagent
657 *
658 * @return agent error code
659 */
660 UINT32 GetListValueFromExtSubagent(const TCHAR *name, StringList *value)
661 {
662 UINT32 rc = ERR_UNKNOWN_PARAMETER;
663 for(int i = 0; i < s_subagents.size(); i++)
664 {
665 if (s_subagents.get(i)->isConnected())
666 {
667 rc = s_subagents.get(i)->getList(name, value);
668 if (rc == ERR_SUCCESS)
669 break;
670 }
671 }
672 return rc;
673 }
674
675 /**
676 * Shutdown all connected external subagents
677 */
678 void ShutdownExtSubagents()
679 {
680 for(int i = 0; i < s_subagents.size(); i++)
681 {
682 if (s_subagents.get(i)->isConnected())
683 {
684 nxlog_debug(1, _T("Sending SHUTDOWN command to external subagent %s"), s_subagents.get(i)->getName());
685 s_subagents.get(i)->shutdown();
686 }
687 }
688 }
689
690 /**
691 * Restart all connected external subagents
692 */
693 void RestartExtSubagents()
694 {
695 for(int i = 0; i < s_subagents.size(); i++)
696 {
697 if (s_subagents.get(i)->isConnected())
698 {
699 nxlog_debug(1, _T("Sending RESTART command to external subagent %s"), s_subagents.get(i)->getName());
700 s_subagents.get(i)->restart();
701 }
702 }
703 }
704
705 /**
706 * Handler for Agent.IsExternalSubagentConnected
707 */
708 LONG H_IsExtSubagentConnected(const TCHAR *cmd, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
709 {
710 TCHAR name[256];
711 if (!AgentGetParameterArg(cmd, 1, name, 256))
712 return SYSINFO_RC_UNSUPPORTED;
713 LONG rc = SYSINFO_RC_NO_SUCH_INSTANCE;
714 for(int i = 0; i < s_subagents.size(); i++)
715 {
716 if (!_tcsicmp(s_subagents.get(i)->getName(), name))
717 {
718 _tprintf(_T(">>> subagent %s connected %d\n"), s_subagents.get(i)->getName(), s_subagents.get(i)->isConnected());
719 ret_int(value, s_subagents.get(i)->isConnected() ? 1 : 0);
720 rc = SYSINFO_RC_SUCCESS;
721 break;
722 }
723 }
724 return rc;
725 }