Implemented object caching on client side
[public/netxms.git] / src / libnxcl / objects.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Client Library
4 ** Copyright (C) 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: objects.cpp
21 **
22 **/
23
24 #include "libnxcl.h"
25
26
27 //
28 // Destroy object
29 //
30
31 void DestroyObject(NXC_OBJECT *pObject)
32 {
33 DebugPrintf(_T("DestroyObject(id:%ld, name:\"%s\")"), pObject->dwId, pObject->szName);
34 switch(pObject->iClass)
35 {
36 case OBJECT_CONTAINER:
37 safe_free(pObject->container.pszDescription);
38 break;
39 case OBJECT_NODE:
40 safe_free(pObject->node.pszDescription);
41 break;
42 case OBJECT_TEMPLATE:
43 safe_free(pObject->dct.pszDescription);
44 break;
45 case OBJECT_NETWORKSERVICE:
46 safe_free(pObject->netsrv.pszRequest);
47 safe_free(pObject->netsrv.pszResponce);
48 break;
49 }
50 safe_free(pObject->pdwChildList);
51 safe_free(pObject->pdwParentList);
52 safe_free(pObject->pAccessList);
53 free(pObject);
54 }
55
56
57 //
58 // Perform binary search on index
59 // Returns INVALID_INDEX if key not found or position of appropriate network object
60 // We assume that pIndex == NULL will not be passed
61 //
62
63 static DWORD SearchIndex(INDEX *pIndex, DWORD dwIndexSize, DWORD dwKey)
64 {
65 DWORD dwFirst, dwLast, dwMid;
66
67 if (dwIndexSize == 0)
68 return INVALID_INDEX;
69
70 dwFirst = 0;
71 dwLast = dwIndexSize - 1;
72
73 if ((dwKey < pIndex[0].dwKey) || (dwKey > pIndex[dwLast].dwKey))
74 return INVALID_INDEX;
75
76 while(dwFirst < dwLast)
77 {
78 dwMid = (dwFirst + dwLast) / 2;
79 if (dwKey == pIndex[dwMid].dwKey)
80 return dwMid;
81 if (dwKey < pIndex[dwMid].dwKey)
82 dwLast = dwMid - 1;
83 else
84 dwFirst = dwMid + 1;
85 }
86
87 if (dwKey == pIndex[dwLast].dwKey)
88 return dwLast;
89
90 return INVALID_INDEX;
91 }
92
93
94 //
95 // Index comparision callback for qsort()
96 //
97
98 static int IndexCompare(const void *pArg1, const void *pArg2)
99 {
100 return ((INDEX *)pArg1)->dwKey < ((INDEX *)pArg2)->dwKey ? -1 :
101 (((INDEX *)pArg1)->dwKey > ((INDEX *)pArg2)->dwKey ? 1 : 0);
102 }
103
104
105 //
106 // Add object to list
107 //
108
109 void NXCL_Session::AddObject(NXC_OBJECT *pObject, BOOL bSortIndex)
110 {
111 DebugPrintf(_T("AddObject(id:%ld, name:\"%s\")"), pObject->dwId, pObject->szName);
112 LockObjectIndex();
113 m_pIndexById = (INDEX *)realloc(m_pIndexById, sizeof(INDEX) * (m_dwNumObjects + 1));
114 m_pIndexById[m_dwNumObjects].dwKey = pObject->dwId;
115 m_pIndexById[m_dwNumObjects].pObject = pObject;
116 m_dwNumObjects++;
117 if (bSortIndex)
118 qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
119 UnlockObjectIndex();
120 }
121
122
123 //
124 // Replace object's data in list
125 //
126
127 static void ReplaceObject(NXC_OBJECT *pObject, NXC_OBJECT *pNewObject)
128 {
129 DebugPrintf(_T("ReplaceObject(id:%ld, name:\"%s\")"), pObject->dwId, pObject->szName);
130 switch(pObject->iClass)
131 {
132 case OBJECT_CONTAINER:
133 safe_free(pObject->container.pszDescription);
134 break;
135 case OBJECT_NODE:
136 safe_free(pObject->node.pszDescription);
137 break;
138 case OBJECT_TEMPLATE:
139 safe_free(pObject->dct.pszDescription);
140 break;
141 case OBJECT_NETWORKSERVICE:
142 safe_free(pObject->netsrv.pszRequest);
143 safe_free(pObject->netsrv.pszResponce);
144 break;
145 }
146 safe_free(pObject->pdwChildList);
147 safe_free(pObject->pdwParentList);
148 safe_free(pObject->pAccessList);
149 memcpy(pObject, pNewObject, sizeof(NXC_OBJECT));
150 free(pNewObject);
151 }
152
153
154 //
155 // Create new object from message
156 //
157
158 static NXC_OBJECT *NewObjectFromMsg(CSCPMessage *pMsg)
159 {
160 NXC_OBJECT *pObject;
161 DWORD i, dwId1, dwId2;
162
163 // Allocate memory for new object structure
164 pObject = (NXC_OBJECT *)malloc(sizeof(NXC_OBJECT));
165 memset(pObject, 0, sizeof(NXC_OBJECT));
166
167 // Common attributes
168 pObject->dwId = pMsg->GetVariableLong(VID_OBJECT_ID);
169 pObject->iClass = pMsg->GetVariableShort(VID_OBJECT_CLASS);
170 pMsg->GetVariableStr(VID_OBJECT_NAME, pObject->szName, MAX_OBJECT_NAME);
171 pObject->iStatus = pMsg->GetVariableShort(VID_OBJECT_STATUS);
172 pObject->dwIpAddr = pMsg->GetVariableLong(VID_IP_ADDRESS);
173 pObject->bIsDeleted = pMsg->GetVariableShort(VID_IS_DELETED);
174 pObject->dwImage = pMsg->GetVariableLong(VID_IMAGE_ID);
175
176 // Parents
177 pObject->dwNumParents = pMsg->GetVariableLong(VID_PARENT_CNT);
178 pObject->pdwParentList = (DWORD *)malloc(sizeof(DWORD) * pObject->dwNumParents);
179 for(i = 0, dwId1 = VID_PARENT_ID_BASE; i < pObject->dwNumParents; i++, dwId1++)
180 pObject->pdwParentList[i] = pMsg->GetVariableLong(dwId1);
181
182 // Childs
183 pObject->dwNumChilds = pMsg->GetVariableLong(VID_CHILD_CNT);
184 pObject->pdwChildList = (DWORD *)malloc(sizeof(DWORD) * pObject->dwNumChilds);
185 for(i = 0, dwId1 = VID_CHILD_ID_BASE; i < pObject->dwNumChilds; i++, dwId1++)
186 pObject->pdwChildList[i] = pMsg->GetVariableLong(dwId1);
187
188 // Access control
189 pObject->bInheritRights = pMsg->GetVariableShort(VID_INHERIT_RIGHTS);
190 pObject->dwAclSize = pMsg->GetVariableLong(VID_ACL_SIZE);
191 pObject->pAccessList = (NXC_ACL_ENTRY *)malloc(sizeof(NXC_ACL_ENTRY) * pObject->dwAclSize);
192 for(i = 0, dwId1 = VID_ACL_USER_BASE, dwId2 = VID_ACL_RIGHTS_BASE;
193 i < pObject->dwAclSize; i++, dwId1++, dwId2++)
194 {
195 pObject->pAccessList[i].dwUserId = pMsg->GetVariableLong(dwId1);
196 pObject->pAccessList[i].dwAccessRights = pMsg->GetVariableLong(dwId2);
197 }
198
199 // Class-specific attributes
200 switch(pObject->iClass)
201 {
202 case OBJECT_INTERFACE:
203 pObject->iface.dwIpNetMask = pMsg->GetVariableLong(VID_IP_NETMASK);
204 pObject->iface.dwIfIndex = pMsg->GetVariableLong(VID_IF_INDEX);
205 pObject->iface.dwIfType = pMsg->GetVariableLong(VID_IF_TYPE);
206 pMsg->GetVariableBinary(VID_MAC_ADDR, pObject->iface.bMacAddr, MAC_ADDR_LENGTH);
207 break;
208 case OBJECT_NODE:
209 pObject->node.dwFlags = pMsg->GetVariableLong(VID_FLAGS);
210 pObject->node.dwDiscoveryFlags = pMsg->GetVariableLong(VID_DISCOVERY_FLAGS);
211 pObject->node.dwNodeType = pMsg->GetVariableLong(VID_NODE_TYPE);
212 pObject->node.dwPollerNode = pMsg->GetVariableLong(VID_POLLER_NODE_ID);
213 pObject->node.wAgentPort = pMsg->GetVariableShort(VID_AGENT_PORT);
214 pObject->node.wAuthMethod = pMsg->GetVariableShort(VID_AUTH_METHOD);
215 pMsg->GetVariableStr(VID_SHARED_SECRET, pObject->node.szSharedSecret, MAX_SECRET_LENGTH);
216 pMsg->GetVariableStr(VID_COMMUNITY_STRING, pObject->node.szCommunityString, MAX_COMMUNITY_LENGTH);
217 pMsg->GetVariableStr(VID_SNMP_OID, pObject->node.szObjectId, MAX_OID_LENGTH);
218 pObject->node.pszDescription = pMsg->GetVariableStr(VID_DESCRIPTION);
219 pObject->node.wSNMPVersion = pMsg->GetVariableShort(VID_SNMP_VERSION);
220 pMsg->GetVariableStr(VID_AGENT_VERSION, pObject->node.szAgentVersion, MAX_AGENT_VERSION_LEN);
221 pMsg->GetVariableStr(VID_PLATFORM_NAME, pObject->node.szPlatformName, MAX_PLATFORM_NAME_LEN);
222 break;
223 case OBJECT_SUBNET:
224 pObject->subnet.dwIpNetMask = pMsg->GetVariableLong(VID_IP_NETMASK);
225 break;
226 case OBJECT_CONTAINER:
227 pObject->container.dwCategory = pMsg->GetVariableLong(VID_CATEGORY);
228 pObject->container.pszDescription = pMsg->GetVariableStr(VID_DESCRIPTION);
229 break;
230 case OBJECT_TEMPLATE:
231 pObject->dct.dwVersion = pMsg->GetVariableLong(VID_TEMPLATE_VERSION);
232 pObject->dct.pszDescription = pMsg->GetVariableStr(VID_DESCRIPTION);
233 break;
234 case OBJECT_NETWORKSERVICE:
235 pObject->netsrv.iServiceType = (int)pMsg->GetVariableShort(VID_SERVICE_TYPE);
236 pObject->netsrv.wProto = pMsg->GetVariableShort(VID_IP_PROTO);
237 pObject->netsrv.wPort = pMsg->GetVariableShort(VID_IP_PORT);
238 pObject->netsrv.dwPollerNode = pMsg->GetVariableLong(VID_POLLER_NODE_ID);
239 pObject->netsrv.pszRequest = pMsg->GetVariableStr(VID_SERVICE_REQUEST);
240 pObject->netsrv.pszResponce = pMsg->GetVariableStr(VID_SERVICE_RESPONCE);
241 break;
242 default:
243 break;
244 }
245
246 return pObject;
247 }
248
249
250 //
251 // Process object information received from server
252 //
253
254 void NXCL_Session::ProcessObjectUpdate(CSCPMessage *pMsg)
255 {
256 NXC_OBJECT *pObject, *pNewObject;
257
258 switch(pMsg->GetCode())
259 {
260 case CMD_OBJECT_LIST_END:
261 if (!(m_dwFlags & NXC_SF_HAS_OBJECT_CACHE))
262 {
263 LockObjectIndex();
264 qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
265 UnlockObjectIndex();
266 }
267 CompleteSync(RCC_SUCCESS);
268 break;
269 case CMD_OBJECT:
270 DebugPrintf(_T("RECV_OBJECT: ID=%d Name=\"%s\" Class=%d"), pMsg->GetVariableLong(VID_OBJECT_ID),
271 pMsg->GetVariableStr(VID_OBJECT_NAME), pMsg->GetVariableShort(VID_OBJECT_CLASS));
272
273 // Create new object from message and add it to list
274 pNewObject = NewObjectFromMsg(pMsg);
275 if (m_dwFlags & NXC_SF_HAS_OBJECT_CACHE)
276 {
277 // We already have some objects loaded from cache file
278 pObject = FindObjectById(pNewObject->dwId, TRUE);
279 if (pObject == NULL)
280 {
281 AddObject(pNewObject, TRUE);
282 }
283 else
284 {
285 ReplaceObject(pObject, pNewObject);
286 }
287 }
288 else
289 {
290 // No cache file, all objects are new
291 AddObject(pNewObject, FALSE);
292 }
293 break;
294 case CMD_OBJECT_UPDATE:
295 pNewObject = NewObjectFromMsg(pMsg);
296 pObject = FindObjectById(pNewObject->dwId, TRUE);
297 if (pObject == NULL)
298 {
299 AddObject(pNewObject, TRUE);
300 pObject = pNewObject;
301 }
302 else
303 {
304 ReplaceObject(pObject, pNewObject);
305 }
306 CallEventHandler(NXC_EVENT_OBJECT_CHANGED, pObject->dwId, pObject);
307 break;
308 default:
309 break;
310 }
311 }
312
313
314 //
315 // Synchronize objects with the server
316 // This function is NOT REENTRANT
317 //
318
319 DWORD NXCL_Session::SyncObjects(TCHAR *pszCacheFile)
320 {
321 CSCPMessage msg;
322 DWORD dwRetCode, dwRqId;
323
324 dwRqId = CreateRqId();
325 PrepareForSync();
326
327 DestroyAllObjects();
328
329 m_dwFlags &= ~NXC_SF_HAS_OBJECT_CACHE;
330 if (pszCacheFile != NULL)
331 LoadObjectsFromCache(pszCacheFile);
332
333 msg.SetCode(CMD_GET_OBJECTS);
334 msg.SetId(dwRqId);
335 msg.SetVariable(VID_TIMESTAMP, m_dwTimeStamp);
336 SendMsg(&msg);
337
338 dwRetCode = WaitForRCC(dwRqId);
339
340 // If request was successful, wait for object list end or for disconnection
341 if (dwRetCode == RCC_SUCCESS)
342 dwRetCode = WaitForSync(INFINITE);
343
344 return dwRetCode;
345 }
346
347
348 //
349 // Wrappers for NXCL_Session::SyncObjects()
350 //
351
352 DWORD LIBNXCL_EXPORTABLE NXCSyncObjects(NXC_SESSION hSession)
353 {
354 return ((NXCL_Session *)hSession)->SyncObjects(NULL);
355 }
356
357 DWORD LIBNXCL_EXPORTABLE NXCSyncObjectsEx(NXC_SESSION hSession, TCHAR *pszCacheFile)
358 {
359 return ((NXCL_Session *)hSession)->SyncObjects(pszCacheFile);
360 }
361
362
363 //
364 // Find object by ID
365 //
366
367 NXC_OBJECT *NXCL_Session::FindObjectById(DWORD dwId, BOOL bLock)
368 {
369 DWORD dwPos;
370 NXC_OBJECT *pObject;
371
372 if (bLock)
373 LockObjectIndex();
374
375 dwPos = SearchIndex(m_pIndexById, m_dwNumObjects, dwId);
376 pObject = (dwPos == INVALID_INDEX) ? NULL : m_pIndexById[dwPos].pObject;
377
378 if (bLock)
379 UnlockObjectIndex();
380
381 return pObject;
382 }
383
384 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectById(NXC_SESSION hSession, DWORD dwId)
385 {
386 return ((NXCL_Session *)hSession)->FindObjectById(dwId, TRUE);
387 }
388
389 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectByIdNoLock(NXC_SESSION hSession, DWORD dwId)
390 {
391 return ((NXCL_Session *)hSession)->FindObjectById(dwId, FALSE);
392 }
393
394
395 //
396 // Find object by name
397 //
398
399 NXC_OBJECT *NXCL_Session::FindObjectByName(TCHAR *pszName)
400 {
401 NXC_OBJECT *pObject = NULL;
402 DWORD i;
403
404 if (pszName != NULL)
405 if (*pszName != 0)
406 {
407 LockObjectIndex();
408
409 for(i = 0; i < m_dwNumObjects; i++)
410 if (MatchString(pszName, m_pIndexById[i].pObject->szName, FALSE))
411 {
412 pObject = m_pIndexById[i].pObject;
413 break;
414 }
415
416 UnlockObjectIndex();
417 }
418 return pObject;
419 }
420
421 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectByName(NXC_SESSION hSession, TCHAR *pszName)
422 {
423 return ((NXCL_Session *)hSession)->FindObjectByName(pszName);
424 }
425
426
427 //
428 // Enumerate all objects
429 //
430
431 void NXCL_Session::EnumerateObjects(BOOL (* pHandler)(NXC_OBJECT *))
432 {
433 DWORD i;
434
435 LockObjectIndex();
436 for(i = 0; i < m_dwNumObjects; i++)
437 if (!pHandler(m_pIndexById[i].pObject))
438 break;
439 UnlockObjectIndex();
440 }
441
442 void LIBNXCL_EXPORTABLE NXCEnumerateObjects(NXC_SESSION hSession, BOOL (* pHandler)(NXC_OBJECT *))
443 {
444 ((NXCL_Session *)hSession)->EnumerateObjects(pHandler);
445 }
446
447
448 //
449 // Get root object
450 //
451
452 NXC_OBJECT *NXCL_Session::GetRootObject(DWORD dwId, DWORD dwIndex)
453 {
454 if (m_dwNumObjects > dwIndex)
455 if (m_pIndexById[dwIndex].dwKey == dwId)
456 return m_pIndexById[dwIndex].pObject;
457 return NULL;
458 }
459
460
461 //
462 // Get topology root ("Entire Network") object
463 //
464
465 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCGetTopologyRootObject(NXC_SESSION hSession)
466 {
467 return ((NXCL_Session *)hSession)->GetRootObject(1, 0);
468 }
469
470
471 //
472 // Get service tree root ("All Services") object
473 //
474
475 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCGetServiceRootObject(NXC_SESSION hSession)
476 {
477 return ((NXCL_Session *)hSession)->GetRootObject(2, 1);
478 }
479
480
481 //
482 // Get template tree root ("Templates") object
483 //
484
485 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCGetTemplateRootObject(NXC_SESSION hSession)
486 {
487 return ((NXCL_Session *)hSession)->GetRootObject(3, 2);
488 }
489
490
491 //
492 // Get pointer to first object on objects' list and entire number of objects
493 //
494
495 void *NXCL_Session::GetObjectIndex(DWORD *pdwNumObjects)
496 {
497 if (pdwNumObjects != NULL)
498 *pdwNumObjects = m_dwNumObjects;
499 return m_pIndexById;
500 }
501
502 void LIBNXCL_EXPORTABLE *NXCGetObjectIndex(NXC_SESSION hSession, DWORD *pdwNumObjects)
503 {
504 return ((NXCL_Session *)hSession)->GetObjectIndex(pdwNumObjects);
505 }
506
507
508 //
509 // Lock object index
510 //
511
512 void LIBNXCL_EXPORTABLE NXCLockObjectIndex(NXC_SESSION hSession)
513 {
514 ((NXCL_Session *)hSession)->LockObjectIndex();
515 }
516
517
518 //
519 // Unlock object index
520 //
521
522 void LIBNXCL_EXPORTABLE NXCUnlockObjectIndex(NXC_SESSION hSession)
523 {
524 ((NXCL_Session *)hSession)->UnlockObjectIndex();
525 }
526
527
528 //
529 // Modify object
530 //
531
532 DWORD LIBNXCL_EXPORTABLE NXCModifyObject(NXC_SESSION hSession, NXC_OBJECT_UPDATE *pUpdate)
533 {
534 CSCPMessage msg;
535 DWORD dwRqId;
536
537 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
538
539 // Build request message
540 msg.SetCode(CMD_MODIFY_OBJECT);
541 msg.SetId(dwRqId);
542 msg.SetVariable(VID_OBJECT_ID, pUpdate->dwObjectId);
543 if (pUpdate->dwFlags & OBJ_UPDATE_NAME)
544 msg.SetVariable(VID_OBJECT_NAME, pUpdate->pszName);
545 if (pUpdate->dwFlags & OBJ_UPDATE_AGENT_PORT)
546 msg.SetVariable(VID_AGENT_PORT, (WORD)pUpdate->iAgentPort);
547 if (pUpdate->dwFlags & OBJ_UPDATE_AGENT_AUTH)
548 msg.SetVariable(VID_AUTH_METHOD, (WORD)pUpdate->iAuthType);
549 if (pUpdate->dwFlags & OBJ_UPDATE_AGENT_SECRET)
550 msg.SetVariable(VID_SHARED_SECRET, pUpdate->pszSecret);
551 if (pUpdate->dwFlags & OBJ_UPDATE_SNMP_COMMUNITY)
552 msg.SetVariable(VID_COMMUNITY_STRING, pUpdate->pszCommunity);
553 if (pUpdate->dwFlags & OBJ_UPDATE_IMAGE)
554 msg.SetVariable(VID_IMAGE_ID, pUpdate->dwImage);
555 if (pUpdate->dwFlags & OBJ_UPDATE_SNMP_VERSION)
556 msg.SetVariable(VID_SNMP_VERSION, pUpdate->wSNMPVersion);
557 if (pUpdate->dwFlags & OBJ_UPDATE_DESCRIPTION)
558 msg.SetVariable(VID_DESCRIPTION, pUpdate->pszDescription);
559 if (pUpdate->dwFlags & OBJ_UPDATE_CHECK_REQUEST)
560 msg.SetVariable(VID_SERVICE_REQUEST, pUpdate->pszRequest);
561 if (pUpdate->dwFlags & OBJ_UPDATE_CHECK_RESPONCE)
562 msg.SetVariable(VID_SERVICE_RESPONCE, pUpdate->pszResponce);
563 if (pUpdate->dwFlags & OBJ_UPDATE_IP_PROTO)
564 msg.SetVariable(VID_IP_PROTO, pUpdate->wProto);
565 if (pUpdate->dwFlags & OBJ_UPDATE_IP_PORT)
566 msg.SetVariable(VID_IP_PORT, pUpdate->wPort);
567 if (pUpdate->dwFlags & OBJ_UPDATE_SERVICE_TYPE)
568 msg.SetVariable(VID_SERVICE_TYPE, (WORD)pUpdate->iServiceType);
569 if (pUpdate->dwFlags & OBJ_UPDATE_POLLER_NODE)
570 msg.SetVariable(VID_POLLER_NODE_ID, pUpdate->dwPollerNode);
571 if (pUpdate->dwFlags & OBJ_UPDATE_IP_ADDR)
572 msg.SetVariable(VID_IP_ADDRESS, pUpdate->dwIpAddr);
573 if (pUpdate->dwFlags & OBJ_UPDATE_ACL)
574 {
575 DWORD i, dwId1, dwId2;
576
577 msg.SetVariable(VID_ACL_SIZE, pUpdate->dwAclSize);
578 msg.SetVariable(VID_INHERIT_RIGHTS, (WORD)pUpdate->bInheritRights);
579 for(i = 0, dwId1 = VID_ACL_USER_BASE, dwId2 = VID_ACL_RIGHTS_BASE;
580 i < pUpdate->dwAclSize; i++, dwId1++, dwId2++)
581 {
582 msg.SetVariable(dwId1, pUpdate->pAccessList[i].dwUserId);
583 msg.SetVariable(dwId2, pUpdate->pAccessList[i].dwAccessRights);
584 }
585 }
586
587 // Send request
588 ((NXCL_Session *)hSession)->SendMsg(&msg);
589
590 // Wait for reply
591 return ((NXCL_Session *)hSession)->WaitForRCC(dwRqId);
592 }
593
594
595 //
596 // Set object's mamagement status
597 //
598
599 DWORD LIBNXCL_EXPORTABLE NXCSetObjectMgmtStatus(NXC_SESSION hSession, DWORD dwObjectId,
600 BOOL bIsManaged)
601 {
602 CSCPMessage msg;
603 DWORD dwRqId;
604
605 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
606
607 // Build request message
608 msg.SetCode(CMD_SET_OBJECT_MGMT_STATUS);
609 msg.SetId(dwRqId);
610 msg.SetVariable(VID_OBJECT_ID, dwObjectId);
611 msg.SetVariable(VID_MGMT_STATUS, (WORD)bIsManaged);
612
613 // Send request
614 ((NXCL_Session *)hSession)->SendMsg(&msg);
615
616 // Wait for reply
617 return ((NXCL_Session *)hSession)->WaitForRCC(dwRqId);
618 }
619
620
621 //
622 // Create new object
623 //
624
625 DWORD LIBNXCL_EXPORTABLE NXCCreateObject(NXC_SESSION hSession,
626 NXC_OBJECT_CREATE_INFO *pCreateInfo,
627 DWORD *pdwObjectId)
628 {
629 CSCPMessage msg, *pResponce;
630 DWORD dwRqId, dwRetCode;
631
632 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
633
634 // Build request message
635 msg.SetCode(CMD_CREATE_OBJECT);
636 msg.SetId(dwRqId);
637 msg.SetVariable(VID_PARENT_ID, pCreateInfo->dwParentId);
638 msg.SetVariable(VID_OBJECT_CLASS, (WORD)pCreateInfo->iClass);
639 msg.SetVariable(VID_OBJECT_NAME, pCreateInfo->pszName);
640 switch(pCreateInfo->iClass)
641 {
642 case OBJECT_NODE:
643 msg.SetVariable(VID_IP_ADDRESS, pCreateInfo->cs.node.dwIpAddr);
644 msg.SetVariable(VID_IP_NETMASK, pCreateInfo->cs.node.dwNetMask);
645 break;
646 case OBJECT_CONTAINER:
647 msg.SetVariable(VID_CATEGORY, pCreateInfo->cs.container.dwCategory);
648 msg.SetVariable(VID_DESCRIPTION, pCreateInfo->cs.container.pszDescription);
649 break;
650 case OBJECT_TEMPLATEGROUP:
651 msg.SetVariable(VID_DESCRIPTION, pCreateInfo->cs.templateGroup.pszDescription);
652 break;
653 case OBJECT_NETWORKSERVICE:
654 msg.SetVariable(VID_SERVICE_TYPE, (WORD)pCreateInfo->cs.netsrv.iServiceType);
655 msg.SetVariable(VID_IP_PROTO, pCreateInfo->cs.netsrv.wProto);
656 msg.SetVariable(VID_IP_PORT, pCreateInfo->cs.netsrv.wPort);
657 msg.SetVariable(VID_SERVICE_REQUEST, pCreateInfo->cs.netsrv.pszRequest);
658 msg.SetVariable(VID_SERVICE_RESPONCE, pCreateInfo->cs.netsrv.pszResponce);
659 break;
660 default:
661 break;
662 }
663
664 // Send request
665 ((NXCL_Session *)hSession)->SendMsg(&msg);
666
667 // Wait for responce. Creating node object can include polling,
668 // which can take a minute or even more in worst cases
669 pResponce = ((NXCL_Session *)hSession)->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, 120000);
670 if (pResponce != NULL)
671 {
672 dwRetCode = pResponce->GetVariableLong(VID_RCC);
673 if (dwRetCode == RCC_SUCCESS)
674 {
675 *pdwObjectId = pResponce->GetVariableLong(VID_OBJECT_ID);
676 }
677 delete pResponce;
678 }
679 else
680 {
681 dwRetCode = RCC_TIMEOUT;
682 }
683
684 return dwRetCode;
685 }
686
687
688 //
689 // Bind/unbind objects
690 //
691
692 static DWORD ChangeObjectBinding(NXCL_Session *pSession, DWORD dwParentObject,
693 DWORD dwChildObject, BOOL bBind)
694 {
695 CSCPMessage msg;
696 DWORD dwRqId;
697
698 dwRqId = pSession->CreateRqId();
699
700 // Build request message
701 msg.SetCode(bBind ? CMD_BIND_OBJECT : CMD_UNBIND_OBJECT);
702 msg.SetId(dwRqId);
703 msg.SetVariable(VID_PARENT_ID, dwParentObject);
704 msg.SetVariable(VID_CHILD_ID, dwChildObject);
705
706 // Send request
707 pSession->SendMsg(&msg);
708
709 // Wait for reply
710 return pSession->WaitForRCC(dwRqId);
711 }
712
713
714 //
715 // Bind object
716 //
717
718 DWORD LIBNXCL_EXPORTABLE NXCBindObject(NXC_SESSION hSession, DWORD dwParentObject,
719 DWORD dwChildObject)
720 {
721 return ChangeObjectBinding((NXCL_Session *)hSession, dwParentObject, dwChildObject, TRUE);
722 }
723
724
725 //
726 // Unbind object
727 //
728
729 DWORD LIBNXCL_EXPORTABLE NXCUnbindObject(NXC_SESSION hSession, DWORD dwParentObject,
730 DWORD dwChildObject)
731 {
732 return ChangeObjectBinding((NXCL_Session *)hSession, dwParentObject, dwChildObject, FALSE);
733 }
734
735
736 //
737 // Delete object
738 //
739
740 DWORD LIBNXCL_EXPORTABLE NXCDeleteObject(NXC_SESSION hSession, DWORD dwObject)
741 {
742 CSCPMessage msg;
743 DWORD dwRqId;
744
745 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
746
747 // Build request message
748 msg.SetCode(CMD_DELETE_OBJECT);
749 msg.SetId(dwRqId);
750 msg.SetVariable(VID_OBJECT_ID, dwObject);
751
752 // Send request
753 ((NXCL_Session *)hSession)->SendMsg(&msg);
754
755 // Wait for reply
756 return ((NXCL_Session *)hSession)->WaitForRCC(dwRqId);
757 }
758
759
760 //
761 // Load container categories
762 //
763
764 DWORD LIBNXCL_EXPORTABLE NXCLoadCCList(NXC_SESSION hSession, NXC_CC_LIST **ppList)
765 {
766 CSCPMessage msg, *pResponce;
767 DWORD dwRqId, dwRetCode = RCC_SUCCESS, dwNumCats = 0, dwCatId = 0;
768
769 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
770
771 msg.SetCode(CMD_GET_CONTAINER_CAT_LIST);
772 msg.SetId(dwRqId);
773 ((NXCL_Session *)hSession)->SendMsg(&msg);
774
775 *ppList = (NXC_CC_LIST *)malloc(sizeof(NXC_CC_LIST));
776 (*ppList)->dwNumElements = 0;
777 (*ppList)->pElements = NULL;
778
779 do
780 {
781 pResponce = ((NXCL_Session *)hSession)->WaitForMessage(CMD_CONTAINER_CAT_DATA, dwRqId);
782 if (pResponce != NULL)
783 {
784 dwCatId = pResponce->GetVariableLong(VID_CATEGORY_ID);
785 if (dwCatId != 0) // 0 is end of list indicator
786 {
787 (*ppList)->pElements = (NXC_CONTAINER_CATEGORY *)realloc((*ppList)->pElements,
788 sizeof(NXC_CONTAINER_CATEGORY) * ((*ppList)->dwNumElements + 1));
789 (*ppList)->pElements[(*ppList)->dwNumElements].dwId = dwCatId;
790 (*ppList)->pElements[(*ppList)->dwNumElements].dwImageId =
791 pResponce->GetVariableLong(VID_IMAGE_ID);
792 pResponce->GetVariableStr(VID_CATEGORY_NAME,
793 (*ppList)->pElements[(*ppList)->dwNumElements].szName, MAX_OBJECT_NAME);
794 (*ppList)->pElements[(*ppList)->dwNumElements].pszDescription =
795 pResponce->GetVariableStr(VID_DESCRIPTION);
796 (*ppList)->dwNumElements++;
797 }
798 delete pResponce;
799 }
800 else
801 {
802 dwRetCode = RCC_TIMEOUT;
803 dwCatId = 0;
804 }
805 }
806 while(dwCatId != 0);
807
808 // Destroy results on failure
809 if (dwRetCode != RCC_SUCCESS)
810 {
811 safe_free((*ppList)->pElements);
812 free(*ppList);
813 *ppList = NULL;
814 }
815
816 return dwRetCode;
817 }
818
819
820 //
821 // Destroy list of container categories
822 //
823
824 void LIBNXCL_EXPORTABLE NXCDestroyCCList(NXC_CC_LIST *pList)
825 {
826 DWORD i;
827
828 if (pList == NULL)
829 return;
830
831 for(i = 0; i < pList->dwNumElements; i++)
832 safe_free(pList->pElements[i].pszDescription);
833 safe_free(pList->pElements);
834 free(pList);
835 }
836
837
838 //
839 // Perform a forced node poll
840 //
841
842 DWORD LIBNXCL_EXPORTABLE NXCPollNode(NXC_SESSION hSession, DWORD dwObjectId, int iPollType,
843 void (* pfCallback)(TCHAR *, void *), void *pArg)
844 {
845 DWORD dwRetCode, dwRqId;
846 CSCPMessage msg, *pResponce;
847 TCHAR *pszMsg;
848
849 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
850
851 msg.SetCode(CMD_POLL_NODE);
852 msg.SetId(dwRqId);
853 msg.SetVariable(VID_OBJECT_ID, dwObjectId);
854 msg.SetVariable(VID_POLL_TYPE, (WORD)iPollType);
855 ((NXCL_Session *)hSession)->SendMsg(&msg);
856
857 do
858 {
859 // Polls can take a long time, so we wait up to 120 seconds for each message
860 pResponce = ((NXCL_Session *)hSession)->WaitForMessage(CMD_POLLING_INFO, dwRqId, 120000);
861 if (pResponce != NULL)
862 {
863 dwRetCode = pResponce->GetVariableLong(VID_RCC);
864 if ((dwRetCode == RCC_OPERATION_IN_PROGRESS) && (pfCallback != NULL))
865 {
866 pszMsg = pResponce->GetVariableStr(VID_POLLER_MESSAGE);
867 pfCallback(pszMsg, pArg);
868 free(pszMsg);
869 }
870 delete pResponce;
871 }
872 else
873 {
874 dwRetCode = RCC_TIMEOUT;
875 }
876 }
877 while(dwRetCode == RCC_OPERATION_IN_PROGRESS);
878
879 return dwRetCode;
880 }
881
882
883 //
884 // Wake up node by sending magic packet
885 //
886
887 DWORD LIBNXCL_EXPORTABLE NXCWakeUpNode(NXC_SESSION hSession, DWORD dwObjectId)
888 {
889 DWORD dwRqId;
890 CSCPMessage msg;
891
892 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
893
894 msg.SetCode(CMD_WAKEUP_NODE);
895 msg.SetId(dwRqId);
896 msg.SetVariable(VID_OBJECT_ID, dwObjectId);
897 ((NXCL_Session *)hSession)->SendMsg(&msg);
898 return ((NXCL_Session *)hSession)->WaitForRCC(dwRqId);
899 }
900
901
902 //
903 // Retrieve list of supported agent parameters
904 //
905
906 DWORD LIBNXCL_EXPORTABLE NXCGetSupportedParameters(NXC_SESSION hSession, DWORD dwNodeId,
907 DWORD *pdwNumParams,
908 NXC_AGENT_PARAM **ppParamList)
909 {
910 CSCPMessage msg, *pResponce;
911 DWORD i, dwId, dwRqId, dwRetCode;
912
913 dwRqId = ((NXCL_Session *)hSession)->CreateRqId();
914
915 *pdwNumParams = 0;
916 *ppParamList = NULL;
917
918 // Build request message
919 msg.SetCode(CMD_GET_PARAMETER_LIST);
920 msg.SetId(dwRqId);
921 msg.SetVariable(VID_OBJECT_ID, dwNodeId);
922
923 // Send request
924 ((NXCL_Session *)hSession)->SendMsg(&msg);
925
926 // Wait for responce
927 pResponce = ((NXCL_Session *)hSession)->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId);
928 if (pResponce != NULL)
929 {
930 dwRetCode = pResponce->GetVariableLong(VID_RCC);
931 if (dwRetCode == RCC_SUCCESS)
932 {
933 *pdwNumParams = pResponce->GetVariableLong(VID_NUM_PARAMETERS);
934 *ppParamList = (NXC_AGENT_PARAM *)malloc(sizeof(NXC_AGENT_PARAM) * *pdwNumParams);
935 for(i = 0, dwId = VID_PARAM_LIST_BASE; i < *pdwNumParams; i++)
936 {
937 pResponce->GetVariableStr(dwId++, (*ppParamList)[i].szName, MAX_PARAM_NAME);
938 pResponce->GetVariableStr(dwId++, (*ppParamList)[i].szDescription, MAX_DB_STRING);
939 (*ppParamList)[i].iDataType = (int)pResponce->GetVariableShort(dwId++);
940 }
941 }
942 delete pResponce;
943 }
944 else
945 {
946 dwRetCode = RCC_TIMEOUT;
947 }
948
949 return dwRetCode;
950 }
951
952
953 //
954 // Check if object has default name formed from IP address
955 //
956
957 static BOOL ObjectHasDefaultName(NXC_OBJECT *pObject)
958 {
959 if (pObject->iClass == OBJECT_SUBNET)
960 {
961 TCHAR szBuffer[64], szIpAddr[32];
962 _stprintf(szBuffer, _T("%s/%d"), IpToStr(pObject->dwIpAddr, szIpAddr),
963 BitsInMask(pObject->subnet.dwIpNetMask));
964 return !_tcscmp(szBuffer, pObject->szName);
965 }
966 else
967 {
968 return ((pObject->dwIpAddr != 0) &&
969 (ntohl(_t_inet_addr(pObject->szName)) == pObject->dwIpAddr));
970 }
971 }
972
973
974 //
975 // Get object name suitable for comparision
976 //
977
978 void LIBNXCL_EXPORTABLE NXCGetComparableObjectName(NXC_SESSION hSession, DWORD dwObjectId, TCHAR *pszName)
979 {
980 NXC_OBJECT *pObject;
981
982 pObject = ((NXCL_Session *)hSession)->FindObjectById(dwObjectId, TRUE);
983 if (pObject != NULL)
984 {
985 // If object has an IP address as name, we sort as numbers
986 // otherwise in alphabetical order
987 if (ObjectHasDefaultName(pObject))
988 {
989 _sntprintf(pszName, MAX_OBJECT_NAME, _T("\x01%03d%03d%03d%03d"),
990 pObject->dwIpAddr >> 24, (pObject->dwIpAddr >> 16) & 255,
991 (pObject->dwIpAddr >> 8) & 255, pObject->dwIpAddr & 255);
992 }
993 else
994 {
995 _tcscpy(pszName, pObject->szName);
996 }
997 }
998 else
999 {
1000 *pszName = 0;
1001 }
1002 }
1003
1004
1005 //
1006 // Save object's cache to file
1007 //
1008
1009 DWORD LIBNXCL_EXPORTABLE NXCSaveObjectCache(NXC_SESSION hSession, TCHAR *pszFile)
1010 {
1011 FILE *hFile;
1012 OBJECT_CACHE_HEADER hdr;
1013 DWORD i, dwResult, dwNumObjects, dwSize;
1014 INDEX *pList;
1015
1016 hFile = _tfopen(pszFile, _T("wb"));
1017 if (hFile != NULL)
1018 {
1019 ((NXCL_Session *)hSession)->LockObjectIndex();
1020 pList = (INDEX *)((NXCL_Session *)hSession)->GetObjectIndex(&dwNumObjects);
1021
1022 // Write cache file header
1023 hdr.dwMagic = OBJECT_CACHE_MAGIC;
1024 hdr.dwStructSize = sizeof(NXC_OBJECT);
1025 hdr.dwTimeStamp = ((NXCL_Session *)hSession)->GetTimeStamp();
1026 hdr.dwNumObjects = dwNumObjects;
1027 fwrite(&hdr, 1, sizeof(OBJECT_CACHE_HEADER), hFile);
1028
1029 // Write all objects
1030 for(i = 0; i < dwNumObjects; i++)
1031 {
1032 fwrite(pList[i].pObject, 1, sizeof(NXC_OBJECT), hFile);
1033 fwrite(pList[i].pObject->pdwChildList, 1,
1034 sizeof(DWORD) * pList[i].pObject->dwNumChilds, hFile);
1035 fwrite(pList[i].pObject->pdwParentList, 1,
1036 sizeof(DWORD) * pList[i].pObject->dwNumParents, hFile);
1037 fwrite(pList[i].pObject->pAccessList, 1,
1038 sizeof(NXC_ACL_ENTRY) * pList[i].pObject->dwAclSize, hFile);
1039 switch(pList[i].pObject->iClass)
1040 {
1041 case OBJECT_NODE:
1042 dwSize = _tcslen(pList[i].pObject->node.pszDescription) * sizeof(TCHAR);
1043 fwrite(&dwSize, 1, sizeof(DWORD), hFile);
1044 fwrite(pList[i].pObject->node.pszDescription, 1, dwSize, hFile);
1045 break;
1046 case OBJECT_CONTAINER:
1047 dwSize = _tcslen(pList[i].pObject->container.pszDescription) * sizeof(TCHAR);
1048 fwrite(&dwSize, 1, sizeof(DWORD), hFile);
1049 fwrite(pList[i].pObject->container.pszDescription, 1, dwSize, hFile);
1050 break;
1051 case OBJECT_TEMPLATE:
1052 dwSize = _tcslen(pList[i].pObject->dct.pszDescription) * sizeof(TCHAR);
1053 fwrite(&dwSize, 1, sizeof(DWORD), hFile);
1054 fwrite(pList[i].pObject->dct.pszDescription, 1, dwSize, hFile);
1055 break;
1056 case OBJECT_NETWORKSERVICE:
1057 dwSize = _tcslen(pList[i].pObject->netsrv.pszRequest) * sizeof(TCHAR);
1058 fwrite(&dwSize, 1, sizeof(DWORD), hFile);
1059 fwrite(pList[i].pObject->netsrv.pszRequest, 1, dwSize, hFile);
1060
1061 dwSize = _tcslen(pList[i].pObject->netsrv.pszResponce) * sizeof(TCHAR);
1062 fwrite(&dwSize, 1, sizeof(DWORD), hFile);
1063 fwrite(pList[i].pObject->netsrv.pszResponce, 1, dwSize, hFile);
1064 break;
1065 default:
1066 break;
1067 }
1068 }
1069
1070 ((NXCL_Session *)hSession)->UnlockObjectIndex();
1071 fclose(hFile);
1072 dwResult = RCC_SUCCESS;
1073 }
1074 else
1075 {
1076 dwResult = RCC_IO_ERROR;
1077 }
1078
1079 return dwResult;
1080 }
1081
1082
1083 //
1084 // Load objects from cache file
1085 //
1086
1087 void NXCL_Session::LoadObjectsFromCache(TCHAR *pszFile)
1088 {
1089 FILE *hFile;
1090 OBJECT_CACHE_HEADER hdr;
1091 NXC_OBJECT object;
1092 DWORD i, dwSize;
1093
1094 hFile = _tfopen(pszFile, _T("rb"));
1095 if (hFile != NULL)
1096 {
1097 // Read header
1098 if (fread(&hdr, 1, sizeof(OBJECT_CACHE_HEADER), hFile) == sizeof(OBJECT_CACHE_HEADER))
1099 {
1100 if ((hdr.dwMagic == OBJECT_CACHE_MAGIC) &&
1101 (hdr.dwStructSize == sizeof(NXC_OBJECT)))
1102 {
1103 m_dwTimeStamp = hdr.dwTimeStamp;
1104 for(i = 0; i < hdr.dwNumObjects; i++)
1105 {
1106 if (fread(&object, 1, sizeof(NXC_OBJECT), hFile) == sizeof(NXC_OBJECT))
1107 {
1108 object.pdwChildList = (DWORD *)malloc(sizeof(DWORD) * object.dwNumChilds);
1109 fread(object.pdwChildList, 1, sizeof(DWORD) * object.dwNumChilds, hFile);
1110
1111 object.pdwParentList = (DWORD *)malloc(sizeof(DWORD) * object.dwNumParents);
1112 fread(object.pdwParentList, 1, sizeof(DWORD) * object.dwNumParents, hFile);
1113
1114 object.pAccessList = (NXC_ACL_ENTRY *)malloc(sizeof(NXC_ACL_ENTRY) * object.dwAclSize);
1115 fread(object.pAccessList, 1, sizeof(NXC_ACL_ENTRY) * object.dwAclSize, hFile);
1116
1117 switch(object.iClass)
1118 {
1119 case OBJECT_NODE:
1120 fread(&dwSize, 1, sizeof(DWORD), hFile);
1121 object.node.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
1122 fread(object.node.pszDescription, 1, dwSize, hFile);
1123 object.node.pszDescription[dwSize / sizeof(TCHAR)] = 0;
1124 break;
1125 case OBJECT_CONTAINER:
1126 fread(&dwSize, 1, sizeof(DWORD), hFile);
1127 object.container.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
1128 fread(object.container.pszDescription, 1, dwSize, hFile);
1129 object.container.pszDescription[dwSize / sizeof(TCHAR)] = 0;
1130 break;
1131 case OBJECT_TEMPLATE:
1132 fread(&dwSize, 1, sizeof(DWORD), hFile);
1133 object.dct.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
1134 fread(object.dct.pszDescription, 1, dwSize, hFile);
1135 object.dct.pszDescription[dwSize / sizeof(TCHAR)] = 0;
1136 break;
1137 case OBJECT_NETWORKSERVICE:
1138 fread(&dwSize, 1, sizeof(DWORD), hFile);
1139 object.netsrv.pszRequest = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
1140 fread(object.netsrv.pszRequest, 1, dwSize, hFile);
1141 object.netsrv.pszRequest[dwSize / sizeof(TCHAR)] = 0;
1142
1143 fread(&dwSize, 1, sizeof(DWORD), hFile);
1144 object.netsrv.pszResponce = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
1145 fread(object.netsrv.pszResponce, 1, dwSize, hFile);
1146 object.netsrv.pszResponce[dwSize / sizeof(TCHAR)] = 0;
1147 break;
1148 default:
1149 break;
1150 }
1151
1152 AddObject((NXC_OBJECT *)nx_memdup(&object, sizeof(NXC_OBJECT)), FALSE);
1153 }
1154 }
1155 LockObjectIndex();
1156 qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
1157 UnlockObjectIndex();
1158 m_dwFlags |= NXC_SF_HAS_OBJECT_CACHE;
1159 }
1160 }
1161
1162 fclose(hFile);
1163 }
1164 }