Fixed bug in row deletion in policy editor, which can cause console to crash
[public/netxms.git] / src / server / libnxsrv / agent.cpp
CommitLineData
f77084bb
VK
1/*
2** NetXMS - Network Management System
3** Server Library
4** Copyright (C) 2003, 2004 Victor Kirhenshtein
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** $module: agent.cpp
21**
22**/
23
24#include "libnxsrv.h"
1ba9a162 25#include <stdarg.h>
f77084bb
VK
26
27
28//
29// Constants
30//
31
32#define RECEIVER_BUFFER_SIZE 262144
33
34
35//
36// Receiver thread starter
37//
38
ccdbbb52 39THREAD_RESULT THREAD_CALL AgentConnection::ReceiverThreadStarter(void *pArg)
f77084bb
VK
40{
41 ((AgentConnection *)pArg)->ReceiverThread();
ccdbbb52 42 return THREAD_OK;
f77084bb
VK
43}
44
45
46//
47// Default constructor for AgentConnection - normally shouldn't be used
48//
49
50AgentConnection::AgentConnection()
51{
52 m_dwAddr = inet_addr("127.0.0.1");
53 m_wPort = AGENT_LISTEN_PORT;
54 m_iAuthMethod = AUTH_NONE;
55 m_szSecret[0] = 0;
56 m_hSocket = -1;
57 m_tLastCommandTime = 0;
58 m_dwNumDataLines = 0;
59 m_ppDataLines = NULL;
60 m_pMsgWaitQueue = new MsgWaitQueue;
d1d0b3be 61 m_dwRequestId = 1;
f77084bb 62 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
1c62be36 63 m_bIsConnected = FALSE;
38e832d6 64 m_mutexDataLock = MutexCreate();
ccdbbb52 65 m_hReceiverThread = INVALID_THREAD_HANDLE;
f77084bb
VK
66}
67
68
69//
70// Normal constructor for AgentConnection
71//
72
268774a0 73AgentConnection::AgentConnection(DWORD dwAddr, WORD wPort, int iAuthMethod, TCHAR *pszSecret)
f77084bb
VK
74{
75 m_dwAddr = dwAddr;
76 m_wPort = wPort;
77 m_iAuthMethod = iAuthMethod;
268774a0
VK
78 if (pszSecret != NULL)
79 {
80#ifdef UNICODE
81 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
82 pszSecret, -1, m_szSecret, MAX_SECRET_LENGTH, NULL, NULL);
83#else
84 strncpy(m_szSecret, pszSecret, MAX_SECRET_LENGTH);
85#endif
86 }
f77084bb 87 else
268774a0 88 {
f77084bb 89 m_szSecret[0] = 0;
268774a0 90 }
f77084bb
VK
91 m_hSocket = -1;
92 m_tLastCommandTime = 0;
93 m_dwNumDataLines = 0;
94 m_ppDataLines = NULL;
95 m_pMsgWaitQueue = new MsgWaitQueue;
d1d0b3be 96 m_dwRequestId = 1;
f77084bb 97 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
1c62be36 98 m_bIsConnected = FALSE;
38e832d6 99 m_mutexDataLock = MutexCreate();
ccdbbb52 100 m_hReceiverThread = INVALID_THREAD_HANDLE;
f77084bb
VK
101}
102
103
104//
105// Destructor
106//
107
108AgentConnection::~AgentConnection()
109{
1a60d114
VK
110 // Disconnect from peer
111 Disconnect();
38e832d6
VK
112
113 // Wait for receiver thread termination
ccdbbb52 114 ThreadJoin(m_hReceiverThread);
38e832d6
VK
115
116 Lock();
f77084bb 117 DestroyResultData();
38e832d6
VK
118 Unlock();
119
f77084bb 120 delete m_pMsgWaitQueue;
38e832d6
VK
121
122 MutexDestroy(m_mutexDataLock);
f77084bb
VK
123}
124
125
126//
059f6632
VK
127// Print message. This function is virtual and can be overrided in
128// derived classes. Default implementation will print message to stdout.
f77084bb
VK
129//
130
268774a0 131void AgentConnection::PrintMsg(TCHAR *pszFormat, ...)
f77084bb
VK
132{
133 va_list args;
134
135 va_start(args, pszFormat);
268774a0 136 _vtprintf(pszFormat, args);
f77084bb 137 va_end(args);
268774a0 138 _tprintf(_T("\n"));
f77084bb
VK
139}
140
141
142//
143// Receiver thread
144//
145
146void AgentConnection::ReceiverThread(void)
147{
148 CSCPMessage *pMsg;
149 CSCP_MESSAGE *pRawMsg;
150 CSCP_BUFFER *pMsgBuffer;
151 int iErr;
268774a0 152 TCHAR szBuffer[128];
f77084bb
VK
153
154 // Initialize raw message receiving function
9d72bde1 155 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
f77084bb
VK
156 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
157
158 // Allocate space for raw message
9d72bde1 159 pRawMsg = (CSCP_MESSAGE *)malloc(RECEIVER_BUFFER_SIZE);
f77084bb
VK
160
161 // Message receiving loop
162 while(1)
163 {
164 // Receive raw message
165 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, pMsgBuffer, RECEIVER_BUFFER_SIZE)) <= 0)
166 break;
167
168 // Check if we get too large message
169 if (iErr == 1)
170 {
268774a0 171 PrintMsg(_T("Received too large message %s (%ld bytes)"),
f77084bb
VK
172 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
173 ntohl(pRawMsg->dwSize));
174 continue;
175 }
176
177 // Check that actual received packet size is equal to encoded in packet
178 if ((int)ntohl(pRawMsg->dwSize) != iErr)
179 {
268774a0 180 PrintMsg(_T("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]"), ntohl(pRawMsg->dwSize), iErr);
f77084bb
VK
181 continue; // Bad packet, wait for next
182 }
183
184 // Create message object from raw message
185 pMsg = new CSCPMessage(pRawMsg);
186 if (pMsg->GetCode() == CMD_TRAP)
187 {
901c96c7 188 OnTrap(pMsg);
f77084bb
VK
189 delete pMsg;
190 }
191 else
192 {
193 m_pMsgWaitQueue->Put(pMsg);
194 }
195 }
196
1c62be36
VK
197 // Close socket and mark connection as disconnected
198 Disconnect();
199
9d72bde1
VK
200 free(pRawMsg);
201 free(pMsgBuffer);
f77084bb
VK
202}
203
204
205//
206// Connect to agent
207//
208
209BOOL AgentConnection::Connect(BOOL bVerbose)
210{
211 struct sockaddr_in sa;
268774a0 212 TCHAR szBuffer[256];
f77084bb 213 BOOL bSuccess = FALSE;
d1d0b3be 214 DWORD dwError;
f77084bb 215
1c62be36
VK
216 // Check if already connected
217 if ((m_bIsConnected) || (m_hSocket != -1))
218 return FALSE;
219
ccdbbb52
VK
220 // Wait for receiver thread from previous connection, if any
221 ThreadJoin(m_hReceiverThread);
222 m_hReceiverThread = INVALID_THREAD_HANDLE;
223
f77084bb
VK
224 // Create socket
225 m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
226 if (m_hSocket == -1)
227 {
268774a0 228 PrintMsg(_T("Call to socket() failed"));
f77084bb
VK
229 goto connect_cleanup;
230 }
231
232 // Fill in address structure
233 memset(&sa, 0, sizeof(sa));
234 sa.sin_addr.s_addr = m_dwAddr;
235 sa.sin_family = AF_INET;
236 sa.sin_port = htons(m_wPort);
237
238 // Connect to server
239 if (connect(m_hSocket, (struct sockaddr *)&sa, sizeof(sa)) == -1)
240 {
241 if (bVerbose)
268774a0 242 PrintMsg(_T("Cannot establish connection with agent %s"), IpToStr(m_dwAddr, szBuffer));
f77084bb
VK
243 goto connect_cleanup;
244 }
245
246 // Start receiver thread
ccdbbb52 247 m_hReceiverThread = ThreadCreateEx(ReceiverThreadStarter, 0, this);
f77084bb
VK
248
249 // Authenticate itself to agent
d1d0b3be 250 if ((dwError = Authenticate()) != ERR_SUCCESS)
f77084bb 251 {
268774a0 252 PrintMsg(_T("Authentication to agent %s failed (%s)"), IpToStr(m_dwAddr, szBuffer),
d1d0b3be
VK
253 AgentErrorCodeToText(dwError));
254 goto connect_cleanup;
f77084bb
VK
255 }
256
257 // Test connectivity
d1d0b3be 258 if ((dwError = Nop()) != ERR_SUCCESS)
f77084bb 259 {
268774a0 260 PrintMsg(_T("Communication with agent %s failed (%s)"), IpToStr(m_dwAddr, szBuffer),
d1d0b3be 261 AgentErrorCodeToText(dwError));
f77084bb
VK
262 goto connect_cleanup;
263 }
264
265 bSuccess = TRUE;
266
267connect_cleanup:
268 if (!bSuccess)
269 {
1c62be36 270 Lock();
f77084bb
VK
271 if (m_hSocket != -1)
272 {
273 shutdown(m_hSocket, 2);
274 closesocket(m_hSocket);
275 m_hSocket = -1;
276 }
1c62be36 277 Unlock();
f77084bb 278 }
1c62be36 279 m_bIsConnected = bSuccess;
f77084bb
VK
280 return bSuccess;
281}
282
283
284//
285// Disconnect from agent
286//
287
288void AgentConnection::Disconnect(void)
289{
1c62be36 290 Lock();
f77084bb
VK
291 if (m_hSocket != -1)
292 {
293 shutdown(m_hSocket, 2);
294 closesocket(m_hSocket);
295 m_hSocket = -1;
296 }
297 DestroyResultData();
1c62be36
VK
298 m_bIsConnected = FALSE;
299 Unlock();
f77084bb
VK
300}
301
302
303//
304// Destroy command execuion results data
305//
306
307void AgentConnection::DestroyResultData(void)
308{
309 DWORD i;
310
311 if (m_ppDataLines != NULL)
312 {
313 for(i = 0; i < m_dwNumDataLines; i++)
314 if (m_ppDataLines[i] != NULL)
9d72bde1
VK
315 free(m_ppDataLines[i]);
316 free(m_ppDataLines);
f77084bb
VK
317 m_ppDataLines = NULL;
318 }
319 m_dwNumDataLines = 0;
320}
321
322
323//
324// Get interface list from agent
325//
326
327INTERFACE_LIST *AgentConnection::GetInterfaceList(void)
328{
1c62be36
VK
329 INTERFACE_LIST *pIfList = NULL;
330 DWORD i;
268774a0 331 TCHAR *pChar, *pBuf;
1c62be36 332
268774a0 333 if (GetList(_T("Net.InterfaceList")) == ERR_SUCCESS)
1c62be36 334 {
9d72bde1 335 pIfList = (INTERFACE_LIST *)malloc(sizeof(INTERFACE_LIST));
1c62be36 336 pIfList->iNumEntries = m_dwNumDataLines;
9d72bde1 337 pIfList->pInterfaces = (INTERFACE_INFO *)malloc(sizeof(INTERFACE_INFO) * m_dwNumDataLines);
1c62be36
VK
338 memset(pIfList->pInterfaces, 0, sizeof(INTERFACE_INFO) * m_dwNumDataLines);
339 for(i = 0; i < m_dwNumDataLines; i++)
340 {
341 pBuf = m_ppDataLines[i];
342
343 // Index
268774a0 344 pChar = _tcschr(pBuf, ' ');
1c62be36
VK
345 if (pChar != NULL)
346 {
347 *pChar = 0;
268774a0 348 pIfList->pInterfaces[i].dwIndex = _tcstoul(pBuf, NULL, 10);
1c62be36
VK
349 pBuf = pChar + 1;
350 }
351
352 // Address and mask
268774a0 353 pChar = _tcschr(pBuf, _T(' '));
1c62be36
VK
354 if (pChar != NULL)
355 {
268774a0 356 TCHAR *pSlash;
1c62be36
VK
357
358 *pChar = 0;
268774a0 359 pSlash = _tcschr(pBuf, _T('/'));
1c62be36
VK
360 if (pSlash != NULL)
361 {
362 *pSlash = 0;
363 pSlash++;
364 }
365 else // Just a paranoia protection, should'n happen if agent working correctly
366 {
268774a0 367 pSlash = _T("24");
1c62be36 368 }
268774a0
VK
369 pIfList->pInterfaces[i].dwIpAddr = _t_inet_addr(pBuf);
370 pIfList->pInterfaces[i].dwIpNetMask = htonl(~(0xFFFFFFFF >> _tcstoul(pSlash, NULL, 10)));
1c62be36
VK
371 pBuf = pChar + 1;
372 }
373
374 // Interface type
268774a0 375 pChar = _tcschr(pBuf, ' ');
1c62be36
VK
376 if (pChar != NULL)
377 {
378 *pChar = 0;
268774a0 379 pIfList->pInterfaces[i].dwIndex = _tcstoul(pBuf, NULL, 10);
1c62be36
VK
380 pBuf = pChar + 1;
381 }
382
383 // Name
268774a0 384 _tcsncpy(pIfList->pInterfaces[i].szName, pBuf, MAX_OBJECT_NAME - 1);
1c62be36
VK
385 }
386
9d72bde1 387 Lock();
1c62be36 388 DestroyResultData();
9d72bde1 389 Unlock();
1c62be36
VK
390 }
391
392 return pIfList;
f77084bb
VK
393}
394
395
396//
397// Get parameter value
398//
399
268774a0 400DWORD AgentConnection::GetParameter(TCHAR *pszParam, DWORD dwBufSize, TCHAR *pszBuffer)
f77084bb
VK
401{
402 CSCPMessage msg, *pResponce;
403 DWORD dwRqId, dwRetCode;
404
1c62be36 405 if (m_bIsConnected)
f77084bb 406 {
1c62be36
VK
407 dwRqId = m_dwRequestId++;
408 msg.SetCode(CMD_GET_PARAMETER);
409 msg.SetId(dwRqId);
410 msg.SetVariable(VID_PARAMETER, pszParam);
411 if (SendMessage(&msg))
f77084bb 412 {
1c62be36
VK
413 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
414 if (pResponce != NULL)
415 {
416 dwRetCode = pResponce->GetVariableLong(VID_RCC);
417 if (dwRetCode == ERR_SUCCESS)
418 pResponce->GetVariableStr(VID_VALUE, pszBuffer, dwBufSize);
419 delete pResponce;
420 }
421 else
422 {
423 dwRetCode = ERR_REQUEST_TIMEOUT;
424 }
f77084bb
VK
425 }
426 else
427 {
1c62be36 428 dwRetCode = ERR_CONNECTION_BROKEN;
f77084bb
VK
429 }
430 }
431 else
432 {
1c62be36 433 dwRetCode = ERR_NOT_CONNECTED;
f77084bb
VK
434 }
435
436 return dwRetCode;
437}
438
439
440//
441// Get ARP cache
442//
443
444ARP_CACHE *AgentConnection::GetArpCache(void)
445{
1c62be36 446 ARP_CACHE *pArpCache = NULL;
268774a0 447 TCHAR szByte[4], *pBuf, *pChar;
1c62be36
VK
448 DWORD i, j;
449
268774a0 450 if (GetList(_T("Net.ArpCache")) == ERR_SUCCESS)
1c62be36
VK
451 {
452 // Create empty structure
9d72bde1 453 pArpCache = (ARP_CACHE *)malloc(sizeof(ARP_CACHE));
1c62be36 454 pArpCache->dwNumEntries = m_dwNumDataLines;
9d72bde1 455 pArpCache->pEntries = (ARP_ENTRY *)malloc(sizeof(ARP_ENTRY) * m_dwNumDataLines);
1c62be36
VK
456 memset(pArpCache->pEntries, 0, sizeof(ARP_ENTRY) * m_dwNumDataLines);
457
458 szByte[2] = 0;
459
460 // Parse data lines
461 // Each line has form of XXXXXXXXXXXX a.b.c.d
462 // where XXXXXXXXXXXX is a MAC address (12 hexadecimal digits)
463 // and a.b.c.d is an IP address in decimal dotted notation
464 for(i = 0; i < m_dwNumDataLines; i++)
465 {
466 pBuf = m_ppDataLines[i];
268774a0 467 if (_tcslen(pBuf) < 20) // Invalid line
1c62be36
VK
468 continue;
469
470 // MAC address
471 for(j = 0; j < 6; j++)
472 {
268774a0
VK
473 memcpy(szByte, pBuf, sizeof(TCHAR) * 2);
474 pArpCache->pEntries[i].bMacAddr[j] = (BYTE)_tcstol(szByte, NULL, 16);
1c62be36
VK
475 pBuf+=2;
476 }
477
478 // IP address
479 while(*pBuf == ' ')
480 pBuf++;
268774a0 481 pChar = _tcschr(pBuf, _T(' '));
1c62be36
VK
482 if (pChar != NULL)
483 *pChar = 0;
268774a0 484 pArpCache->pEntries[i].dwIpAddr = _t_inet_addr(pBuf);
1c62be36
VK
485
486 // Interface index
487 if (pChar != NULL)
268774a0 488 pArpCache->pEntries[i].dwIndex = _tcstoul(pChar + 1, NULL, 10);
1c62be36
VK
489 }
490
491 DestroyResultData();
492 }
493 return pArpCache;
f77084bb
VK
494}
495
496
497//
498// Send dummy command to agent (can be used for keepalive)
499//
500
501DWORD AgentConnection::Nop(void)
502{
503 CSCPMessage msg;
504 DWORD dwRqId;
505
506 dwRqId = m_dwRequestId++;
507 msg.SetCode(CMD_KEEPALIVE);
508 msg.SetId(dwRqId);
509 if (SendMessage(&msg))
510 return WaitForRCC(dwRqId, m_dwCommandTimeout);
511 else
512 return ERR_CONNECTION_BROKEN;
513}
514
515
516//
517// Wait for request completion code
518//
519
520DWORD AgentConnection::WaitForRCC(DWORD dwRqId, DWORD dwTimeOut)
521{
522 CSCPMessage *pMsg;
523 DWORD dwRetCode;
524
525 pMsg = m_pMsgWaitQueue->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, dwTimeOut);
526 if (pMsg != NULL)
527 {
528 dwRetCode = pMsg->GetVariableLong(VID_RCC);
529 delete pMsg;
530 }
531 else
532 {
533 dwRetCode = ERR_REQUEST_TIMEOUT;
534 }
535 return dwRetCode;
536}
537
538
539//
540// Send message to agent
541//
542
543BOOL AgentConnection::SendMessage(CSCPMessage *pMsg)
544{
545 CSCP_MESSAGE *pRawMsg;
546 BOOL bResult;
547
548 pRawMsg = pMsg->CreateMessage();
549 bResult = (send(m_hSocket, (char *)pRawMsg, ntohl(pRawMsg->dwSize), 0) == (int)ntohl(pRawMsg->dwSize));
9d72bde1 550 free(pRawMsg);
f77084bb
VK
551 return bResult;
552}
901c96c7
VK
553
554
555//
556// Trap handler. Should be overriden in derived classes to implement
557// actual trap processing. Default implementation do nothing.
558//
559
560void AgentConnection::OnTrap(CSCPMessage *pMsg)
561{
562}
563
564
565//
566// Get list of values
567//
568
268774a0 569DWORD AgentConnection::GetList(TCHAR *pszParam)
901c96c7
VK
570{
571 CSCPMessage msg, *pResponce;
572 DWORD i, dwRqId, dwRetCode;
573
1c62be36 574 if (m_bIsConnected)
901c96c7 575 {
1c62be36
VK
576 DestroyResultData();
577 dwRqId = m_dwRequestId++;
578 msg.SetCode(CMD_GET_LIST);
579 msg.SetId(dwRqId);
580 msg.SetVariable(VID_PARAMETER, pszParam);
581 if (SendMessage(&msg))
901c96c7 582 {
1c62be36
VK
583 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
584 if (pResponce != NULL)
585 {
586 dwRetCode = pResponce->GetVariableLong(VID_RCC);
587 if (dwRetCode == ERR_SUCCESS)
588 {
589 m_dwNumDataLines = pResponce->GetVariableLong(VID_NUM_STRINGS);
268774a0 590 m_ppDataLines = (TCHAR **)malloc(sizeof(TCHAR *) * m_dwNumDataLines);
1c62be36
VK
591 for(i = 0; i < m_dwNumDataLines; i++)
592 m_ppDataLines[i] = pResponce->GetVariableStr(VID_ENUM_VALUE_BASE + i);
593 }
594 delete pResponce;
595 }
596 else
901c96c7 597 {
1c62be36 598 dwRetCode = ERR_REQUEST_TIMEOUT;
901c96c7 599 }
901c96c7
VK
600 }
601 else
602 {
1c62be36 603 dwRetCode = ERR_CONNECTION_BROKEN;
901c96c7
VK
604 }
605 }
606 else
607 {
1c62be36 608 dwRetCode = ERR_NOT_CONNECTED;
901c96c7
VK
609 }
610
611 return dwRetCode;
612}
d1d0b3be
VK
613
614
615//
616// Authenticate to agent
617//
618
619DWORD AgentConnection::Authenticate(void)
620{
621 CSCPMessage msg;
622 DWORD dwRqId;
623 BYTE hash[32];
268774a0
VK
624#ifdef UNICODE
625 WCHAR szBuffer[MAX_SECRET_LENGTH];
626#endif
d1d0b3be
VK
627
628 if (m_iAuthMethod == AUTH_NONE)
629 return ERR_SUCCESS; // No authentication required
630
631 dwRqId = m_dwRequestId++;
632 msg.SetCode(CMD_AUTHENTICATE);
633 msg.SetId(dwRqId);
634 msg.SetVariable(VID_AUTH_METHOD, (WORD)m_iAuthMethod);
635 switch(m_iAuthMethod)
636 {
637 case AUTH_PLAINTEXT:
268774a0
VK
638#ifdef UNICODE
639 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_szSecret, -1, szBuffer, MAX_SECRET_LENGTH);
640 msg.SetVariable(VID_SHARED_SECRET, szBuffer);
641#else
d1d0b3be 642 msg.SetVariable(VID_SHARED_SECRET, m_szSecret);
268774a0 643#endif
d1d0b3be
VK
644 break;
645 case AUTH_MD5_HASH:
646 CalculateMD5Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
647 msg.SetVariable(VID_SHARED_SECRET, hash, MD5_DIGEST_SIZE);
648 break;
649 case AUTH_SHA1_HASH:
650 CalculateSHA1Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
651 msg.SetVariable(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
652 break;
653 default:
654 break;
655 }
656 if (SendMessage(&msg))
657 return WaitForRCC(dwRqId, m_dwCommandTimeout);
658 else
659 return ERR_CONNECTION_BROKEN;
660}
3c774461
VK
661
662
663//
664// Execute action on agent
665//
666
268774a0 667DWORD AgentConnection::ExecAction(TCHAR *pszAction, int argc, TCHAR **argv)
3c774461
VK
668{
669 CSCPMessage msg;
670 DWORD dwRqId;
671 int i;
672
1c62be36
VK
673 if (!m_bIsConnected)
674 return ERR_NOT_CONNECTED;
675
3c774461
VK
676 dwRqId = m_dwRequestId++;
677 msg.SetCode(CMD_ACTION);
678 msg.SetId(dwRqId);
679 msg.SetVariable(VID_ACTION_NAME, pszAction);
680 msg.SetVariable(VID_NUM_ARGS, (DWORD)argc);
681 for(i = 0; i < argc; i++)
682 msg.SetVariable(VID_ACTION_ARG_BASE + i, argv[i]);
683
684 if (SendMessage(&msg))
685 return WaitForRCC(dwRqId, m_dwCommandTimeout);
686 else
687 return ERR_CONNECTION_BROKEN;
688}