object save optimization - object properties divided into groups and anly modified...
[public/netxms.git] / src / server / core / netobj.cpp
1 /*
2 ** NetXMS - Network Management System
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: netobj.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Class names
27 */
28 static const TCHAR *s_className[]=
29 {
30 _T("Generic"), _T("Subnet"), _T("Node"), _T("Interface"),
31 _T("Network"), _T("Container"), _T("Zone"), _T("ServiceRoot"),
32 _T("Template"), _T("TemplateGroup"), _T("TemplateRoot"),
33 _T("NetworkService"), _T("VPNConnector"), _T("Condition"),
34 _T("Cluster"), _T("PolicyGroup"), _T("PolicyRoot"),
35 _T("AgentPolicy"), _T("AgentPolicyConfig"), _T("NetworkMapRoot"),
36 _T("NetworkMapGroup"), _T("NetworkMap"), _T("DashboardRoot"),
37 _T("Dashboard"), _T("ReportRoot"), _T("ReportGroup"), _T("Report"),
38 _T("BusinessServiceRoot"), _T("BusinessService"), _T("NodeLink"),
39 _T("ServiceCheck"), _T("MobileDevice"), _T("Rack"), _T("AccessPoint"),
40 _T("AgentPolicyLogParser"), _T("Chassis"), _T("DashboardGroup"),
41 _T("Sensor")
42 };
43
44 /**
45 * Default constructor
46 */
47 NetObj::NetObj()
48 {
49 int i;
50
51 m_id = 0;
52 m_dwRefCount = 0;
53 m_mutexProperties = MutexCreate();
54 m_mutexRefCount = MutexCreate();
55 m_mutexACL = MutexCreate();
56 m_rwlockParentList = RWLockCreate();
57 m_rwlockChildList = RWLockCreate();
58 m_status = STATUS_UNKNOWN;
59 m_name[0] = 0;
60 m_comments = NULL;
61 m_modified = 0;
62 m_isDeleted = false;
63 m_isHidden = false;
64 m_isSystem = false;
65 m_maintenanceMode = false;
66 m_maintenanceEventId = 0;
67 m_childList = new ObjectArray<NetObj>(0, 16, false);
68 m_parentList = new ObjectArray<NetObj>(4, 4, false);
69 m_accessList = new AccessList();
70 m_inheritAccessRights = true;
71 m_trustedNodes = NULL;
72 m_pollRequestor = NULL;
73 m_statusCalcAlg = SA_CALCULATE_DEFAULT;
74 m_statusPropAlg = SA_PROPAGATE_DEFAULT;
75 m_fixedStatus = STATUS_WARNING;
76 m_statusShift = 0;
77 m_statusSingleThreshold = 75;
78 m_timestamp = 0;
79 for(i = 0; i < 4; i++)
80 {
81 m_statusTranslation[i] = i + 1;
82 m_statusThresholds[i] = 80 - i * 20;
83 }
84 m_submapId = 0;
85 m_moduleData = NULL;
86 m_postalAddress = new PostalAddress();
87 m_dashboards = new IntegerArray<UINT32>();
88 m_urls = new ObjectArray<ObjectUrl>(4, 4, true);
89 m_state = 0;
90 m_runtimeFlags = 0;
91 m_flags = 0;
92 }
93
94 /**
95 * Destructor
96 */
97 NetObj::~NetObj()
98 {
99 MutexDestroy(m_mutexProperties);
100 MutexDestroy(m_mutexRefCount);
101 MutexDestroy(m_mutexACL);
102 RWLockDestroy(m_rwlockParentList);
103 RWLockDestroy(m_rwlockChildList);
104 delete m_childList;
105 delete m_parentList;
106 delete m_accessList;
107 delete m_trustedNodes;
108 free(m_comments);
109 delete m_moduleData;
110 delete m_postalAddress;
111 delete m_dashboards;
112 delete m_urls;
113 }
114
115 /**
116 * Get class name for this object
117 */
118 const TCHAR *NetObj::getObjectClassName() const
119 {
120 return getObjectClassName(getObjectClass());
121 }
122
123 /**
124 * Get class name for given class ID
125 */
126 const TCHAR *NetObj::getObjectClassName(int objectClass)
127 {
128 return ((objectClass >= 0) && (objectClass < sizeof(s_className) / sizeof(const TCHAR *))) ? s_className[objectClass] : _T("Custom");
129 }
130
131 /**
132 * Create object from database data
133 */
134 bool NetObj::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
135 {
136 return false; // Abstract objects cannot be loaded from database
137 }
138
139 /**
140 * Link related objects after loading from database
141 */
142 void NetObj::linkObjects()
143 {
144 }
145
146 /**
147 * Save object to database
148 */
149 bool NetObj::saveToDatabase(DB_HANDLE hdb)
150 {
151 return false; // Abstract objects cannot be saved to database
152 }
153
154 /**
155 * Parameters for DeleteModuleDataCallback and SaveModuleDataCallback
156 */
157 struct ModuleDataDatabaseCallbackParams
158 {
159 UINT32 id;
160 DB_HANDLE hdb;
161 };
162
163 /**
164 * Callback for deleting module data from database
165 */
166 static EnumerationCallbackResult SaveModuleRuntimeDataCallback(const TCHAR *key, const void *value, void *data)
167 {
168 return ((ModuleData *)value)->saveRuntimeData(((ModuleDataDatabaseCallbackParams *)data)->hdb, ((ModuleDataDatabaseCallbackParams *)data)->id) ? _CONTINUE : _STOP;
169 }
170
171 /**
172 * Save runtime data to database. Called only on server shutdown to save
173 * less important but frequently changing runtime data when it is not feasible
174 * to mark object as modified on each change of such data.
175 */
176 bool NetObj::saveRuntimeData(DB_HANDLE hdb)
177 {
178 DB_STATEMENT hStmt = DBPrepare(hdb, _T("UPDATE object_properties SET status=? WHERE object_id=?"));
179 if (hStmt == NULL)
180 return false;
181
182 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_status);
183 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_id);
184 bool success = DBExecute(hStmt);
185 DBFreeStatement(hStmt);
186
187 // Save module data
188 if (success && (m_moduleData != NULL))
189 {
190 ModuleDataDatabaseCallbackParams data;
191 data.id = m_id;
192 data.hdb = hdb;
193 success = (m_moduleData->forEach(SaveModuleRuntimeDataCallback, &data) == _CONTINUE);
194 }
195
196 return success;
197 }
198
199 /**
200 * Callback for deleting module data from database
201 */
202 static EnumerationCallbackResult DeleteModuleDataCallback(const TCHAR *key, const void *value, void *data)
203 {
204 return ((ModuleData *)value)->deleteFromDatabase(((ModuleDataDatabaseCallbackParams *)data)->hdb, ((ModuleDataDatabaseCallbackParams *)data)->id) ? _CONTINUE : _STOP;
205 }
206
207 /**
208 * Delete object from database
209 */
210 bool NetObj::deleteFromDatabase(DB_HANDLE hdb)
211 {
212 // Delete ACL
213 bool success = executeQueryOnObject(hdb, _T("DELETE FROM acl WHERE object_id=?"));
214 if (success)
215 success = executeQueryOnObject(hdb, _T("DELETE FROM object_properties WHERE object_id=?"));
216 if (success)
217 success = executeQueryOnObject(hdb, _T("DELETE FROM object_custom_attributes WHERE object_id=?"));
218 if (success)
219 success = executeQueryOnObject(hdb, _T("DELETE FROM object_urls WHERE object_id=?"));
220
221 // Delete events
222 if (success && ConfigReadInt(_T("DeleteEventsOfDeletedObject"), 1))
223 {
224 success = executeQueryOnObject(hdb, _T("DELETE FROM event_log WHERE event_source=?"));
225 }
226
227 // Delete alarms
228 if (success && ConfigReadInt(_T("DeleteAlarmsOfDeletedObject"), 1))
229 {
230 success = DeleteObjectAlarms(m_id, hdb);
231 }
232
233 if (success && isLocationTableExists(hdb))
234 {
235 TCHAR query[256];
236 _sntprintf(query, 256, _T("DROP TABLE gps_history_%d"), m_id);
237 success = DBQuery(hdb, query);
238 }
239
240 // Delete module data
241 if (success && (m_moduleData != NULL))
242 {
243 ModuleDataDatabaseCallbackParams data;
244 data.id = m_id;
245 data.hdb = hdb;
246 success = (m_moduleData->forEach(DeleteModuleDataCallback, &data) == _CONTINUE);
247 }
248
249 return success;
250 }
251
252 /**
253 * Load common object properties from database
254 */
255 bool NetObj::loadCommonProperties(DB_HANDLE hdb)
256 {
257 bool success = false;
258
259 // Load access options
260 DB_STATEMENT hStmt = DBPrepare(hdb,
261 _T("SELECT name,status,is_deleted,")
262 _T("inherit_access_rights,last_modified,status_calc_alg,")
263 _T("status_prop_alg,status_fixed_val,status_shift,")
264 _T("status_translation,status_single_threshold,")
265 _T("status_thresholds,comments,is_system,")
266 _T("location_type,latitude,longitude,location_accuracy,")
267 _T("location_timestamp,guid,image,submap_id,country,city,")
268 _T("street_address,postcode,maint_mode,maint_event_id,state,flags FROM object_properties ")
269 _T("WHERE object_id=?"));
270 if (hStmt != NULL)
271 {
272 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
273 DB_RESULT hResult = DBSelectPrepared(hStmt);
274 if (hResult != NULL)
275 {
276 if (DBGetNumRows(hResult) > 0)
277 {
278 DBGetField(hResult, 0, 0, m_name, MAX_OBJECT_NAME);
279 m_status = DBGetFieldLong(hResult, 0, 1);
280 m_isDeleted = DBGetFieldLong(hResult, 0, 2) ? true : false;
281 m_inheritAccessRights = DBGetFieldLong(hResult, 0, 3) ? true : false;
282 m_timestamp = (time_t)DBGetFieldULong(hResult, 0, 4);
283 m_statusCalcAlg = DBGetFieldLong(hResult, 0, 5);
284 m_statusPropAlg = DBGetFieldLong(hResult, 0, 6);
285 m_fixedStatus = DBGetFieldLong(hResult, 0, 7);
286 m_statusShift = DBGetFieldLong(hResult, 0, 8);
287 DBGetFieldByteArray(hResult, 0, 9, m_statusTranslation, 4, STATUS_WARNING);
288 m_statusSingleThreshold = DBGetFieldLong(hResult, 0, 10);
289 DBGetFieldByteArray(hResult, 0, 11, m_statusThresholds, 4, 50);
290 safe_free(m_comments);
291 m_comments = DBGetField(hResult, 0, 12, NULL, 0);
292 m_isSystem = DBGetFieldLong(hResult, 0, 13) ? true : false;
293
294 int locType = DBGetFieldLong(hResult, 0, 14);
295 if (locType != GL_UNSET)
296 {
297 TCHAR lat[32], lon[32];
298
299 DBGetField(hResult, 0, 15, lat, 32);
300 DBGetField(hResult, 0, 16, lon, 32);
301 m_geoLocation = GeoLocation(locType, lat, lon, DBGetFieldLong(hResult, 0, 17), DBGetFieldULong(hResult, 0, 18));
302 }
303 else
304 {
305 m_geoLocation = GeoLocation();
306 }
307
308 m_guid = DBGetFieldGUID(hResult, 0, 19);
309 m_image = DBGetFieldGUID(hResult, 0, 20);
310 m_submapId = DBGetFieldULong(hResult, 0, 21);
311
312 TCHAR country[64], city[64], streetAddress[256], postcode[32];
313 DBGetField(hResult, 0, 22, country, 64);
314 DBGetField(hResult, 0, 23, city, 64);
315 DBGetField(hResult, 0, 24, streetAddress, 256);
316 DBGetField(hResult, 0, 25, postcode, 32);
317 delete m_postalAddress;
318 m_postalAddress = new PostalAddress(country, city, streetAddress, postcode);
319
320 m_maintenanceMode = DBGetFieldLong(hResult, 0, 26) ? true : false;
321 m_maintenanceEventId = DBGetFieldUInt64(hResult, 0, 27);
322
323 m_state = DBGetFieldULong(hResult, 0, 28);
324 m_runtimeFlags = 0;
325 m_flags = DBGetFieldULong(hResult, 0, 29);
326
327 success = true;
328 }
329 DBFreeResult(hResult);
330 }
331 DBFreeStatement(hStmt);
332 }
333
334 // Load custom attributes
335 if (success)
336 {
337 hStmt = DBPrepare(hdb, _T("SELECT attr_name,attr_value FROM object_custom_attributes WHERE object_id=?"));
338 if (hStmt != NULL)
339 {
340 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
341 DB_RESULT hResult = DBSelectPrepared(hStmt);
342 if (hResult != NULL)
343 {
344 int i, count;
345 TCHAR *name, *value;
346
347 count = DBGetNumRows(hResult);
348 for(i = 0; i < count; i++)
349 {
350 name = DBGetField(hResult, i, 0, NULL, 0);
351 if (name != NULL)
352 {
353 value = DBGetField(hResult, i, 1, NULL, 0);
354 if (value != NULL)
355 {
356 m_customAttributes.setPreallocated(name, value);
357 }
358 }
359 }
360 DBFreeResult(hResult);
361 }
362 else
363 {
364 success = false;
365 }
366 DBFreeStatement(hStmt);
367 }
368 else
369 {
370 success = false;
371 }
372 }
373
374 // Load associated dashboards
375 if (success)
376 {
377 hStmt = DBPrepare(hdb, _T("SELECT dashboard_id FROM dashboard_associations WHERE object_id=?"));
378 if (hStmt != NULL)
379 {
380 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
381 DB_RESULT hResult = DBSelectPrepared(hStmt);
382 if (hResult != NULL)
383 {
384 int count = DBGetNumRows(hResult);
385 for(int i = 0; i < count; i++)
386 {
387 m_dashboards->add(DBGetFieldULong(hResult, i, 0));
388 }
389 DBFreeResult(hResult);
390 }
391 else
392 {
393 success = false;
394 }
395 DBFreeStatement(hStmt);
396 }
397 else
398 {
399 success = false;
400 }
401 }
402
403 // Load associated URLs
404 if (success)
405 {
406 hStmt = DBPrepare(hdb, _T("SELECT url_id,url,description FROM object_urls WHERE object_id=?"));
407 if (hStmt != NULL)
408 {
409 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
410 DB_RESULT hResult = DBSelectPrepared(hStmt);
411 if (hResult != NULL)
412 {
413 int count = DBGetNumRows(hResult);
414 for(int i = 0; i < count; i++)
415 {
416 m_urls->add(new ObjectUrl(hResult, i));
417 }
418 DBFreeResult(hResult);
419 }
420 else
421 {
422 success = false;
423 }
424 DBFreeStatement(hStmt);
425 }
426 else
427 {
428 success = false;
429 }
430 }
431
432 if (success)
433 success = loadTrustedNodes(hdb);
434
435 if (!success)
436 DbgPrintf(4, _T("NetObj::loadCommonProperties() failed for object %s [%ld] class=%d"), m_name, (long)m_id, getObjectClass());
437
438 return success;
439 }
440
441 /**
442 * Callback for saving custom attribute in database
443 */
444 static EnumerationCallbackResult SaveAttributeCallback(const TCHAR *key, const void *value, void *data)
445 {
446 DB_STATEMENT hStmt = (DB_STATEMENT)data;
447 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, key, DB_BIND_STATIC);
448 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, (const TCHAR *)value, DB_BIND_STATIC);
449 return DBExecute(hStmt) ? _CONTINUE : _STOP;
450 }
451
452 /**
453 * Callback for saving module data in database
454 */
455 static EnumerationCallbackResult SaveModuleDataCallback(const TCHAR *key, const void *value, void *data)
456 {
457 return ((ModuleData *)value)->saveToDatabase(((ModuleDataDatabaseCallbackParams *)data)->hdb, ((ModuleDataDatabaseCallbackParams *)data)->id) ? _CONTINUE : _STOP;
458 }
459
460 /**
461 * Save common object properties to database
462 */
463 bool NetObj::saveCommonProperties(DB_HANDLE hdb)
464 {
465 if (!(m_modified & MODIFY_COMMON_PROPERTIES))
466 return true;
467
468 DB_STATEMENT hStmt;
469 if (IsDatabaseRecordExist(hdb, _T("object_properties"), _T("object_id"), m_id))
470 {
471 hStmt = DBPrepare(hdb,
472 _T("UPDATE object_properties SET name=?,status=?,")
473 _T("is_deleted=?,inherit_access_rights=?,")
474 _T("last_modified=?,status_calc_alg=?,status_prop_alg=?,")
475 _T("status_fixed_val=?,status_shift=?,status_translation=?,")
476 _T("status_single_threshold=?,status_thresholds=?,")
477 _T("comments=?,is_system=?,location_type=?,latitude=?,")
478 _T("longitude=?,location_accuracy=?,location_timestamp=?,")
479 _T("guid=?,image=?,submap_id=?,country=?,city=?,")
480 _T("street_address=?,postcode=?,maint_mode=?,maint_event_id=?,state=?,flags=? WHERE object_id=?"));
481 }
482 else
483 {
484 hStmt = DBPrepare(hdb,
485 _T("INSERT INTO object_properties (name,status,is_deleted,")
486 _T("inherit_access_rights,last_modified,status_calc_alg,")
487 _T("status_prop_alg,status_fixed_val,status_shift,status_translation,")
488 _T("status_single_threshold,status_thresholds,comments,is_system,")
489 _T("location_type,latitude,longitude,location_accuracy,location_timestamp,")
490 _T("guid,image,submap_id,country,city,street_address,postcode,maint_mode,")
491 _T("maint_event_id,state,flags,object_id) ")
492 _T("VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
493 }
494 if (hStmt == NULL)
495 return false;
496
497 TCHAR szTranslation[16], szThresholds[16], lat[32], lon[32];
498 for(int i = 0, j = 0; i < 4; i++, j += 2)
499 {
500 _sntprintf(&szTranslation[j], 16 - j, _T("%02X"), (BYTE)m_statusTranslation[i]);
501 _sntprintf(&szThresholds[j], 16 - j, _T("%02X"), (BYTE)m_statusThresholds[i]);
502 }
503 _sntprintf(lat, 32, _T("%f"), m_geoLocation.getLatitude());
504 _sntprintf(lon, 32, _T("%f"), m_geoLocation.getLongitude());
505
506 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
507 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (LONG)m_status);
508 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)(m_isDeleted ? 1 : 0));
509 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (LONG)(m_inheritAccessRights ? 1 : 0));
510 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (LONG)m_timestamp);
511 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (LONG)m_statusCalcAlg);
512 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (LONG)m_statusPropAlg);
513 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (LONG)m_fixedStatus);
514 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, (LONG)m_statusShift);
515 DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, szTranslation, DB_BIND_STATIC);
516 DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, (LONG)m_statusSingleThreshold);
517 DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, szThresholds, DB_BIND_STATIC);
518 DBBind(hStmt, 13, DB_SQLTYPE_VARCHAR, m_comments, DB_BIND_STATIC);
519 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (LONG)(m_isSystem ? 1 : 0));
520 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getType());
521 DBBind(hStmt, 16, DB_SQLTYPE_VARCHAR, lat, DB_BIND_STATIC);
522 DBBind(hStmt, 17, DB_SQLTYPE_VARCHAR, lon, DB_BIND_STATIC);
523 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getAccuracy());
524 DBBind(hStmt, 19, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
525 DBBind(hStmt, 20, DB_SQLTYPE_VARCHAR, m_guid);
526 DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_image);
527 DBBind(hStmt, 22, DB_SQLTYPE_INTEGER, m_submapId);
528 DBBind(hStmt, 23, DB_SQLTYPE_VARCHAR, m_postalAddress->getCountry(), DB_BIND_STATIC);
529 DBBind(hStmt, 24, DB_SQLTYPE_VARCHAR, m_postalAddress->getCity(), DB_BIND_STATIC);
530 DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_postalAddress->getStreetAddress(), DB_BIND_STATIC);
531 DBBind(hStmt, 26, DB_SQLTYPE_VARCHAR, m_postalAddress->getPostCode(), DB_BIND_STATIC);
532 DBBind(hStmt, 27, DB_SQLTYPE_VARCHAR, m_maintenanceMode ? _T("1") : _T("0"), DB_BIND_STATIC);
533 DBBind(hStmt, 28, DB_SQLTYPE_BIGINT, m_maintenanceEventId);
534 DBBind(hStmt, 29, DB_SQLTYPE_INTEGER, m_state);
535 DBBind(hStmt, 30, DB_SQLTYPE_INTEGER, m_flags);
536 DBBind(hStmt, 31, DB_SQLTYPE_INTEGER, m_id);
537
538 bool success = DBExecute(hStmt);
539 DBFreeStatement(hStmt);
540
541 // Save custom attributes
542 if (success)
543 {
544 TCHAR szQuery[512];
545 _sntprintf(szQuery, 512, _T("DELETE FROM object_custom_attributes WHERE object_id=%d"), m_id);
546 success = DBQuery(hdb, szQuery);
547 if (success)
548 {
549 hStmt = DBPrepare(hdb, _T("INSERT INTO object_custom_attributes (object_id,attr_name,attr_value) VALUES (?,?,?)"));
550 if (hStmt != NULL)
551 {
552 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
553 success = (m_customAttributes.forEach(SaveAttributeCallback, hStmt) == _CONTINUE);
554 DBFreeStatement(hStmt);
555 }
556 else
557 {
558 success = false;
559 }
560 }
561 }
562
563 // Save dashboard associations
564 if (success)
565 {
566 success = ExecuteQueryOnObject(hdb, m_id, _T("DELETE FROM dashboard_associations WHERE object_id=?"));
567 if (success && (m_dashboards->size() > 0))
568 {
569 hStmt = DBPrepare(hdb, _T("INSERT INTO dashboard_associations (object_id,dashboard_id) VALUES (?,?)"));
570 if (hStmt != NULL)
571 {
572 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
573 for(int i = 0; (i < m_dashboards->size()) && success; i++)
574 {
575 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dashboards->get(i));
576 success = DBExecute(hStmt);
577 }
578 DBFreeStatement(hStmt);
579 }
580 else
581 {
582 success = false;
583 }
584 }
585 }
586
587 // Save URL associations
588 if (success)
589 {
590 success = ExecuteQueryOnObject(hdb, m_id, _T("DELETE FROM object_urls WHERE object_id=?"));
591 if (success && (m_urls->size() > 0))
592 {
593 hStmt = DBPrepare(hdb, _T("INSERT INTO object_urls (object_id,url_id,url,description) VALUES (?,?,?,?)"));
594 if (hStmt != NULL)
595 {
596 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
597 for(int i = 0; (i < m_urls->size()) && success; i++)
598 {
599 const ObjectUrl *url = m_urls->get(i);
600 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, url->getId());
601 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, url->getUrl(), DB_BIND_STATIC);
602 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, url->getDescription(), DB_BIND_STATIC);
603 success = DBExecute(hStmt);
604 }
605 DBFreeStatement(hStmt);
606 }
607 else
608 {
609 success = false;
610 }
611 }
612 }
613
614 // Save module data
615 if (success && (m_moduleData != NULL))
616 {
617 ModuleDataDatabaseCallbackParams data;
618 data.id = m_id;
619 data.hdb = hdb;
620 success = (m_moduleData->forEach(SaveModuleDataCallback, &data) == _CONTINUE);
621 }
622
623 if (success)
624 success = saveTrustedNodes(hdb);
625
626 return success;
627 }
628
629 /**
630 * Add reference to the new child object
631 */
632 void NetObj::addChild(NetObj *object)
633 {
634 lockChildList(true);
635 if (m_childList->contains(object))
636 {
637 unlockChildList();
638 return; // Already in the child list
639 }
640 m_childList->add(object);
641 unlockChildList();
642 incRefCount();
643 setModified(MODIFY_RELATIONS);
644 DbgPrintf(7, _T("NetObj::addChild: this=%s [%d]; object=%s [%d]"), m_name, m_id, object->m_name, object->m_id);
645 }
646
647 /**
648 * Add reference to parent object
649 */
650 void NetObj::addParent(NetObj *object)
651 {
652 lockParentList(true);
653 if (m_parentList->contains(object))
654 {
655 unlockParentList();
656 return; // Already in the parents list
657 }
658 m_parentList->add(object);
659 unlockParentList();
660 incRefCount();
661 setModified(MODIFY_RELATIONS);
662 DbgPrintf(7, _T("NetObj::addParent: this=%s [%d]; object=%s [%d]"), m_name, m_id, object->m_name, object->m_id);
663 }
664
665 /**
666 * Delete reference to child object
667 */
668 void NetObj::deleteChild(NetObj *object)
669 {
670 int i;
671
672 lockChildList(true);
673 for(i = 0; i < m_childList->size(); i++)
674 if (m_childList->get(i) == object)
675 break;
676
677 if (i == m_childList->size()) // No such object
678 {
679 unlockChildList();
680 return;
681 }
682
683 DbgPrintf(7, _T("NetObj::deleteChild: this=%s [%d]; object=%s [%d]"), m_name, m_id, object->m_name, object->m_id);
684 m_childList->remove(i);
685 unlockChildList();
686 decRefCount();
687 setModified(MODIFY_RELATIONS);
688 }
689
690 /**
691 * Delete reference to parent object
692 */
693 void NetObj::deleteParent(NetObj *object)
694 {
695 int i;
696
697 lockParentList(true);
698 for(i = 0; i < m_parentList->size(); i++)
699 if (m_parentList->get(i) == object)
700 break;
701 if (i == m_parentList->size()) // No such object
702 {
703 unlockParentList();
704 return;
705 }
706
707 DbgPrintf(7, _T("NetObj::deleteParent: this=%s [%d]; object=%s [%d]"), m_name, m_id, object->m_name, object->m_id);
708
709 m_parentList->remove(i);
710 unlockParentList();
711 decRefCount();
712 setModified(MODIFY_RELATIONS);
713 }
714
715 /**
716 * Walker callback to call OnObjectDelete for each active object
717 */
718 void NetObj::onObjectDeleteCallback(NetObj *object, void *data)
719 {
720 UINT32 currId = ((NetObj *)data)->getId();
721 if ((object->getId() != currId) && !object->isDeleted())
722 object->onObjectDelete(currId);
723 }
724
725 /**
726 * Prepare object for deletion - remove all references, etc.
727 *
728 * @param initiator pointer to parent object which causes recursive deletion or NULL
729 */
730 void NetObj::deleteObject(NetObj *initiator)
731 {
732 DbgPrintf(4, _T("Deleting object %d [%s]"), m_id, m_name);
733
734 // Prevent object change propagation until it's marked as deleted
735 // (to prevent the object's incorrect appearance in GUI
736 lockProperties();
737 m_isHidden = true;
738 unlockProperties();
739
740 // Notify modules about object deletion
741 CALL_ALL_MODULES(pfPreObjectDelete, (this));
742
743 prepareForDeletion();
744
745 DbgPrintf(5, _T("NetObj::deleteObject(): deleting object %d from indexes"), m_id);
746 NetObjDeleteFromIndexes(this);
747
748 // Delete references to this object from child objects
749 DbgPrintf(5, _T("NetObj::deleteObject(): clearing child list for object %d"), m_id);
750 ObjectArray<NetObj> *deleteList = NULL;
751 lockChildList(true);
752 for(int i = 0; i < m_childList->size(); i++)
753 {
754 NetObj *o = m_childList->get(i);
755 if (o->getParentCount() == 1)
756 {
757 // last parent, delete object
758 if (deleteList == NULL)
759 deleteList = new ObjectArray<NetObj>(16, 16, false);
760 deleteList->add(o);
761 }
762 else
763 {
764 o->deleteParent(this);
765 }
766 decRefCount();
767 }
768 m_childList->clear();
769 unlockChildList();
770
771 // Remove references to this object from parent objects
772 DbgPrintf(5, _T("NetObj::Delete(): clearing parent list for object %d"), m_id);
773 lockParentList(true);
774 for(int i = 0; i < m_parentList->size(); i++)
775 {
776 // If parent is deletion initiator then this object already
777 // removed from parent's list
778 NetObj *obj = m_parentList->get(i);
779 if (obj != initiator)
780 {
781 obj->deleteChild(this);
782 if ((obj->getObjectClass() == OBJECT_SUBNET) && (g_flags & AF_DELETE_EMPTY_SUBNETS) && (obj->getChildCount() == 0))
783 {
784 if (deleteList == NULL)
785 deleteList = new ObjectArray<NetObj>(16, 16, false);
786 deleteList->add(obj);
787 }
788 else
789 obj->calculateCompoundStatus();
790 }
791 decRefCount();
792 }
793 m_parentList->clear();
794 unlockParentList();
795
796 // Delete orphaned child objects and empty subnets
797 if (deleteList != NULL)
798 {
799 for(int i = 0; i < deleteList->size(); i++)
800 {
801 NetObj *obj = deleteList->get(i);
802 DbgPrintf(5, _T("NetObj::deleteObject(): calling deleteObject() on %s [%d]"), obj->getName(), obj->getId());
803 obj->deleteObject(this);
804 }
805 delete deleteList;
806 }
807
808 lockProperties();
809 m_isHidden = false;
810 m_isDeleted = true;
811 setModified(MODIFY_ALL);
812 unlockProperties();
813
814 // Notify all other objects about object deletion
815 DbgPrintf(5, _T("NetObj::deleteObject(): calling onObjectDelete(%d)"), m_id);
816 g_idxObjectById.forEach(onObjectDeleteCallback, this);
817
818 DbgPrintf(4, _T("Object %d successfully deleted"), m_id);
819 }
820
821 /**
822 * Default handler for object deletion notification
823 */
824 void NetObj::onObjectDelete(UINT32 dwObjectId)
825 {
826 }
827
828 /**
829 * Get childs IDs in printable form
830 */
831 const TCHAR *NetObj::dbgGetChildList(TCHAR *szBuffer)
832 {
833 TCHAR *pBuf = szBuffer;
834 *pBuf = 0;
835 lockChildList(false);
836 for(int i = 0; i < m_childList->size(); i++)
837 {
838 _sntprintf(pBuf, 10, _T("%d "), m_childList->get(i)->getId());
839 while(*pBuf)
840 pBuf++;
841 }
842 unlockChildList();
843 if (pBuf != szBuffer)
844 *(pBuf - 1) = 0;
845 return szBuffer;
846 }
847
848 /**
849 * Get parents IDs in printable form
850 */
851 const TCHAR *NetObj::dbgGetParentList(TCHAR *szBuffer)
852 {
853 TCHAR *pBuf = szBuffer;
854 *pBuf = 0;
855 lockParentList(false);
856 for(int i = 0; i < m_parentList->size(); i++)
857 {
858 _sntprintf(pBuf, 10, _T("%d "), m_parentList->get(i)->getId());
859 while(*pBuf)
860 pBuf++;
861 }
862 unlockParentList();
863 if (pBuf != szBuffer)
864 *(pBuf - 1) = 0;
865 return szBuffer;
866 }
867
868 /**
869 * Calculate status for compound object based on childs' status
870 */
871 void NetObj::calculateCompoundStatus(BOOL bForcedRecalc)
872 {
873 if (m_status == STATUS_UNMANAGED)
874 return;
875
876 int mostCriticalAlarm = GetMostCriticalStatusForObject(m_id);
877 int mostCriticalDCI = isDataCollectionTarget() ? ((DataCollectionTarget *)this)->getMostCriticalDCIStatus() : STATUS_UNKNOWN;
878
879 int oldStatus = m_status;
880 int mostCriticalStatus, i, count, iStatusAlg;
881 int nSingleThreshold, *pnThresholds;
882 int nRating[5], iChildStatus, nThresholds[4];
883
884 lockProperties();
885 if (m_statusCalcAlg == SA_CALCULATE_DEFAULT)
886 {
887 iStatusAlg = GetDefaultStatusCalculation(&nSingleThreshold, &pnThresholds);
888 }
889 else
890 {
891 iStatusAlg = m_statusCalcAlg;
892 nSingleThreshold = m_statusSingleThreshold;
893 pnThresholds = m_statusThresholds;
894 }
895 if (iStatusAlg == SA_CALCULATE_SINGLE_THRESHOLD)
896 {
897 for(i = 0; i < 4; i++)
898 nThresholds[i] = nSingleThreshold;
899 pnThresholds = nThresholds;
900 }
901
902 switch(iStatusAlg)
903 {
904 case SA_CALCULATE_MOST_CRITICAL:
905 lockChildList(false);
906 for(i = 0, count = 0, mostCriticalStatus = -1; i < m_childList->size(); i++)
907 {
908 iChildStatus = m_childList->get(i)->getPropagatedStatus();
909 if ((iChildStatus < STATUS_UNKNOWN) &&
910 (iChildStatus > mostCriticalStatus))
911 {
912 mostCriticalStatus = iChildStatus;
913 count++;
914 }
915 }
916 m_status = (count > 0) ? mostCriticalStatus : STATUS_UNKNOWN;
917 unlockChildList();
918 break;
919 case SA_CALCULATE_SINGLE_THRESHOLD:
920 case SA_CALCULATE_MULTIPLE_THRESHOLDS:
921 // Step 1: calculate severity raitings
922 memset(nRating, 0, sizeof(int) * 5);
923 lockChildList(false);
924 for(i = 0, count = 0; i < m_childList->size(); i++)
925 {
926 iChildStatus = m_childList->get(i)->getPropagatedStatus();
927 if (iChildStatus < STATUS_UNKNOWN)
928 {
929 while(iChildStatus >= 0)
930 nRating[iChildStatus--]++;
931 count++;
932 }
933 }
934 unlockChildList();
935
936 // Step 2: check what severity rating is above threshold
937 if (count > 0)
938 {
939 for(i = 4; i > 0; i--)
940 if (nRating[i] * 100 / count >= pnThresholds[i - 1])
941 break;
942 m_status = i;
943 }
944 else
945 {
946 m_status = STATUS_UNKNOWN;
947 }
948 break;
949 default:
950 m_status = STATUS_UNKNOWN;
951 break;
952 }
953
954 // If alarms exist for object, apply alarm severity to object's status
955 if (mostCriticalAlarm != STATUS_UNKNOWN)
956 {
957 if (m_status == STATUS_UNKNOWN)
958 {
959 m_status = mostCriticalAlarm;
960 }
961 else
962 {
963 m_status = std::max(m_status, mostCriticalAlarm);
964 }
965 }
966
967 // If DCI status is calculated for object apply DCI object's status
968 if (mostCriticalDCI != STATUS_UNKNOWN)
969 {
970 if (m_status == STATUS_UNKNOWN)
971 {
972 m_status = mostCriticalDCI;
973 }
974 else
975 {
976 m_status = std::max(m_status, mostCriticalDCI);
977 }
978 }
979
980 // Query loaded modules for object status
981 ENUMERATE_MODULES(pfCalculateObjectStatus)
982 {
983 int moduleStatus = g_pModuleList[__i].pfCalculateObjectStatus(this);
984 if (moduleStatus != STATUS_UNKNOWN)
985 {
986 if (m_status == STATUS_UNKNOWN)
987 {
988 m_status = moduleStatus;
989 }
990 else
991 {
992 m_status = std::max(m_status, moduleStatus);
993 }
994 }
995 }
996
997 unlockProperties();
998
999 // Cause parent object(s) to recalculate it's status
1000 if ((oldStatus != m_status) || bForcedRecalc)
1001 {
1002 lockParentList(false);
1003 for(i = 0; i < m_parentList->size(); i++)
1004 m_parentList->get(i)->calculateCompoundStatus();
1005 unlockParentList();
1006 lockProperties();
1007 setModified(MODIFY_RUNTIME); // only notify clients
1008 unlockProperties();
1009 }
1010 }
1011
1012 /**
1013 * Load ACL from database
1014 */
1015 bool NetObj::loadACLFromDB(DB_HANDLE hdb)
1016 {
1017 bool success = false;
1018
1019 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT user_id,access_rights FROM acl WHERE object_id=?"));
1020 if (hStmt != NULL)
1021 {
1022 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
1023 DB_RESULT hResult = DBSelectPrepared(hStmt);
1024 if (hResult != NULL)
1025 {
1026 int count = DBGetNumRows(hResult);
1027 for(int i = 0; i < count; i++)
1028 m_accessList->addElement(DBGetFieldULong(hResult, i, 0), DBGetFieldULong(hResult, i, 1));
1029 DBFreeResult(hResult);
1030 success = true;
1031 }
1032 DBFreeStatement(hStmt);
1033 }
1034 return success;
1035 }
1036
1037 /**
1038 * ACL enumeration parameters structure
1039 */
1040 struct SAVE_PARAM
1041 {
1042 DB_HANDLE hdb;
1043 UINT32 dwObjectId;
1044 };
1045
1046 /**
1047 * Handler for ACL elements enumeration
1048 */
1049 static void EnumerationHandler(UINT32 dwUserId, UINT32 dwAccessRights, void *pArg)
1050 {
1051 TCHAR szQuery[256];
1052
1053 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("INSERT INTO acl (object_id,user_id,access_rights) VALUES (%d,%d,%d)"),
1054 ((SAVE_PARAM *)pArg)->dwObjectId, dwUserId, dwAccessRights);
1055 DBQuery(((SAVE_PARAM *)pArg)->hdb, szQuery);
1056 }
1057
1058 /**
1059 * Save ACL to database
1060 */
1061 bool NetObj::saveACLToDB(DB_HANDLE hdb)
1062 {
1063 if (!(m_modified & MODIFY_ACCESS_LIST))
1064 return true;
1065
1066 TCHAR szQuery[256];
1067 bool success = false;
1068 SAVE_PARAM sp;
1069
1070 // Save access list
1071 lockACL();
1072 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM acl WHERE object_id=%d"), m_id);
1073 if (DBQuery(hdb, szQuery))
1074 {
1075 sp.dwObjectId = m_id;
1076 sp.hdb = hdb;
1077 m_accessList->enumerateElements(EnumerationHandler, &sp);
1078 success = true;
1079 }
1080 unlockACL();
1081 return success;
1082 }
1083
1084 /**
1085 * Data for SendModuleDataCallback
1086 */
1087 struct SendModuleDataCallbackData
1088 {
1089 NXCPMessage *msg;
1090 UINT32 id;
1091 };
1092
1093 /**
1094 * Callback for sending module data in NXCP message
1095 */
1096 static EnumerationCallbackResult SendModuleDataCallback(const TCHAR *key, const void *value, void *data)
1097 {
1098 ((SendModuleDataCallbackData *)data)->msg->setField(((SendModuleDataCallbackData *)data)->id, key);
1099 ((ModuleData *)value)->fillMessage(((SendModuleDataCallbackData *)data)->msg, ((SendModuleDataCallbackData *)data)->id + 1);
1100 ((SendModuleDataCallbackData *)data)->id += 0x100000;
1101 return _CONTINUE;
1102 }
1103
1104 /**
1105 * Fill NXCP message with object's data
1106 * Object's properties are locked when this method is called. Method should not do any other locks.
1107 * Data required other locks should be filled in fillMessageInternalStage2().
1108 */
1109 void NetObj::fillMessageInternal(NXCPMessage *pMsg)
1110 {
1111 pMsg->setField(VID_OBJECT_CLASS, (WORD)getObjectClass());
1112 pMsg->setField(VID_OBJECT_ID, m_id);
1113 pMsg->setField(VID_GUID, m_guid);
1114 pMsg->setField(VID_OBJECT_NAME, m_name);
1115 pMsg->setField(VID_OBJECT_STATUS, (WORD)m_status);
1116 pMsg->setField(VID_IS_DELETED, (WORD)(m_isDeleted ? 1 : 0));
1117 pMsg->setField(VID_IS_SYSTEM, (INT16)(m_isSystem ? 1 : 0));
1118 pMsg->setField(VID_MAINTENANCE_MODE, (INT16)(m_maintenanceEventId ? 1 : 0));
1119 pMsg->setField(VID_FLAGS, m_flags);
1120
1121 pMsg->setField(VID_INHERIT_RIGHTS, m_inheritAccessRights);
1122 pMsg->setField(VID_STATUS_CALCULATION_ALG, (WORD)m_statusCalcAlg);
1123 pMsg->setField(VID_STATUS_PROPAGATION_ALG, (WORD)m_statusPropAlg);
1124 pMsg->setField(VID_FIXED_STATUS, (WORD)m_fixedStatus);
1125 pMsg->setField(VID_STATUS_SHIFT, (WORD)m_statusShift);
1126 pMsg->setField(VID_STATUS_TRANSLATION_1, (WORD)m_statusTranslation[0]);
1127 pMsg->setField(VID_STATUS_TRANSLATION_2, (WORD)m_statusTranslation[1]);
1128 pMsg->setField(VID_STATUS_TRANSLATION_3, (WORD)m_statusTranslation[2]);
1129 pMsg->setField(VID_STATUS_TRANSLATION_4, (WORD)m_statusTranslation[3]);
1130 pMsg->setField(VID_STATUS_SINGLE_THRESHOLD, (WORD)m_statusSingleThreshold);
1131 pMsg->setField(VID_STATUS_THRESHOLD_1, (WORD)m_statusThresholds[0]);
1132 pMsg->setField(VID_STATUS_THRESHOLD_2, (WORD)m_statusThresholds[1]);
1133 pMsg->setField(VID_STATUS_THRESHOLD_3, (WORD)m_statusThresholds[2]);
1134 pMsg->setField(VID_STATUS_THRESHOLD_4, (WORD)m_statusThresholds[3]);
1135 pMsg->setField(VID_COMMENTS, CHECK_NULL_EX(m_comments));
1136 pMsg->setField(VID_IMAGE, m_image);
1137 pMsg->setField(VID_DRILL_DOWN_OBJECT_ID, m_submapId);
1138 if ((m_trustedNodes != NULL) && (m_trustedNodes->size() > 0))
1139 {
1140 pMsg->setField(VID_NUM_TRUSTED_NODES, m_trustedNodes->size());
1141 pMsg->setFieldFromInt32Array(VID_TRUSTED_NODES, m_trustedNodes);
1142 }
1143 else
1144 {
1145 pMsg->setField(VID_NUM_TRUSTED_NODES, (UINT32)0);
1146 }
1147 pMsg->setFieldFromInt32Array(VID_DASHBOARDS, m_dashboards);
1148
1149 m_customAttributes.fillMessage(pMsg, VID_NUM_CUSTOM_ATTRIBUTES, VID_CUSTOM_ATTRIBUTES_BASE);
1150
1151 m_geoLocation.fillMessage(*pMsg);
1152
1153 pMsg->setField(VID_COUNTRY, m_postalAddress->getCountry());
1154 pMsg->setField(VID_CITY, m_postalAddress->getCity());
1155 pMsg->setField(VID_STREET_ADDRESS, m_postalAddress->getStreetAddress());
1156 pMsg->setField(VID_POSTCODE, m_postalAddress->getPostCode());
1157
1158 pMsg->setField(VID_NUM_URLS, (UINT32)m_urls->size());
1159 UINT32 fieldId = VID_URL_LIST_BASE;
1160 for(int i = 0; i < m_urls->size(); i++)
1161 {
1162 const ObjectUrl *url = m_urls->get(i);
1163 pMsg->setField(fieldId++, url->getId());
1164 pMsg->setField(fieldId++, url->getUrl());
1165 pMsg->setField(fieldId++, url->getDescription());
1166 fieldId += 7;
1167 }
1168
1169 if (m_moduleData != NULL)
1170 {
1171 pMsg->setField(VID_MODULE_DATA_COUNT, (UINT16)m_moduleData->size());
1172 SendModuleDataCallbackData data;
1173 data.msg = pMsg;
1174 data.id = VID_MODULE_DATA_BASE;
1175 m_moduleData->forEach(SendModuleDataCallback, &data);
1176 }
1177 else
1178 {
1179 pMsg->setField(VID_MODULE_DATA_COUNT, (UINT16)0);
1180 }
1181 }
1182
1183 /**
1184 * Fill NXCP message with object's data - stage 2
1185 * Object's properties are not locked when this method is called. Should be
1186 * used only to fill data where properties lock is not enough (like data
1187 * collection configuration).
1188 */
1189 void NetObj::fillMessageInternalStage2(NXCPMessage *pMsg)
1190 {
1191 }
1192
1193 /**
1194 * Fill NXCP message with object's data
1195 */
1196 void NetObj::fillMessage(NXCPMessage *msg)
1197 {
1198 lockProperties();
1199 fillMessageInternal(msg);
1200 unlockProperties();
1201 fillMessageInternalStage2(msg);
1202
1203 lockACL();
1204 m_accessList->fillMessage(msg);
1205 unlockACL();
1206
1207 UINT32 dwId;
1208 int i;
1209
1210 lockParentList(false);
1211 msg->setField(VID_PARENT_CNT, m_parentList->size());
1212 for(i = 0, dwId = VID_PARENT_ID_BASE; i < m_parentList->size(); i++, dwId++)
1213 msg->setField(dwId, m_parentList->get(i)->getId());
1214 unlockParentList();
1215
1216 lockChildList(false);
1217 msg->setField(VID_CHILD_CNT, m_childList->size());
1218 for(i = 0, dwId = VID_CHILD_ID_BASE; i < m_childList->size(); i++, dwId++)
1219 msg->setField(dwId, m_childList->get(i)->getId());
1220 unlockChildList();
1221 }
1222
1223 /**
1224 * Handler for EnumerateSessions()
1225 */
1226 static void BroadcastObjectChange(ClientSession *pSession, void *pArg)
1227 {
1228 if (pSession->isAuthenticated())
1229 pSession->onObjectChange((NetObj *)pArg);
1230 }
1231
1232 /**
1233 * Mark object as modified and put on client's notification queue
1234 * We assume that object is locked at the time of function call
1235 */
1236 void NetObj::setModified(UINT32 flags, bool notify)
1237 {
1238 if (g_bModificationsLocked)
1239 return;
1240
1241 m_modified |= flags;
1242 m_timestamp = time(NULL);
1243
1244 // Send event to all connected clients
1245 if (notify && !m_isHidden && !m_isSystem)
1246 EnumerateClientSessions(BroadcastObjectChange, this);
1247 }
1248
1249 /**
1250 * Modify object from NXCP message - common wrapper
1251 */
1252 UINT32 NetObj::modifyFromMessage(NXCPMessage *msg)
1253 {
1254 lockProperties();
1255 UINT32 rcc = modifyFromMessageInternal(msg);
1256 setModified(MODIFY_ALL);
1257 unlockProperties();
1258 return rcc;
1259 }
1260
1261 /**
1262 * Modify object from NXCP message
1263 */
1264 UINT32 NetObj::modifyFromMessageInternal(NXCPMessage *pRequest)
1265 {
1266 // Change object's name
1267 if (pRequest->isFieldExist(VID_OBJECT_NAME))
1268 pRequest->getFieldAsString(VID_OBJECT_NAME, m_name, MAX_OBJECT_NAME);
1269
1270 // Change object's status calculation/propagation algorithms
1271 if (pRequest->isFieldExist(VID_STATUS_CALCULATION_ALG))
1272 {
1273 m_statusCalcAlg = pRequest->getFieldAsInt16(VID_STATUS_CALCULATION_ALG);
1274 m_statusPropAlg = pRequest->getFieldAsInt16(VID_STATUS_PROPAGATION_ALG);
1275 m_fixedStatus = pRequest->getFieldAsInt16(VID_FIXED_STATUS);
1276 m_statusShift = pRequest->getFieldAsInt16(VID_STATUS_SHIFT);
1277 m_statusTranslation[0] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_1);
1278 m_statusTranslation[1] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_2);
1279 m_statusTranslation[2] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_3);
1280 m_statusTranslation[3] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_4);
1281 m_statusSingleThreshold = pRequest->getFieldAsInt16(VID_STATUS_SINGLE_THRESHOLD);
1282 m_statusThresholds[0] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_1);
1283 m_statusThresholds[1] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_2);
1284 m_statusThresholds[2] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_3);
1285 m_statusThresholds[3] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_4);
1286 }
1287
1288 // Change image
1289 if (pRequest->isFieldExist(VID_IMAGE))
1290 m_image = pRequest->getFieldAsGUID(VID_IMAGE);
1291
1292 // Change object's ACL
1293 if (pRequest->isFieldExist(VID_ACL_SIZE))
1294 {
1295 lockACL();
1296 m_inheritAccessRights = pRequest->getFieldAsBoolean(VID_INHERIT_RIGHTS);
1297 m_accessList->deleteAll();
1298 int count = pRequest->getFieldAsUInt32(VID_ACL_SIZE);
1299 for(int i = 0; i < count; i++)
1300 m_accessList->addElement(pRequest->getFieldAsUInt32(VID_ACL_USER_BASE + i),
1301 pRequest->getFieldAsUInt32(VID_ACL_RIGHTS_BASE + i));
1302 unlockACL();
1303 }
1304
1305 // Change trusted nodes list
1306 if (pRequest->isFieldExist(VID_NUM_TRUSTED_NODES))
1307 {
1308 if (m_trustedNodes == NULL)
1309 m_trustedNodes = new IntegerArray<UINT32>();
1310 else
1311 m_trustedNodes->clear();
1312 pRequest->getFieldAsInt32Array(VID_TRUSTED_NODES, m_trustedNodes);
1313 }
1314
1315 // Change custom attributes
1316 if (pRequest->isFieldExist(VID_NUM_CUSTOM_ATTRIBUTES))
1317 {
1318 UINT32 i, dwId, dwNumElements;
1319 TCHAR *name, *value;
1320
1321 dwNumElements = pRequest->getFieldAsUInt32(VID_NUM_CUSTOM_ATTRIBUTES);
1322 m_customAttributes.clear();
1323 for(i = 0, dwId = VID_CUSTOM_ATTRIBUTES_BASE; i < dwNumElements; i++)
1324 {
1325 name = pRequest->getFieldAsString(dwId++);
1326 value = pRequest->getFieldAsString(dwId++);
1327 if ((name != NULL) && (value != NULL))
1328 m_customAttributes.setPreallocated(name, value);
1329 }
1330 }
1331
1332 // Change geolocation
1333 if (pRequest->isFieldExist(VID_GEOLOCATION_TYPE))
1334 {
1335 m_geoLocation = GeoLocation(*pRequest);
1336 addLocationToHistory();
1337 }
1338
1339 if (pRequest->isFieldExist(VID_DRILL_DOWN_OBJECT_ID))
1340 {
1341 m_submapId = pRequest->getFieldAsUInt32(VID_DRILL_DOWN_OBJECT_ID);
1342 }
1343
1344 if (pRequest->isFieldExist(VID_COUNTRY))
1345 {
1346 TCHAR buffer[64];
1347 pRequest->getFieldAsString(VID_COUNTRY, buffer, 64);
1348 m_postalAddress->setCountry(buffer);
1349 }
1350
1351 if (pRequest->isFieldExist(VID_CITY))
1352 {
1353 TCHAR buffer[64];
1354 pRequest->getFieldAsString(VID_CITY, buffer, 64);
1355 m_postalAddress->setCity(buffer);
1356 }
1357
1358 if (pRequest->isFieldExist(VID_STREET_ADDRESS))
1359 {
1360 TCHAR buffer[256];
1361 pRequest->getFieldAsString(VID_STREET_ADDRESS, buffer, 256);
1362 m_postalAddress->setStreetAddress(buffer);
1363 }
1364
1365 if (pRequest->isFieldExist(VID_POSTCODE))
1366 {
1367 TCHAR buffer[32];
1368 pRequest->getFieldAsString(VID_POSTCODE, buffer, 32);
1369 m_postalAddress->setPostCode(buffer);
1370 }
1371
1372 // Change dashboard list
1373 if (pRequest->isFieldExist(VID_DASHBOARDS))
1374 {
1375 pRequest->getFieldAsInt32Array(VID_DASHBOARDS, m_dashboards);
1376 }
1377
1378 // Update URL list
1379 if (pRequest->isFieldExist(VID_NUM_URLS))
1380 {
1381 m_urls->clear();
1382 int count = pRequest->getFieldAsInt32(VID_NUM_URLS);
1383 UINT32 fieldId = VID_URL_LIST_BASE;
1384 for(int i = 0; i < count; i++)
1385 {
1386 m_urls->add(new ObjectUrl(pRequest, fieldId));
1387 fieldId += 10;
1388 }
1389 }
1390
1391 return RCC_SUCCESS;
1392 }
1393
1394 /**
1395 * Post-modify hook
1396 */
1397 void NetObj::postModify()
1398 {
1399 calculateCompoundStatus(TRUE);
1400 }
1401
1402 /**
1403 * Get rights to object for specific user
1404 *
1405 * @param userId user object ID
1406 */
1407 UINT32 NetObj::getUserRights(UINT32 userId)
1408 {
1409 UINT32 dwRights;
1410
1411 // Admin always has all rights to any object
1412 if (userId == 0)
1413 return 0xFFFFFFFF;
1414
1415 // Non-admin users have no rights to system objects
1416 if (m_isSystem)
1417 return 0;
1418
1419 // Check if have direct right assignment
1420 lockACL();
1421 bool hasDirectRights = m_accessList->getUserRights(userId, &dwRights);
1422 unlockACL();
1423
1424 if (!hasDirectRights)
1425 {
1426 // We don't. If this object inherit rights from parents, get them
1427 if (m_inheritAccessRights)
1428 {
1429 dwRights = 0;
1430 lockParentList(false);
1431 for(int i = 0; i < m_parentList->size(); i++)
1432 dwRights |= m_parentList->get(i)->getUserRights(userId);
1433 unlockParentList();
1434 }
1435 }
1436
1437 return dwRights;
1438 }
1439
1440 /**
1441 * Check if given user has specific rights on this object
1442 *
1443 * @param userId user object ID
1444 * @param requiredRights bit mask of requested right
1445 * @return true if user has all rights specified in requested rights bit mask
1446 */
1447 BOOL NetObj::checkAccessRights(UINT32 userId, UINT32 requiredRights)
1448 {
1449 UINT32 effectiveRights = getUserRights(userId);
1450 return (effectiveRights & requiredRights) == requiredRights;
1451 }
1452
1453 /**
1454 * Drop all user privileges on current object
1455 */
1456 void NetObj::dropUserAccess(UINT32 dwUserId)
1457 {
1458 lockACL();
1459 bool modified = m_accessList->deleteElement(dwUserId);
1460 unlockACL();
1461 if (modified)
1462 {
1463 lockProperties();
1464 setModified(MODIFY_ACCESS_LIST);
1465 unlockProperties();
1466 }
1467 }
1468
1469 /**
1470 * Set object's management status
1471 */
1472 void NetObj::setMgmtStatus(BOOL bIsManaged)
1473 {
1474 int oldStatus;
1475
1476 lockProperties();
1477
1478 if ((bIsManaged && (m_status != STATUS_UNMANAGED)) ||
1479 ((!bIsManaged) && (m_status == STATUS_UNMANAGED)))
1480 {
1481 unlockProperties();
1482 return; // Status is already correct
1483 }
1484
1485 oldStatus = m_status;
1486 m_status = (bIsManaged ? STATUS_UNKNOWN : STATUS_UNMANAGED);
1487
1488 // Generate event if current object is a node
1489 if (getObjectClass() == OBJECT_NODE)
1490 PostEvent(bIsManaged ? EVENT_NODE_UNKNOWN : EVENT_NODE_UNMANAGED, m_id, "d", oldStatus);
1491
1492 setModified(MODIFY_COMMON_PROPERTIES);
1493 unlockProperties();
1494
1495 // Change status for child objects also
1496 lockChildList(false);
1497 for(int i = 0; i < m_childList->size(); i++)
1498 m_childList->get(i)->setMgmtStatus(bIsManaged);
1499 unlockChildList();
1500
1501 // Cause parent object(s) to recalculate it's status
1502 lockParentList(false);
1503 for(int i = 0; i < m_parentList->size(); i++)
1504 m_parentList->get(i)->calculateCompoundStatus();
1505 unlockParentList();
1506 }
1507
1508 /**
1509 * Check if given object is an our child (possibly indirect, i.e child of child)
1510 *
1511 * @param id object ID to test
1512 */
1513 bool NetObj::isChild(UINT32 id)
1514 {
1515 bool bResult = false;
1516
1517 // Check for our own ID (object ID should never change, so we may not lock object's data)
1518 if (m_id == id)
1519 bResult = true;
1520
1521 // First, walk through our own child list
1522 if (!bResult)
1523 {
1524 lockChildList(false);
1525 for(int i = 0; i < m_childList->size(); i++)
1526 if (m_childList->get(i)->getId() == id)
1527 {
1528 bResult = true;
1529 break;
1530 }
1531 unlockChildList();
1532 }
1533
1534 // If given object is not in child list, check if it is indirect child
1535 if (!bResult)
1536 {
1537 lockChildList(false);
1538 for(int i = 0; i < m_childList->size(); i++)
1539 if (m_childList->get(i)->isChild(id))
1540 {
1541 bResult = true;
1542 break;
1543 }
1544 unlockChildList();
1545 }
1546
1547 return bResult;
1548 }
1549
1550 /**
1551 * Send message to client, who requests poll, if any
1552 * This method is used by Node and Interface class objects
1553 */
1554 void NetObj::sendPollerMsg(UINT32 dwRqId, const TCHAR *pszFormat, ...)
1555 {
1556 if (m_pollRequestor != NULL)
1557 {
1558 va_list args;
1559 TCHAR szBuffer[1024];
1560
1561 va_start(args, pszFormat);
1562 _vsntprintf(szBuffer, 1024, pszFormat, args);
1563 va_end(args);
1564 m_pollRequestor->sendPollerMsg(dwRqId, szBuffer);
1565 }
1566 }
1567
1568 /**
1569 * Add child node objects (direct and indirect childs) to list
1570 */
1571 void NetObj::addChildNodesToList(ObjectArray<Node> *nodeList, UINT32 dwUserId)
1572 {
1573 lockChildList(false);
1574
1575 // Walk through our own child list
1576 for(int i = 0; i < m_childList->size(); i++)
1577 {
1578 NetObj *object = m_childList->get(i);
1579 if (object->getObjectClass() == OBJECT_NODE)
1580 {
1581 // Check if this node already in the list
1582 int j;
1583 for(j = 0; j < nodeList->size(); j++)
1584 if (nodeList->get(j)->getId() == object->getId())
1585 break;
1586 if (j == nodeList->size())
1587 {
1588 object->incRefCount();
1589 nodeList->add((Node *)object);
1590 }
1591 }
1592 else
1593 {
1594 if (object->checkAccessRights(dwUserId, OBJECT_ACCESS_READ))
1595 object->addChildNodesToList(nodeList, dwUserId);
1596 }
1597 }
1598
1599 unlockChildList();
1600 }
1601
1602 /**
1603 * Add child data collection targets (direct and indirect childs) to list
1604 */
1605 void NetObj::addChildDCTargetsToList(ObjectArray<DataCollectionTarget> *dctList, UINT32 dwUserId)
1606 {
1607 lockChildList(false);
1608
1609 // Walk through our own child list
1610 for(int i = 0; i < m_childList->size(); i++)
1611 {
1612 NetObj *object = m_childList->get(i);
1613 if (!object->checkAccessRights(dwUserId, OBJECT_ACCESS_READ))
1614 continue;
1615
1616 if (object->isDataCollectionTarget())
1617 {
1618 // Check if this objects already in the list
1619 int j;
1620 for(j = 0; j < dctList->size(); j++)
1621 if (dctList->get(j)->getId() == object->getId())
1622 break;
1623 if (j == dctList->size())
1624 {
1625 object->incRefCount();
1626 dctList->add((DataCollectionTarget *)object);
1627 }
1628 }
1629 object->addChildDCTargetsToList(dctList, dwUserId);
1630 }
1631
1632 unlockChildList();
1633 }
1634
1635 /**
1636 * Hide object and all its childs
1637 */
1638 void NetObj::hide()
1639 {
1640 lockChildList(false);
1641 for(int i = 0; i < m_childList->size(); i++)
1642 m_childList->get(i)->hide();
1643 unlockChildList();
1644
1645 lockProperties();
1646 m_isHidden = true;
1647 unlockProperties();
1648 }
1649
1650 /**
1651 * Unhide object and all its childs
1652 */
1653 void NetObj::unhide()
1654 {
1655 lockProperties();
1656 m_isHidden = false;
1657 if (!m_isSystem)
1658 EnumerateClientSessions(BroadcastObjectChange, this);
1659 unlockProperties();
1660
1661 lockChildList(false);
1662 for(int i = 0; i < m_childList->size(); i++)
1663 m_childList->get(i)->unhide();
1664 unlockChildList();
1665 }
1666
1667 /**
1668 * Return status propagated to parent
1669 */
1670 int NetObj::getPropagatedStatus()
1671 {
1672 int iStatus;
1673
1674 if (m_statusPropAlg == SA_PROPAGATE_DEFAULT)
1675 {
1676 iStatus = DefaultPropagatedStatus(m_status);
1677 }
1678 else
1679 {
1680 switch(m_statusPropAlg)
1681 {
1682 case SA_PROPAGATE_UNCHANGED:
1683 iStatus = m_status;
1684 break;
1685 case SA_PROPAGATE_FIXED:
1686 iStatus = ((m_status > STATUS_NORMAL) && (m_status < STATUS_UNKNOWN)) ? m_fixedStatus : m_status;
1687 break;
1688 case SA_PROPAGATE_RELATIVE:
1689 if ((m_status > STATUS_NORMAL) && (m_status < STATUS_UNKNOWN))
1690 {
1691 iStatus = m_status + m_statusShift;
1692 if (iStatus < 0)
1693 iStatus = 0;
1694 if (iStatus > STATUS_CRITICAL)
1695 iStatus = STATUS_CRITICAL;
1696 }
1697 else
1698 {
1699 iStatus = m_status;
1700 }
1701 break;
1702 case SA_PROPAGATE_TRANSLATED:
1703 if ((m_status > STATUS_NORMAL) && (m_status < STATUS_UNKNOWN))
1704 {
1705 iStatus = m_statusTranslation[m_status - 1];
1706 }
1707 else
1708 {
1709 iStatus = m_status;
1710 }
1711 break;
1712 default:
1713 iStatus = STATUS_UNKNOWN;
1714 break;
1715 }
1716 }
1717 return iStatus;
1718 }
1719
1720 /**
1721 * Prepare object for deletion. Method should return only
1722 * when object deletion is safe
1723 */
1724 void NetObj::prepareForDeletion()
1725 {
1726 }
1727
1728 /**
1729 * Set object's comments.
1730 * NOTE: pszText should be dynamically allocated or NULL
1731 */
1732 void NetObj::setComments(TCHAR *text)
1733 {
1734 lockProperties();
1735 free(m_comments);
1736 m_comments = text;
1737 setModified(MODIFY_COMMON_PROPERTIES);
1738 unlockProperties();
1739 }
1740
1741 /**
1742 * Copy object's comments to NXCP message
1743 */
1744 void NetObj::commentsToMessage(NXCPMessage *pMsg)
1745 {
1746 lockProperties();
1747 pMsg->setField(VID_COMMENTS, CHECK_NULL_EX(m_comments));
1748 unlockProperties();
1749 }
1750
1751 /**
1752 * Load trusted nodes list from database
1753 */
1754 bool NetObj::loadTrustedNodes(DB_HANDLE hdb)
1755 {
1756 TCHAR query[256];
1757 _sntprintf(query, 256, _T("SELECT target_node_id FROM trusted_nodes WHERE source_object_id=%d"), m_id);
1758 DB_RESULT hResult = DBSelect(hdb, query);
1759 if (hResult != NULL)
1760 {
1761 int count = DBGetNumRows(hResult);
1762 if (count > 0)
1763 {
1764 m_trustedNodes = new IntegerArray<UINT32>(count);
1765 for(int i = 0; i < count; i++)
1766 {
1767 m_trustedNodes->add(DBGetFieldULong(hResult, i, 0));
1768 }
1769 }
1770 DBFreeResult(hResult);
1771 }
1772 return (hResult != NULL);
1773 }
1774
1775 /**
1776 * Save list of trusted nodes to database
1777 */
1778 bool NetObj::saveTrustedNodes(DB_HANDLE hdb)
1779 {
1780 bool success = executeQueryOnObject(hdb, _T("DELETE FROM trusted_nodes WHERE source_object_id=?"));
1781 if (success && (m_trustedNodes != NULL) && (m_trustedNodes->size() > 0))
1782 {
1783 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO trusted_nodes (source_object_id,target_node_id) VALUES (?,?)"));
1784 if (hStmt != NULL)
1785 {
1786 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
1787 for(int i = 0; (i < m_trustedNodes->size()) && success; i++)
1788 {
1789 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_trustedNodes->get(i));
1790 success = DBExecute(hStmt);
1791 }
1792 DBFreeStatement(hStmt);
1793 }
1794 else
1795 {
1796 success = false;
1797 }
1798 }
1799 return success;
1800 }
1801
1802 /**
1803 * Check if given node is in trust list
1804 * Will always return TRUE if system parameter CheckTrustedNodes set to 0
1805 */
1806 bool NetObj::isTrustedNode(UINT32 id)
1807 {
1808 if (!(g_flags & AF_CHECK_TRUSTED_NODES))
1809 return true;
1810
1811 lockProperties();
1812 bool rc = (m_trustedNodes != NULL) ? m_trustedNodes->contains(id) : false;
1813 unlockProperties();
1814 return rc;
1815 }
1816
1817 /**
1818 * Get list of parent objects for NXSL script
1819 */
1820 NXSL_Array *NetObj::getParentsForNXSL()
1821 {
1822 NXSL_Array *parents = new NXSL_Array;
1823 int index = 0;
1824
1825 lockParentList(false);
1826 for(int i = 0; i < m_parentList->size(); i++)
1827 {
1828 NetObj *obj = m_parentList->get(i);
1829 if ((obj->getObjectClass() == OBJECT_CONTAINER) ||
1830 (obj->getObjectClass() == OBJECT_SERVICEROOT) ||
1831 (obj->getObjectClass() == OBJECT_NETWORK))
1832 {
1833 parents->set(index++, obj->createNXSLObject());
1834 }
1835 }
1836 unlockParentList();
1837
1838 return parents;
1839 }
1840
1841 /**
1842 * Get list of child objects for NXSL script
1843 */
1844 NXSL_Array *NetObj::getChildrenForNXSL()
1845 {
1846 NXSL_Array *children = new NXSL_Array;
1847 int index = 0;
1848
1849 lockChildList(false);
1850 for(int i = 0; i < m_childList->size(); i++)
1851 {
1852 children->set(index++, m_childList->get(i)->createNXSLObject());
1853 }
1854 unlockChildList();
1855
1856 return children;
1857 }
1858
1859 /**
1860 * Get full list of child objects (including both direct and indirect childs)
1861 */
1862 void NetObj::getFullChildListInternal(ObjectIndex *list, bool eventSourceOnly)
1863 {
1864 lockChildList(false);
1865 for(int i = 0; i < m_childList->size(); i++)
1866 {
1867 NetObj *obj = m_childList->get(i);
1868 if (!eventSourceOnly || IsEventSource(obj->getObjectClass()))
1869 list->put(obj->getId(), obj);
1870 obj->getFullChildListInternal(list, eventSourceOnly);
1871 }
1872 unlockChildList();
1873 }
1874
1875 /**
1876 * Get full list of child objects (including both direct and indirect childs).
1877 * Returned array is dynamically allocated and must be deleted by the caller.
1878 *
1879 * @param eventSourceOnly if true, only objects that can be event source will be included
1880 */
1881 ObjectArray<NetObj> *NetObj::getFullChildList(bool eventSourceOnly, bool updateRefCount)
1882 {
1883 ObjectIndex list;
1884 getFullChildListInternal(&list, eventSourceOnly);
1885 return list.getObjects(updateRefCount);
1886 }
1887
1888 /**
1889 * Get list of child objects (direct only). Returned array is
1890 * dynamically allocated and must be deleted by the caller.
1891 *
1892 * @param typeFilter Only return objects with class ID equals given value.
1893 * Set to -1 to disable filtering.
1894 */
1895 ObjectArray<NetObj> *NetObj::getChildList(int typeFilter)
1896 {
1897 lockChildList(false);
1898 ObjectArray<NetObj> *list = new ObjectArray<NetObj>((int)m_childList->size(), 16, false);
1899 for(int i = 0; i < m_childList->size(); i++)
1900 {
1901 if ((typeFilter == -1) || (typeFilter == m_childList->get(i)->getObjectClass()))
1902 list->add(m_childList->get(i));
1903 }
1904 unlockChildList();
1905 return list;
1906 }
1907
1908 /**
1909 * Get list of parent objects (direct only). Returned array is
1910 * dynamically allocated and must be deleted by the caller.
1911 *
1912 * @param typeFilter Only return objects with class ID equals given value.
1913 * Set to -1 to disable filtering.
1914 */
1915 ObjectArray<NetObj> *NetObj::getParentList(int typeFilter)
1916 {
1917 lockParentList(false);
1918 ObjectArray<NetObj> *list = new ObjectArray<NetObj>(m_parentList->size(), 16, false);
1919 for(int i = 0; i < m_parentList->size(); i++)
1920 {
1921 if ((typeFilter == -1) || (typeFilter == m_parentList->get(i)->getObjectClass()))
1922 list->add(m_parentList->get(i));
1923 }
1924 unlockParentList();
1925 return list;
1926 }
1927
1928 /**
1929 * FInd child object by name (with optional class filter)
1930 */
1931 NetObj *NetObj::findChildObject(const TCHAR *name, int typeFilter)
1932 {
1933 NetObj *object = NULL;
1934 lockChildList(false);
1935 for(int i = 0; i < m_childList->size(); i++)
1936 {
1937 NetObj *o = m_childList->get(i);
1938 if (((typeFilter == -1) || (typeFilter == o->getObjectClass())) && !_tcsicmp(name, o->getName()))
1939 {
1940 object = o;
1941 break;
1942 }
1943 }
1944 unlockChildList();
1945 return object;
1946 }
1947
1948 /**
1949 * Find child node by IP address
1950 */
1951 Node *NetObj::findChildNode(const InetAddress& addr)
1952 {
1953 Node *node = NULL;
1954 lockChildList(false);
1955 for(int i = 0; i < m_childList->size(); i++)
1956 {
1957 NetObj *curr = m_childList->get(i);
1958 if ((curr->getObjectClass() == OBJECT_NODE) && addr.equals(((Node *)curr)->getIpAddress()))
1959 {
1960 node = (Node *)curr;
1961 break;
1962 }
1963 }
1964 unlockChildList();
1965 return node;
1966 }
1967
1968 /**
1969 * Called by client session handler to check if threshold summary should
1970 * be shown for this object. Default implementation always returns false.
1971 */
1972 bool NetObj::showThresholdSummary()
1973 {
1974 return false;
1975 }
1976
1977 /**
1978 * Must return true if object is a possible event source
1979 */
1980 bool NetObj::isEventSource()
1981 {
1982 return false;
1983 }
1984
1985 /**
1986 * Must return true if object is a possible data collection target
1987 */
1988 bool NetObj::isDataCollectionTarget()
1989 {
1990 return false;
1991 }
1992
1993 /**
1994 * Get module data
1995 */
1996 ModuleData *NetObj::getModuleData(const TCHAR *module)
1997 {
1998 lockProperties();
1999 ModuleData *data = (m_moduleData != NULL) ? m_moduleData->get(module) : NULL;
2000 unlockProperties();
2001 return data;
2002 }
2003
2004 /**
2005 * Set module data
2006 */
2007 void NetObj::setModuleData(const TCHAR *module, ModuleData *data)
2008 {
2009 lockProperties();
2010 if (m_moduleData == NULL)
2011 m_moduleData = new StringObjectMap<ModuleData>(true);
2012 m_moduleData->set(module, data);
2013 unlockProperties();
2014 }
2015
2016 /**
2017 * Add new location entry
2018 */
2019 void NetObj::addLocationToHistory()
2020 {
2021 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
2022 bool isSamePlace;
2023 double latitude = 0;
2024 double longitude = 0;
2025 UINT32 accuracy = 0;
2026 UINT32 startTimestamp = 0;
2027 DB_RESULT hResult;
2028 if (!isLocationTableExists(hdb))
2029 {
2030 DbgPrintf(4, _T("NetObj::addLocationToHistory: Geolocation history table will be created for object %s [%d]"), m_name, m_id);
2031 if (!createLocationHistoryTable(hdb))
2032 {
2033 DbgPrintf(4, _T("NetObj::addLocationToHistory: Error creating geolocation history table for object %s [%d]"), m_name, m_id);
2034 return;
2035 }
2036 }
2037 const TCHAR *query;
2038 switch(g_dbSyntax)
2039 {
2040 case DB_SYNTAX_ORACLE:
2041 query = _T("SELECT * FROM (SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC) WHERE ROWNUM<=1");
2042 break;
2043 case DB_SYNTAX_MSSQL:
2044 query = _T("SELECT TOP 1 latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC");
2045 break;
2046 case DB_SYNTAX_DB2:
2047 query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC FETCH FIRST 200 ROWS ONLY");
2048 break;
2049 default:
2050 query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC LIMIT 1");
2051 break;
2052 }
2053 TCHAR preparedQuery[256];
2054 _sntprintf(preparedQuery, 256, query, m_id);
2055 DB_STATEMENT hStmt = DBPrepare(hdb, preparedQuery);
2056
2057 if (hStmt == NULL)
2058 goto onFail;
2059
2060 hResult = DBSelectPrepared(hStmt);
2061 if (hResult == NULL)
2062 goto onFail;
2063 if (DBGetNumRows(hResult) > 0)
2064 {
2065 latitude = DBGetFieldDouble(hResult, 0, 0);
2066 longitude = DBGetFieldDouble(hResult, 0, 1);
2067 accuracy = DBGetFieldLong(hResult, 0, 2);
2068 startTimestamp = DBGetFieldULong(hResult, 0, 3);
2069 isSamePlace = m_geoLocation.sameLocation(latitude, longitude, accuracy);
2070 }
2071 else
2072 {
2073 isSamePlace = false;
2074 }
2075 DBFreeResult(hResult);
2076 DBFreeStatement(hStmt);
2077
2078 if (isSamePlace)
2079 {
2080 TCHAR query[256];
2081 _sntprintf(query, 255, _T("UPDATE gps_history_%d SET end_timestamp = ? WHERE start_timestamp =? "), m_id);
2082 hStmt = DBPrepare(hdb, query);
2083 if (hStmt == NULL)
2084 goto onFail;
2085 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
2086 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, startTimestamp);
2087 }
2088 else
2089 {
2090 TCHAR query[256];
2091 _sntprintf(query, 255, _T("INSERT INTO gps_history_%d (latitude,longitude,")
2092 _T("accuracy,start_timestamp,end_timestamp) VALUES (?,?,?,?,?)"), m_id);
2093 hStmt = DBPrepare(hdb, query);
2094 if (hStmt == NULL)
2095 goto onFail;
2096
2097 TCHAR lat[32], lon[32];
2098 _sntprintf(lat, 32, _T("%f"), m_geoLocation.getLatitude());
2099 _sntprintf(lon, 32, _T("%f"), m_geoLocation.getLongitude());
2100
2101 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, lat, DB_BIND_STATIC);
2102 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, lon, DB_BIND_STATIC);
2103 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getAccuracy());
2104 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
2105 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
2106 }
2107
2108 if(!DBExecute(hStmt))
2109 {
2110 DbgPrintf(1, _T("NetObj::addLocationToHistory: Failed to add location to history. New: lat %f, lon %f, ac %d, t %d. Old: lat %f, lon %f, ac %d, t %d."),
2111 m_geoLocation.getLatitude(), m_geoLocation.getLongitude(), m_geoLocation.getAccuracy(), (UINT32)m_geoLocation.getTimestamp(),
2112 latitude, longitude, accuracy, startTimestamp);
2113 }
2114 DBFreeStatement(hStmt);
2115 DBConnectionPoolReleaseConnection(hdb);
2116 return;
2117
2118 onFail:
2119 if(hStmt != NULL)
2120 DBFreeStatement(hStmt);
2121 DbgPrintf(4, _T("NetObj::addLocationToHistory(%s [%d]): Failed to add location to history"), m_name, m_id);
2122 DBConnectionPoolReleaseConnection(hdb);
2123 return;
2124 }
2125
2126 /**
2127 * Check if given data table exist
2128 */
2129 bool NetObj::isLocationTableExists(DB_HANDLE hdb)
2130 {
2131 TCHAR table[256];
2132 _sntprintf(table, 256, _T("gps_history_%d"), m_id);
2133 int rc = DBIsTableExist(hdb, table);
2134 if (rc == DBIsTableExist_Failure)
2135 {
2136 _tprintf(_T("WARNING: call to DBIsTableExist(\"%s\") failed\n"), table);
2137 }
2138 return rc != DBIsTableExist_NotFound;
2139 }
2140
2141 /**
2142 * Create table for storing geolocation history for this object
2143 */
2144 bool NetObj::createLocationHistoryTable(DB_HANDLE hdb)
2145 {
2146 TCHAR szQuery[256], szQueryTemplate[256];
2147 MetaDataReadStr(_T("LocationHistory"), szQueryTemplate, 255, _T(""));
2148 _sntprintf(szQuery, 256, szQueryTemplate, m_id);
2149 if (!DBQuery(hdb, szQuery))
2150 return false;
2151
2152 return true;
2153 }
2154
2155 /**
2156 * Set status calculation method
2157 */
2158 void NetObj::setStatusCalculation(int method, int arg1, int arg2, int arg3, int arg4)
2159 {
2160 lockProperties();
2161 m_statusCalcAlg = method;
2162 switch(method)
2163 {
2164 case SA_CALCULATE_SINGLE_THRESHOLD:
2165 m_statusSingleThreshold = arg1;
2166 break;
2167 case SA_CALCULATE_MULTIPLE_THRESHOLDS:
2168 m_statusThresholds[0] = arg1;
2169 m_statusThresholds[1] = arg2;
2170 m_statusThresholds[2] = arg3;
2171 m_statusThresholds[3] = arg4;
2172 break;
2173 default:
2174 break;
2175 }
2176 setModified(MODIFY_COMMON_PROPERTIES);
2177 unlockProperties();
2178 }
2179
2180 /**
2181 * Set status propagation method
2182 */
2183 void NetObj::setStatusPropagation(int method, int arg1, int arg2, int arg3, int arg4)
2184 {
2185 lockProperties();
2186 m_statusPropAlg = method;
2187 switch(method)
2188 {
2189 case SA_PROPAGATE_FIXED:
2190 m_fixedStatus = arg1;
2191 break;
2192 case SA_PROPAGATE_RELATIVE:
2193 m_statusShift = arg1;
2194 break;
2195 case SA_PROPAGATE_TRANSLATED:
2196 m_statusTranslation[0] = arg1;
2197 m_statusTranslation[1] = arg2;
2198 m_statusTranslation[2] = arg3;
2199 m_statusTranslation[3] = arg4;
2200 break;
2201 default:
2202 break;
2203 }
2204 setModified(MODIFY_COMMON_PROPERTIES);
2205 unlockProperties();
2206 }
2207
2208 /**
2209 * Set geographical location
2210 */
2211 void NetObj::setGeoLocation(const GeoLocation& geoLocation)
2212 {
2213 lockProperties();
2214 if (!m_geoLocation.equals(geoLocation))
2215 {
2216 m_geoLocation = geoLocation;
2217 setModified(MODIFY_COMMON_PROPERTIES);
2218 }
2219 unlockProperties();
2220 }
2221
2222 /**
2223 * Enter maintenance mode
2224 */
2225 void NetObj::enterMaintenanceMode()
2226 {
2227 DbgPrintf(4, _T("Entering maintenance mode for object %s [%d] (%s)"), m_name, m_id, getObjectClassName());
2228
2229 lockChildList(false);
2230 for(int i = 0; i < m_childList->size(); i++)
2231 {
2232 NetObj *object = m_childList->get(i);
2233 if (object->getStatus() != STATUS_UNMANAGED)
2234 object->enterMaintenanceMode();
2235 }
2236 unlockChildList();
2237 }
2238
2239 /**
2240 * Leave maintenance mode
2241 */
2242 void NetObj::leaveMaintenanceMode()
2243 {
2244 DbgPrintf(4, _T("Leaving maintenance mode for object %s [%d] (%s)"), m_name, m_id, getObjectClassName());
2245
2246 lockChildList(false);
2247 for(int i = 0; i < m_childList->size(); i++)
2248 {
2249 NetObj *object = m_childList->get(i);
2250 if (object->getStatus() != STATUS_UNMANAGED)
2251 object->leaveMaintenanceMode();
2252 }
2253 unlockChildList();
2254 }
2255
2256 /**
2257 * Set custom attribute
2258 */
2259 void NetObj::setCustomAttribute(const TCHAR *name, const TCHAR *value)
2260 {
2261 lockProperties();
2262 const TCHAR *curr = m_customAttributes.get(name);
2263 if ((curr == NULL) || _tcscmp(curr, value))
2264 {
2265 m_customAttributes.set(name, value);
2266 setModified(MODIFY_CUSTOM_ATTRIBUTES);
2267 }
2268 unlockProperties();
2269 }
2270
2271 /**
2272 * Set custom attribute (value is preallocated)
2273 */
2274 void NetObj::setCustomAttributePV(const TCHAR *name, TCHAR *value)
2275 {
2276 lockProperties();
2277 const TCHAR *curr = m_customAttributes.get(name);
2278 if ((curr == NULL) || _tcscmp(curr, value))
2279 {
2280 m_customAttributes.setPreallocated(_tcsdup(name), value);
2281 setModified(MODIFY_CUSTOM_ATTRIBUTES);
2282 }
2283 else
2284 {
2285 free(value);
2286 }
2287 unlockProperties();
2288 }
2289
2290 /**
2291 * Delete custom attribute
2292 */
2293 void NetObj::deleteCustomAttribute(const TCHAR *name)
2294 {
2295 lockProperties();
2296 if (m_customAttributes.contains(name))
2297 {
2298 m_customAttributes.remove(name);
2299 setModified(MODIFY_CUSTOM_ATTRIBUTES);
2300 }
2301 unlockProperties();
2302 }
2303
2304 /**
2305 * Get custom attribute into buffer
2306 */
2307 TCHAR *NetObj::getCustomAttribute(const TCHAR *name, TCHAR *buffer, size_t size) const
2308 {
2309 TCHAR *result;
2310 lockProperties();
2311 const TCHAR *value = m_customAttributes.get(name);
2312 if (value != NULL)
2313 {
2314 _tcslcpy(buffer, value, size);
2315 result = buffer;
2316 }
2317 else
2318 {
2319 result = NULL;
2320 }
2321 unlockProperties();
2322 return result;
2323 }
2324
2325 /**
2326 * Get copy of custom attribute. Returned value must be freed by caller
2327 */
2328 TCHAR *NetObj::getCustomAttributeCopy(const TCHAR *name) const
2329 {
2330 lockProperties();
2331 const TCHAR *value = m_customAttributes.get(name);
2332 TCHAR *result = _tcsdup_ex(value);
2333 unlockProperties();
2334 return result;
2335 }
2336
2337 /**
2338 * Get custom attribute as NXSL value
2339 */
2340 NXSL_Value *NetObj::getCustomAttributeForNXSL(const TCHAR *name) const
2341 {
2342 NXSL_Value *value = NULL;
2343 lockProperties();
2344 const TCHAR *av = m_customAttributes.get(name);
2345 if (av != NULL)
2346 value = new NXSL_Value(av);
2347 unlockProperties();
2348 return value;
2349 }
2350
2351 /**
2352 * Get all custom attributes as NXSL hash map
2353 */
2354 NXSL_Value *NetObj::getCustomAttributesForNXSL() const
2355 {
2356 NXSL_HashMap *map = new NXSL_HashMap();
2357 lockProperties();
2358 StructArray<KeyValuePair> *attributes = m_customAttributes.toArray();
2359 for(int i = 0; i < attributes->size(); i++)
2360 {
2361 KeyValuePair *p = attributes->get(i);
2362 map->set(p->key, new NXSL_Value((const TCHAR *)p->value));
2363 }
2364 unlockProperties();
2365 delete attributes;
2366 return new NXSL_Value(map);
2367 }
2368
2369 /**
2370 * Create NXSL object for this object
2371 */
2372 NXSL_Value *NetObj::createNXSLObject()
2373 {
2374 return new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, this));
2375 }
2376
2377 /**
2378 * Serialize object to JSON
2379 */
2380 json_t *NetObj::toJson()
2381 {
2382 json_t *root = json_object();
2383 json_object_set_new(root, "id", json_integer(m_id));
2384 json_object_set_new(root, "guid", m_guid.toJson());
2385 json_object_set_new(root, "timestamp", json_integer(m_timestamp));
2386 json_object_set_new(root, "name", json_string_t(m_name));
2387 json_object_set_new(root, "comments", json_string_t(m_comments));
2388 json_object_set_new(root, "status", json_integer(m_status));
2389 json_object_set_new(root, "statusCalcAlg", json_integer(m_statusCalcAlg));
2390 json_object_set_new(root, "statusPropAlg", json_integer(m_statusPropAlg));
2391 json_object_set_new(root, "fixedStatus", json_integer(m_fixedStatus));
2392 json_object_set_new(root, "statusShift", json_integer(m_statusShift));
2393 json_object_set_new(root, "statusTranslation", json_integer_array(m_statusTranslation, 4));
2394 json_object_set_new(root, "statusSingleThreshold", json_integer(m_statusSingleThreshold));
2395 json_object_set_new(root, "statusThresholds", json_integer_array(m_statusThresholds, 4));
2396 json_object_set_new(root, "isSystem", json_boolean(m_isSystem));
2397 json_object_set_new(root, "maintenanceMode", json_boolean(m_maintenanceMode));
2398 json_object_set_new(root, "maintenanceEventId", json_integer(m_maintenanceEventId));
2399 json_object_set_new(root, "image", m_image.toJson());
2400 json_object_set_new(root, "geoLocation", m_geoLocation.toJson());
2401 json_object_set_new(root, "postalAddress", m_postalAddress->toJson());
2402 json_object_set_new(root, "submapId", json_integer(m_submapId));
2403 json_object_set_new(root, "dashboards", m_dashboards->toJson());
2404 json_object_set_new(root, "urls", json_object_array(m_urls));
2405 json_object_set_new(root, "accessList", m_accessList->toJson());
2406 json_object_set_new(root, "inheritAccessRights", json_boolean(m_inheritAccessRights));
2407 json_object_set_new(root, "trustedNodes", (m_trustedNodes != NULL) ? m_trustedNodes->toJson() : json_array());
2408 json_object_set_new(root, "customAttributes", m_customAttributes.toJson());
2409
2410 json_t *children = json_array();
2411 lockChildList(false);
2412 for(int i = 0; i < m_childList->size(); i++)
2413 json_array_append_new(children, json_integer(m_childList->get(i)->getId()));
2414 unlockChildList();
2415 json_object_set_new(root, "children", children);
2416
2417 json_t *parents = json_array();
2418 lockParentList(false);
2419 for(int i = 0; i < m_parentList->size(); i++)
2420 json_array_append_new(parents, json_integer(m_parentList->get(i)->getId()));
2421 unlockParentList();
2422 json_object_set_new(root, "parents", parents);
2423
2424 return root;
2425 }