VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp@ 34079

Last change on this file since 34079 was 31934, checked in by vboxsync, 14 years ago

VBoxService/PropCache: Fixed memory leak on destruction.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 31934 2010-08-24 16:11:16Z vboxsync $ */
2/** @file
3 * VBoxServicePropCache - Guest property cache.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/list.h>
24#include <iprt/mem.h>
25#include <iprt/string.h>
26
27#include <VBox/VBoxGuestLib.h>
28#include "VBoxServiceInternal.h"
29#include "VBoxServiceUtils.h"
30#include "VBoxServicePropCache.h"
31
32
33/** Internal functions, not for public use. */
34PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t uFlags);
35PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName);
36
37
38/** @todo Docs */
39PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t uFlags)
40{
41 AssertPtr(pCache);
42 AssertPtr(pszName);
43 /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
44 * map.
45 * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
46 * implementation (AVL tree). However, this is not important at the
47 * moment. */
48 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt, pNode = NULL;
49 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
50 {
51 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
52 {
53 if (strcmp(pNodeIt->pszName, pszName) == 0)
54 {
55 pNode = pNodeIt;
56 break;
57 }
58 }
59 RTCritSectLeave(&pCache->CritSect);
60 }
61 return pNode;
62}
63
64
65/** @todo Docs */
66PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
67{
68 AssertPtr(pszName);
69 PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
70 if (pNode)
71 {
72 pNode->pszName = RTStrDup(pszName);
73 pNode->pszValue = NULL;
74 pNode->fFlags = 0;
75 pNode->pszValueReset = NULL;
76
77 int rc = RTCritSectEnter(&pCache->CritSect);
78 if (RT_SUCCESS(rc))
79 {
80 /*rc =*/ RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
81 rc = RTCritSectLeave(&pCache->CritSect);
82 }
83 }
84 return pNode;
85}
86
87
88/** @todo Docs */
89int vboxServicePropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
90{
91 AssertPtr(pszName);
92 int rc;
93 if (pszValueFormat != NULL)
94 {
95 va_list va;
96 va_start(va, pszValueFormat);
97
98 char *pszValue;
99 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
100 {
101 if (fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY)
102 {
103 /*
104 * Because a value can be temporary we have to make sure it also
105 * gets deleted when the property cache did not have the chance to
106 * gracefully clean it up (due to a hard VM reset etc), so set this
107 * guest property using the TRANSIENT flag.
108 */
109 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSIENT");
110 }
111 else
112 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
113 RTStrFree(pszValue);
114 }
115 else
116 rc = VERR_NO_MEMORY;
117 va_end(va);
118 }
119 else
120 {
121 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
122 }
123 return rc;
124}
125
126
127/**
128 * Creates a property cache.
129 *
130 * @returns IPRT status code.
131 * @param pCache Pointer to the cache.
132 * @param uClientId The HGCM handle of to the guest property service.
133 */
134int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
135{
136 AssertPtr(pCache);
137 /** @todo Prevent init the cache twice!
138 * r=bird: Use a magic. */
139 RTListInit(&pCache->NodeHead);
140 pCache->uClientID = uClientId;
141 return RTCritSectInit(&pCache->CritSect);
142}
143
144
145/**
146 * Updates a cache entry without submitting any changes to the host.
147 * This is handy for defining default values/flags.
148 *
149 * @returns VBox status code.
150 *
151 * @param pCache The property cache.
152 * @param pszName The property name.
153 * @param fFlags The property flags to set.
154 * @param pszValueReset The property reset value.
155 */
156int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache,
157 const char *pszName, uint32_t fFlags, const char *pszValueReset)
158{
159 AssertPtr(pCache);
160 AssertPtr(pszName);
161 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
162 if (pNode == NULL)
163 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
164
165 int rc;
166 if (pNode != NULL)
167 {
168 rc = RTCritSectEnter(&pCache->CritSect);
169 if (RT_SUCCESS(rc))
170 {
171 pNode->fFlags = fFlags;
172 if (pszValueReset)
173 {
174 if (pNode->pszValueReset)
175 RTStrFree(pNode->pszValueReset);
176 pNode->pszValueReset = RTStrDup(pszValueReset);
177 }
178 rc = RTCritSectLeave(&pCache->CritSect);
179 }
180 }
181 else
182 rc = VERR_NO_MEMORY;
183 return rc;
184}
185
186
187/**
188 * Updates the local guest property cache and writes it to HGCM if outdated.
189 *
190 * @returns VBox status code.
191 *
192 * @param pCache The property cache.
193 * @param pszName The property name.
194 * @param pszValueFormat The property format string. If this is NULL then
195 * the property will be deleted (if possible).
196 * @param ... Format arguments.
197 */
198int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
199{
200 AssertPtr(pCache);
201 Assert(pCache->uClientID);
202 AssertPtr(pszName);
203
204 /*
205 * Format the value first.
206 */
207 char *pszValue = NULL;
208 if (pszValueFormat)
209 {
210 va_list va;
211 va_start(va, pszValueFormat);
212 RTStrAPrintfV(&pszValue, pszValueFormat, va);
213 va_end(va);
214 if (!pszValue)
215 return VERR_NO_STR_MEMORY;
216 }
217
218 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
219
220 /* Lock the cache. */
221 int rc = RTCritSectEnter(&pCache->CritSect);
222 if (RT_SUCCESS(rc))
223 {
224 if (pNode == NULL)
225 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
226
227 AssertPtr(pNode);
228 if (pszValue) /* Do we have a value to check for? */
229 {
230 bool fUpdate = false;
231 /* Always update this property, no matter what? */
232 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE)
233 fUpdate = true;
234 /* Did the value change so we have to update? */
235 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
236 fUpdate = true;
237 /* No value stored at the moment but we have a value now? */
238 else if (pNode->pszValue == NULL)
239 fUpdate = true;
240
241 if (fUpdate)
242 {
243 /* Write the update. */
244 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
245 RTStrFree(pNode->pszValue);
246 pNode->pszValue = RTStrDup(pszValue);
247 }
248 else
249 rc = VINF_NO_CHANGE; /* No update needed. */
250 }
251 else
252 {
253 /* No value specified. Deletion (or no action required). */
254 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
255 {
256 /* Delete property (but do not remove from cache) if not deleted yet. */
257 RTStrFree(pNode->pszValue);
258 pNode->pszValue = NULL;
259 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
260 0, /* Flags */ NULL /* Value */);
261 }
262 else
263 rc = VINF_NO_CHANGE; /* No update needed. */
264 }
265
266 /* Release cache. */
267 RTCritSectLeave(&pCache->CritSect);
268 }
269
270 /* Delete temp stuff. */
271 RTStrFree(pszValue);
272 return rc;
273}
274
275
276/**
277 * Updates all cache values which are matching the specified path.
278 *
279 * @returns VBox status code.
280 *
281 * @param pCache The property cache.
282 * @param pszValue The value to set. A NULL will delete the value.
283 * @param fFlags Flags to set.
284 * @param pszPathFormat The path format string. May not be null and has
285 * to be an absolute path.
286 * @param ... Format arguments.
287 */
288int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags, const char *pszPathFormat, ...)
289{
290 AssertPtr(pCache);
291 AssertPtr(pszPathFormat);
292 int rc = VERR_NOT_FOUND;
293 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
294 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
295 {
296 /*
297 * Format the value first.
298 */
299 char *pszPath = NULL;
300 va_list va;
301 va_start(va, pszPathFormat);
302 RTStrAPrintfV(&pszPath, pszPathFormat, va);
303 va_end(va);
304 if (!pszPath)
305 {
306 rc = VERR_NO_STR_MEMORY;
307 }
308 else
309 {
310 /* Iterate through all nodes and compare their paths. */
311 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
312 {
313 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
314 {
315 /** @todo Use some internal function to update the node directly, this is slow atm. */
316 rc = VBoxServicePropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
317 }
318 if (RT_FAILURE(rc))
319 break;
320 }
321 RTStrFree(pszPath);
322 }
323 RTCritSectLeave(&pCache->CritSect);
324 }
325 return rc;
326}
327
328
329/**
330 * Flushes the cache by writing every item regardless of its state.
331 *
332 * @param pCache The property cache.
333 */
334int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
335{
336 AssertPtr(pCache);
337 int rc = VINF_SUCCESS;
338 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
339 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
340 {
341 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
342 {
343 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNodeIt->pszName,
344 pNodeIt->fFlags, pNodeIt->pszValue);
345 if (RT_FAILURE(rc))
346 break;
347 }
348 RTCritSectLeave(&pCache->CritSect);
349 }
350 return rc;
351}
352
353
354/**
355 * Reset all temporary properties and destroy the cache.
356 *
357 * @param pCache The property cache.
358 */
359void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
360{
361 AssertPtr(pCache);
362 Assert(pCache->uClientID);
363
364 /* Lock the cache. */
365 int rc = RTCritSectEnter(&pCache->CritSect);
366 if (RT_SUCCESS(rc))
367 {
368 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListNodeGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
369 while (pNode)
370 {
371 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
372 ? NULL :
373 RTListNodeGetNext(&pNode->NodeSucc,
374 VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
375 RTListNodeRemove(&pNode->NodeSucc);
376
377 /*
378 * When destroying the cache and we have a temporary value, remove the
379 * (eventually) set TRANSIENT flag from it so that it doesn't get deleted
380 * by the host side in order to put the actual reset value in it.
381 */
382 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY)
383 vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, 0 /* Flags, clear all */, pNode->pszValueReset);
384
385 AssertPtr(pNode->pszName);
386 RTStrFree(pNode->pszName);
387 RTStrFree(pNode->pszValue);
388 RTStrFree(pNode->pszValueReset);
389 pNode->fFlags = 0;
390
391 RTMemFree(pNode);
392
393 pNode = pNext;
394 }
395 RTCritSectLeave(&pCache->CritSect);
396 }
397
398 /* Destroy critical section. */
399 RTCritSectDelete(&pCache->CritSect);
400}
401
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette