VirtualBox

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

Last change on this file since 44248 was 44099, checked in by vboxsync, 12 years ago

Need coffee first.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 44099 2012-12-12 08:35:51Z 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 AssertPtrReturn(pCache, NULL);
42 AssertPtrReturn(pszName, NULL);
43
44 /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
45 * map.
46 * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
47 * implementation (AVL tree). However, this is not important at the
48 * moment. */
49 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt, pNode = NULL;
50 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
51 {
52 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
53 {
54 if (strcmp(pNodeIt->pszName, pszName) == 0)
55 {
56 pNode = pNodeIt;
57 break;
58 }
59 }
60 RTCritSectLeave(&pCache->CritSect);
61 }
62 return pNode;
63}
64
65
66/** @todo Docs */
67PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
68{
69 AssertPtrReturn(pCache, NULL);
70 AssertPtrReturn(pszName, NULL);
71
72 PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
73 if (pNode)
74 {
75 pNode->pszName = RTStrDup(pszName);
76 if (!pNode->pszName)
77 {
78 RTMemFree(pNode);
79 return NULL;
80 }
81 pNode->pszValue = NULL;
82 pNode->fFlags = 0;
83 pNode->pszValueReset = NULL;
84
85 int rc = RTCritSectEnter(&pCache->CritSect);
86 if (RT_SUCCESS(rc))
87 {
88 /*rc =*/ RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
89 rc = RTCritSectLeave(&pCache->CritSect);
90 }
91 }
92 return pNode;
93}
94
95
96/** @todo Docs */
97int vboxServicePropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
98{
99 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
100
101 int rc;
102 if (pszValueFormat != NULL)
103 {
104 va_list va;
105 va_start(va, pszValueFormat);
106
107 char *pszValue;
108 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
109 {
110 if (fFlags & VBOXSERVICEPROPCACHEFLAG_TRANSIENT)
111 {
112 /*
113 * Because a value can be temporary we have to make sure it also
114 * gets deleted when the property cache did not have the chance to
115 * gracefully clean it up (due to a hard VM reset etc), so set this
116 * guest property using the TRANSRESET flag..
117 */
118 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSRESET");
119 if (rc == VERR_PARSE_ERROR)
120 {
121 /* Host does not support the "TRANSRESET" flag, so only
122 * use the "TRANSIENT" flag -- better than nothing :-). */
123 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSIENT");
124 /** @todo r=bird: Remember that the host doesn't support
125 * this. */
126 }
127 }
128 else
129 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue /* No transient flags set */);
130 RTStrFree(pszValue);
131 }
132 else
133 rc = VERR_NO_MEMORY;
134 va_end(va);
135 }
136 else
137 {
138 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
139 }
140 return rc;
141}
142
143
144/**
145 * Creates a property cache.
146 *
147 * @returns IPRT status code.
148 * @param pCache Pointer to the cache.
149 * @param uClientId The HGCM handle of to the guest property service.
150 */
151int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
152{
153 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
154 /** @todo Prevent init the cache twice!
155 * r=bird: Use a magic. */
156 RTListInit(&pCache->NodeHead);
157 pCache->uClientID = uClientId;
158 return RTCritSectInit(&pCache->CritSect);
159}
160
161
162/**
163 * Updates a cache entry without submitting any changes to the host.
164 * This is handy for defining default values/flags.
165 *
166 * @returns VBox status code.
167 *
168 * @param pCache The property cache.
169 * @param pszName The property name.
170 * @param fFlags The property flags to set.
171 * @param pszValueReset The property reset value.
172 */
173int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache,
174 const char *pszName, uint32_t fFlags, const char *pszValueReset)
175{
176 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
177 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
178 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
179 if (pNode == NULL)
180 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
181
182 int rc;
183 if (pNode != NULL)
184 {
185 rc = RTCritSectEnter(&pCache->CritSect);
186 if (RT_SUCCESS(rc))
187 {
188 pNode->fFlags = fFlags;
189 if (pszValueReset)
190 {
191 if (pNode->pszValueReset)
192 RTStrFree(pNode->pszValueReset);
193 pNode->pszValueReset = RTStrDup(pszValueReset);
194 AssertPtr(pNode->pszValueReset);
195 }
196 rc = RTCritSectLeave(&pCache->CritSect);
197 }
198 }
199 else
200 rc = VERR_NO_MEMORY;
201 return rc;
202}
203
204
205/**
206 * Updates the local guest property cache and writes it to HGCM if outdated.
207 *
208 * @returns VBox status code.
209 *
210 * @param pCache The property cache.
211 * @param pszName The property name.
212 * @param pszValueFormat The property format string. If this is NULL then
213 * the property will be deleted (if possible).
214 * @param ... Format arguments.
215 */
216int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
217{
218 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
219 Assert(pCache->uClientID);
220 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
221
222 /*
223 * Format the value first.
224 */
225 char *pszValue = NULL;
226 if (pszValueFormat)
227 {
228 va_list va;
229 va_start(va, pszValueFormat);
230 RTStrAPrintfV(&pszValue, pszValueFormat, va);
231 va_end(va);
232 if (!pszValue)
233 return VERR_NO_STR_MEMORY;
234 }
235
236 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
237
238 /* Lock the cache. */
239 int rc = RTCritSectEnter(&pCache->CritSect);
240 if (RT_SUCCESS(rc))
241 {
242 if (pNode == NULL)
243 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
244
245 AssertPtr(pNode);
246 if (pszValue) /* Do we have a value to check for? */
247 {
248 bool fUpdate = false;
249 /* Always update this property, no matter what? */
250 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE)
251 fUpdate = true;
252 /* Did the value change so we have to update? */
253 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
254 fUpdate = true;
255 /* No value stored at the moment but we have a value now? */
256 else if (pNode->pszValue == NULL)
257 fUpdate = true;
258
259 if (fUpdate)
260 {
261 /* Write the update. */
262 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
263 VBoxServiceVerbose(4, "PropCache %p: Written \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n",
264 pCache, pNode->pszName, pszValue, pNode->fFlags, rc);
265 RTStrFree(pNode->pszValue);
266 pNode->pszValue = RTStrDup(pszValue);
267 if (!pNode->pszValue)
268 rc = VERR_NO_MEMORY;
269 }
270 else
271 rc = VINF_NO_CHANGE; /* No update needed. */
272 }
273 else
274 {
275 /* No value specified. Deletion (or no action required). */
276 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
277 {
278 /* Delete property (but do not remove from cache) if not deleted yet. */
279 RTStrFree(pNode->pszValue);
280 pNode->pszValue = NULL;
281 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
282 0, /* Flags */ NULL /* Value */);
283 VBoxServiceVerbose(4, "PropCache %p: Deleted \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n",
284 pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc);
285 }
286 else
287 rc = VINF_NO_CHANGE; /* No update needed. */
288 }
289
290 /* Release cache. */
291 RTCritSectLeave(&pCache->CritSect);
292 }
293
294 /* Delete temp stuff. */
295 RTStrFree(pszValue);
296 return rc;
297}
298
299
300/**
301 * Updates all cache values which are matching the specified path.
302 *
303 * @returns VBox status code.
304 *
305 * @param pCache The property cache.
306 * @param pszValue The value to set. A NULL will delete the value.
307 * @param fFlags Flags to set.
308 * @param pszPathFormat The path format string. May not be null and has
309 * to be an absolute path.
310 * @param ... Format arguments.
311 */
312int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags, const char *pszPathFormat, ...)
313{
314 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
315 AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER);
316
317 int rc = VERR_NOT_FOUND;
318 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
319 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
320 {
321 /*
322 * Format the value first.
323 */
324 char *pszPath = NULL;
325 va_list va;
326 va_start(va, pszPathFormat);
327 RTStrAPrintfV(&pszPath, pszPathFormat, va);
328 va_end(va);
329 if (!pszPath)
330 {
331 rc = VERR_NO_STR_MEMORY;
332 }
333 else
334 {
335 /* Iterate through all nodes and compare their paths. */
336 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
337 {
338 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
339 {
340 /** @todo Use some internal function to update the node directly, this is slow atm. */
341 rc = VBoxServicePropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
342 }
343 if (RT_FAILURE(rc))
344 break;
345 }
346 RTStrFree(pszPath);
347 }
348 RTCritSectLeave(&pCache->CritSect);
349 }
350 return rc;
351}
352
353
354/**
355 * Flushes the cache by writing every item regardless of its state.
356 *
357 * @param pCache The property cache.
358 */
359int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
360{
361 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
362
363 int rc = VINF_SUCCESS;
364 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
365 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
366 {
367 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
368 {
369 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNodeIt->pszName,
370 pNodeIt->fFlags, pNodeIt->pszValue);
371 if (RT_FAILURE(rc))
372 break;
373 }
374 RTCritSectLeave(&pCache->CritSect);
375 }
376 return rc;
377}
378
379
380/**
381 * Reset all temporary properties and destroy the cache.
382 *
383 * @param pCache The property cache.
384 */
385void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
386{
387 AssertPtrReturnVoid(pCache);
388 Assert(pCache->uClientID);
389
390 /* Lock the cache. */
391 int rc = RTCritSectEnter(&pCache->CritSect);
392 if (RT_SUCCESS(rc))
393 {
394 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
395 while (pNode)
396 {
397 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
398 ? NULL :
399 RTListNodeGetNext(&pNode->NodeSucc,
400 VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
401 RTListNodeRemove(&pNode->NodeSucc);
402
403 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY)
404 {
405 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
406 pNode->fFlags, pNode->pszValueReset);
407 }
408
409 AssertPtr(pNode->pszName);
410 RTStrFree(pNode->pszName);
411 RTStrFree(pNode->pszValue);
412 RTStrFree(pNode->pszValueReset);
413 pNode->fFlags = 0;
414
415 RTMemFree(pNode);
416
417 pNode = pNext;
418 }
419 RTCritSectLeave(&pCache->CritSect);
420 }
421
422 /* Destroy critical section. */
423 RTCritSectDelete(&pCache->CritSect);
424}
425
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