all pollers converted to single thread pool
[public/netxms.git] / src / server / core / condition.cpp
CommitLineData
e83d726c 1/*
5039dede 2** NetXMS - Network Management System
9fddfb91 3** Copyright (C) 2003-2013 Victor Kirhenshtein
5039dede
AK
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: container.cpp
20**
21**/
22
23#include "nxcore.h"
24
21c9acce
VK
25/**
26 * Default constructor
27 */
28Condition::Condition() : NetObj()
5039dede 29{
9fddfb91
VK
30 m_scriptSource = NULL;
31 m_script = NULL;
32 m_dciList = NULL;
33 m_dciCount = 0;
34 m_sourceObject = 0;
35 m_activeStatus = STATUS_MAJOR;
36 m_inactiveStatus = STATUS_NORMAL;
37 m_isActive = false;
38 m_lastPoll = 0;
39 m_queuedForPolling = false;
40 m_activationEventCode = EVENT_CONDITION_ACTIVATED;
41 m_deactivationEventCode = EVENT_CONDITION_DEACTIVATED;
5039dede
AK
42}
43
21c9acce
VK
44/**
45 * Constructor for new objects
46 */
01152a54 47Condition::Condition(bool hidden) : NetObj()
5039dede 48{
9fddfb91
VK
49 m_scriptSource = NULL;
50 m_script = NULL;
51 m_dciList = NULL;
52 m_dciCount = 0;
53 m_sourceObject = 0;
54 m_activeStatus = STATUS_MAJOR;
55 m_inactiveStatus = STATUS_NORMAL;
01152a54 56 m_isHidden = hidden;
9fddfb91
VK
57 m_isActive = false;
58 m_lastPoll = 0;
59 m_queuedForPolling = false;
60 m_activationEventCode = EVENT_CONDITION_ACTIVATED;
61 m_deactivationEventCode = EVENT_CONDITION_DEACTIVATED;
5039dede
AK
62}
63
9fddfb91
VK
64/**
65 * Destructor
66 */
5039dede
AK
67Condition::~Condition()
68{
9fddfb91
VK
69 safe_free(m_dciList);
70 safe_free(m_scriptSource);
71 delete m_script;
5039dede
AK
72}
73
9fddfb91
VK
74/**
75 * Load object from database
76 */
c42b4551 77BOOL Condition::loadFromDatabase(UINT32 dwId)
5039dede
AK
78{
79 TCHAR szQuery[512];
80 DB_RESULT hResult;
967893bb 81 UINT32 i;
5039dede 82
c42b4551 83 m_id = dwId;
5039dede 84
89135050 85 if (!loadCommonProperties())
5039dede
AK
86 return FALSE;
87
88 // Load properties
89 _sntprintf(szQuery, 512, _T("SELECT activation_event,deactivation_event,")
90 _T("source_object,active_status,inactive_status,")
91 _T("script FROM conditions WHERE id=%d"), dwId);
92 hResult = DBSelect(g_hCoreDB, szQuery);
93 if (hResult == NULL)
94 return FALSE; // Query failed
95
96 if (DBGetNumRows(hResult) == 0)
97 {
98 // No object with given ID in database
99 DBFreeResult(hResult);
100 return FALSE;
101 }
102
9fddfb91
VK
103 m_activationEventCode = DBGetFieldULong(hResult, 0, 0);
104 m_deactivationEventCode = DBGetFieldULong(hResult, 0, 1);
105 m_sourceObject = DBGetFieldULong(hResult, 0, 2);
106 m_activeStatus = DBGetFieldLong(hResult, 0, 3);
107 m_inactiveStatus = DBGetFieldLong(hResult, 0, 4);
108 m_scriptSource = DBGetField(hResult, 0, 5, NULL, 0);
109 DecodeSQLString(m_scriptSource);
e83d726c 110
5039dede
AK
111 DBFreeResult(hResult);
112
113 // Compile script
6b29839d
VK
114 NXSL_Program *p = (NXSL_Program *)NXSLCompile(m_scriptSource, szQuery, 512);
115 if (p != NULL)
116 {
117 m_script = new NXSL_VM(new NXSL_ServerEnv);
118 if (!m_script->load(p))
119 {
c42b4551 120 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, m_script->getErrorText());
6b29839d
VK
121 delete_and_null(m_script);
122 }
123 delete p;
124 }
125 else
126 {
127 m_script = NULL;
c42b4551 128 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, szQuery);
6b29839d 129 }
5039dede
AK
130
131 // Load DCI map
132 _sntprintf(szQuery, 512, _T("SELECT dci_id,node_id,dci_func,num_polls ")
133 _T("FROM cond_dci_map WHERE condition_id=%d ORDER BY sequence_number"), dwId);
134 hResult = DBSelect(g_hCoreDB, szQuery);
135 if (hResult == NULL)
136 return FALSE; // Query failed
137
9fddfb91
VK
138 m_dciCount = DBGetNumRows(hResult);
139 if (m_dciCount > 0)
5039dede 140 {
9fddfb91
VK
141 m_dciList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dciCount);
142 for(i = 0; i < m_dciCount; i++)
5039dede 143 {
9fddfb91
VK
144 m_dciList[i].id = DBGetFieldULong(hResult, i, 0);
145 m_dciList[i].nodeId = DBGetFieldULong(hResult, i, 1);
146 m_dciList[i].function = DBGetFieldLong(hResult, i, 2);
147 m_dciList[i].polls = DBGetFieldLong(hResult, i, 3);
5039dede
AK
148 }
149 }
150 DBFreeResult(hResult);
151
89135050 152 return loadACLFromDB();
5039dede
AK
153}
154
9fddfb91
VK
155/**
156 * Save object to database
157 */
c42b4551 158BOOL Condition::saveToDatabase(DB_HANDLE hdb)
5039dede
AK
159{
160 TCHAR *pszEscScript, *pszQuery;
161 DB_RESULT hResult;
162 BOOL bNewObject = TRUE;
967893bb 163 UINT32 i;
5039dede 164
c42b4551 165 lockProperties();
5039dede 166
89135050 167 saveCommonProperties(hdb);
5039dede 168
9fddfb91 169 pszEscScript = EncodeSQLString(CHECK_NULL_EX(m_scriptSource));
35f836fe
VK
170 size_t qlen = _tcslen(pszEscScript) + 1024;
171 pszQuery = (TCHAR *)malloc(sizeof(TCHAR) * qlen);
5039dede
AK
172
173 // Check for object's existence in database
c42b4551 174 _sntprintf(pszQuery, qlen, _T("SELECT id FROM conditions WHERE id=%d"), m_id);
5039dede
AK
175 hResult = DBSelect(hdb, pszQuery);
176 if (hResult != NULL)
177 {
178 if (DBGetNumRows(hResult) > 0)
179 bNewObject = FALSE;
180 DBFreeResult(hResult);
181 }
182
183 // Form and execute INSERT or UPDATE query
184 if (bNewObject)
185 {
e83d726c 186 _sntprintf(pszQuery, qlen,
35f836fe 187 _T("INSERT INTO conditions (id,activation_event,")
5039dede
AK
188 _T("deactivation_event,source_object,active_status,")
189 _T("inactive_status,script) VALUES (%d,%d,%d,%d,%d,%d,'%s')"),
c42b4551 190 m_id, m_activationEventCode, m_deactivationEventCode,
9fddfb91 191 m_sourceObject, m_activeStatus, m_inactiveStatus, pszEscScript);
5039dede
AK
192 }
193 else
194 {
35f836fe
VK
195 _sntprintf(pszQuery, qlen,
196 _T("UPDATE conditions SET activation_event=%d,")
5039dede
AK
197 _T("deactivation_event=%d,source_object=%d,active_status=%d,")
198 _T("inactive_status=%d,script='%s' WHERE id=%d"),
9fddfb91 199 m_activationEventCode, m_deactivationEventCode, m_sourceObject,
c42b4551 200 m_activeStatus, m_inactiveStatus, pszEscScript, m_id);
5039dede
AK
201 }
202 free(pszEscScript);
203 DBQuery(hdb, pszQuery);
204
205 // Save DCI mapping
c42b4551 206 _sntprintf(pszQuery, qlen, _T("DELETE FROM cond_dci_map WHERE condition_id=%d"), m_id);
5039dede 207 DBQuery(hdb, pszQuery);
9fddfb91 208 for(i = 0; i < m_dciCount; i++)
5039dede 209 {
35f836fe
VK
210 _sntprintf(pszQuery, qlen, _T("INSERT INTO cond_dci_map (condition_id,sequence_number,dci_id,node_id,")
211 _T("dci_func,num_polls) VALUES (%d,%d,%d,%d,%d,%d)"),
c42b4551 212 m_id, i, m_dciList[i].id, m_dciList[i].nodeId,
9fddfb91 213 m_dciList[i].function, m_dciList[i].polls);
5039dede
AK
214 DBQuery(hdb, pszQuery);
215 }
216 free(pszQuery);
217
218 // Save access list
89135050 219 saveACLToDB(hdb);
5039dede
AK
220
221 // Unlock object and clear modification flag
01152a54 222 m_isModified = false;
c42b4551 223 unlockProperties();
5039dede
AK
224 return TRUE;
225}
226
22ee6d97
VK
227/**
228 * Delete object from database
229 */
c42b4551 230bool Condition::deleteFromDatabase(DB_HANDLE hdb)
5039dede 231{
c42b4551 232 bool success = NetObj::deleteFromDatabase(hdb);
22ee6d97
VK
233 if (success)
234 success = executeQueryOnObject(hdb, _T("DELETE FROM conditions WHERE id=?"));
235 if (success)
236 success = executeQueryOnObject(hdb, _T("DELETE FROM cond_dci_map WHERE condition_id=?"));
237 return success;
5039dede
AK
238}
239
22ee6d97
VK
240/**
241 * Create NXCP message from object
242 */
8fe90adb 243void Condition::fillMessageInternal(NXCPMessage *pMsg)
5039dede 244{
8fe90adb 245 NetObj::fillMessageInternal(pMsg);
e83d726c 246
b368969c
VK
247 pMsg->setField(VID_SCRIPT, CHECK_NULL_EX(m_scriptSource));
248 pMsg->setField(VID_ACTIVATION_EVENT, m_activationEventCode);
249 pMsg->setField(VID_DEACTIVATION_EVENT, m_deactivationEventCode);
250 pMsg->setField(VID_SOURCE_OBJECT, m_sourceObject);
251 pMsg->setField(VID_ACTIVE_STATUS, (WORD)m_activeStatus);
252 pMsg->setField(VID_INACTIVE_STATUS, (WORD)m_inactiveStatus);
253 pMsg->setField(VID_NUM_ITEMS, m_dciCount);
8fe90adb 254 for(UINT32 i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
5039dede 255 {
b368969c
VK
256 pMsg->setField(dwId++, m_dciList[i].id);
257 pMsg->setField(dwId++, m_dciList[i].nodeId);
258 pMsg->setField(dwId++, (WORD)m_dciList[i].function);
259 pMsg->setField(dwId++, (WORD)m_dciList[i].polls);
260 pMsg->setField(dwId++, (WORD)GetDCObjectType(m_dciList[i].nodeId, m_dciList[i].id));
9fddfb91 261 dwId += 5;
5039dede
AK
262 }
263}
264
9fddfb91
VK
265/**
266 * Modify object from NXCP message
267 */
8fe90adb 268UINT32 Condition::modifyFromMessageInternal(NXCPMessage *pRequest)
5039dede 269{
967893bb 270 UINT32 i, dwId;
5039dede 271 NetObj *pObject;
5039dede 272
5039dede 273 // Change script
5c44534b 274 if (pRequest->isFieldExist(VID_SCRIPT))
5039dede
AK
275 {
276 TCHAR szError[1024];
277
9fddfb91
VK
278 safe_free(m_scriptSource);
279 delete m_script;
b368969c 280 m_scriptSource = pRequest->getFieldAsString(VID_SCRIPT);
6b29839d
VK
281 NXSL_Program *p = (NXSL_Program *)NXSLCompile(m_scriptSource, szError, 1024);
282 if (p != NULL)
283 {
284 m_script = new NXSL_VM(new NXSL_ServerEnv);
285 if (!m_script->load(p))
286 {
c42b4551 287 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, m_script->getErrorText());
6b29839d
VK
288 delete_and_null(m_script);
289 }
290 delete p;
291 }
292 else
293 {
294 m_script = NULL;
c42b4551 295 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, szError);
6b29839d 296 }
5039dede
AK
297 }
298
299 // Change activation event
5c44534b 300 if (pRequest->isFieldExist(VID_ACTIVATION_EVENT))
b368969c 301 m_activationEventCode = pRequest->getFieldAsUInt32(VID_ACTIVATION_EVENT);
5039dede
AK
302
303 // Change deactivation event
5c44534b 304 if (pRequest->isFieldExist(VID_DEACTIVATION_EVENT))
b368969c 305 m_deactivationEventCode = pRequest->getFieldAsUInt32(VID_DEACTIVATION_EVENT);
5039dede
AK
306
307 // Change source object
5c44534b 308 if (pRequest->isFieldExist(VID_SOURCE_OBJECT))
b368969c 309 m_sourceObject = pRequest->getFieldAsUInt32(VID_SOURCE_OBJECT);
5039dede
AK
310
311 // Change active status
5c44534b 312 if (pRequest->isFieldExist(VID_ACTIVE_STATUS))
b368969c 313 m_activeStatus = pRequest->getFieldAsUInt16(VID_ACTIVE_STATUS);
5039dede
AK
314
315 // Change inactive status
5c44534b 316 if (pRequest->isFieldExist(VID_INACTIVE_STATUS))
b368969c 317 m_inactiveStatus = pRequest->getFieldAsUInt16(VID_INACTIVE_STATUS);
5039dede
AK
318
319 // Change DCI list
5c44534b 320 if (pRequest->isFieldExist(VID_NUM_ITEMS))
5039dede 321 {
9fddfb91 322 safe_free(m_dciList);
b368969c 323 m_dciCount = pRequest->getFieldAsUInt32(VID_NUM_ITEMS);
9fddfb91 324 if (m_dciCount > 0)
5039dede 325 {
9fddfb91
VK
326 m_dciList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dciCount);
327 for(i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
5039dede 328 {
b368969c
VK
329 m_dciList[i].id = pRequest->getFieldAsUInt32(dwId++);
330 m_dciList[i].nodeId = pRequest->getFieldAsUInt32(dwId++);
331 m_dciList[i].function = pRequest->getFieldAsUInt16(dwId++);
332 m_dciList[i].polls = pRequest->getFieldAsUInt16(dwId++);
5039dede
AK
333 dwId += 6;
334 }
335
336 // Update cache size of DCIs
9fddfb91 337 for(i = 0; i < m_dciCount; i++)
5039dede 338 {
9fddfb91 339 pObject = FindObjectById(m_dciList[i].nodeId);
5039dede
AK
340 if (pObject != NULL)
341 {
c42b4551 342 if (pObject->getObjectClass() == OBJECT_NODE)
5039dede 343 {
9fddfb91 344 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
16d6f798 345 if ((pItem != NULL) && (pItem->getType() == DCO_TYPE_ITEM))
5039dede 346 {
c42b4551 347 ((DCItem *)pItem)->updateCacheSize(m_id);
5039dede
AK
348 }
349 }
350 }
351 }
352 }
353 else
354 {
9fddfb91 355 m_dciList = NULL;
5039dede
AK
356 }
357 }
358
8fe90adb 359 return NetObj::modifyFromMessageInternal(pRequest);
5039dede
AK
360}
361
21c9acce
VK
362/**
363 * Lock for polling
364 */
9fddfb91 365void Condition::lockForPoll()
5039dede 366{
9fddfb91 367 m_queuedForPolling = TRUE;
5039dede
AK
368}
369
21c9acce 370/**
208d7427 371 * Poller entry point
21c9acce 372 */
208d7427 373void Condition::doPoll(PollerInfo *poller)
5039dede 374{
208d7427
VK
375 poller->startExecution();
376 check();
c42b4551 377 lockProperties();
9fddfb91
VK
378 m_queuedForPolling = FALSE;
379 m_lastPoll = time(NULL);
c42b4551 380 unlockProperties();
208d7427 381 delete poller;
5039dede
AK
382}
383
21c9acce
VK
384/**
385 * Check condition
386 */
3f7c0fe4 387void Condition::check()
5039dede 388{
5039dede
AK
389 NXSL_Value **ppValueList, *pValue;
390 NetObj *pObject;
967893bb 391 UINT32 i, dwNumValues;
5039dede
AK
392 int iOldStatus = m_iStatus;
393
9fddfb91 394 if ((m_script == NULL) || (m_iStatus == STATUS_UNMANAGED))
5039dede
AK
395 return;
396
c42b4551 397 lockProperties();
9fddfb91
VK
398 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * m_dciCount);
399 memset(ppValueList, 0, sizeof(NXSL_Value *) * m_dciCount);
400 for(i = 0; i < m_dciCount; i++)
5039dede 401 {
9fddfb91 402 pObject = FindObjectById(m_dciList[i].nodeId);
5039dede
AK
403 if (pObject != NULL)
404 {
c42b4551 405 if (pObject->getObjectClass() == OBJECT_NODE)
5039dede 406 {
9fddfb91
VK
407 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
408 if (pItem != NULL)
5039dede 409 {
9fddfb91
VK
410 if (pItem->getType() == DCO_TYPE_ITEM)
411 {
412 ppValueList[i] = ((DCItem *)pItem)->getValueForNXSL(m_dciList[i].function, m_dciList[i].polls);
413 }
414 else if (pItem->getType() == DCO_TYPE_TABLE)
415 {
416 Table *t = ((DCTable *)pItem)->getLastValue();
417 if (t != NULL)
418 {
419 ppValueList[i] = new NXSL_Value(new NXSL_Object(&g_nxslTableClass, t));
420 }
421 }
5039dede
AK
422 }
423 }
424 }
425 if (ppValueList[i] == NULL)
426 ppValueList[i] = new NXSL_Value;
427 }
9fddfb91 428 dwNumValues = m_dciCount;
c42b4551 429 unlockProperties();
5039dede 430
22aa5156
VK
431 // Create array from values
432 NXSL_Array *array = new NXSL_Array;
433 for(i = 0; i < dwNumValues; i++)
434 {
435 array->set(i + 1, new NXSL_Value(ppValueList[i]));
436 }
9fddfb91 437 m_script->setGlobalVariable(_T("$values"), new NXSL_Value(array));
22aa5156 438
c42b4551 439 DbgPrintf(6, _T("Running evaluation script for condition %d \"%s\""), m_id, m_name);
6b29839d 440 if (m_script->run(dwNumValues, ppValueList))
5039dede 441 {
9fddfb91 442 pValue = m_script->getResult();
bb5365ed 443 if (pValue->getValueAsInt32() == 0)
5039dede 444 {
9fddfb91 445 if (m_isActive)
5039dede
AK
446 {
447 // Deactivate condition
c42b4551 448 lockProperties();
9fddfb91
VK
449 m_iStatus = m_inactiveStatus;
450 m_isActive = FALSE;
c42b4551
VK
451 setModified();
452 unlockProperties();
5039dede 453
9fddfb91
VK
454 PostEvent(m_deactivationEventCode,
455 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
c42b4551 456 "dsdd", m_id, m_name, iOldStatus, m_iStatus);
5039dede
AK
457
458 DbgPrintf(6, _T("Condition %d \"%s\" deactivated"),
c42b4551 459 m_id, m_name);
5039dede
AK
460 }
461 else
462 {
463 DbgPrintf(6, _T("Condition %d \"%s\" still inactive"),
c42b4551
VK
464 m_id, m_name);
465 lockProperties();
9fddfb91 466 if (m_iStatus != m_inactiveStatus)
5039dede 467 {
9fddfb91 468 m_iStatus = m_inactiveStatus;
c42b4551 469 setModified();
5039dede 470 }
c42b4551 471 unlockProperties();
5039dede
AK
472 }
473 }
474 else
475 {
9fddfb91 476 if (!m_isActive)
5039dede
AK
477 {
478 // Activate condition
c42b4551 479 lockProperties();
9fddfb91
VK
480 m_iStatus = m_activeStatus;
481 m_isActive = TRUE;
c42b4551
VK
482 setModified();
483 unlockProperties();
5039dede 484
9fddfb91
VK
485 PostEvent(m_activationEventCode,
486 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
c42b4551 487 "dsdd", m_id, m_name, iOldStatus, m_iStatus);
5039dede
AK
488
489 DbgPrintf(6, _T("Condition %d \"%s\" activated"),
c42b4551 490 m_id, m_name);
5039dede
AK
491 }
492 else
493 {
494 DbgPrintf(6, _T("Condition %d \"%s\" still active"),
c42b4551
VK
495 m_id, m_name);
496 lockProperties();
9fddfb91 497 if (m_iStatus != m_activeStatus)
5039dede 498 {
9fddfb91 499 m_iStatus = m_activeStatus;
c42b4551 500 setModified();
5039dede 501 }
c42b4551 502 unlockProperties();
5039dede
AK
503 }
504 }
505 }
506 else
507 {
508 nxlog_write(MSG_COND_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE,
c42b4551 509 "dss", m_id, m_name, m_script->getErrorText());
5039dede 510
c42b4551 511 lockProperties();
5039dede
AK
512 if (m_iStatus != STATUS_UNKNOWN)
513 {
514 m_iStatus = STATUS_UNKNOWN;
c42b4551 515 setModified();
5039dede 516 }
c42b4551 517 unlockProperties();
5039dede
AK
518 }
519 free(ppValueList);
520
521 // Cause parent object(s) to recalculate it's status
522 if (iOldStatus != m_iStatus)
523 {
524 LockParentList(FALSE);
525 for(i = 0; i < m_dwParentCount; i++)
27f9598d 526 m_pParentList[i]->calculateCompoundStatus();
5039dede
AK
527 UnlockParentList();
528 }
529}
530
9fddfb91
VK
531/**
532 * Determine DCI cache size required by condition object
533 */
534int Condition::getCacheSizeForDCI(UINT32 itemId, bool noLock)
5039dede 535{
967893bb 536 UINT32 i;
5039dede
AK
537 int nSize = 0;
538
9fddfb91 539 if (!noLock)
c42b4551 540 lockProperties();
9fddfb91 541 for(i = 0; i < m_dciCount; i++)
5039dede 542 {
9fddfb91 543 if (m_dciList[i].id == itemId)
5039dede 544 {
9fddfb91 545 switch(m_dciList[i].function)
5039dede
AK
546 {
547 case F_LAST:
548 nSize = 1;
549 break;
550 case F_DIFF:
551 nSize = 2;
552 break;
553 default:
9fddfb91 554 nSize = m_dciList[i].polls;
5039dede
AK
555 break;
556 }
557 break;
558 }
559 }
9fddfb91 560 if (!noLock)
c42b4551 561 unlockProperties();
5039dede
AK
562 return nSize;
563}