VirtualBox

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

Last change on this file since 53517 was 45874, checked in by vboxsync, 12 years ago

VBoxService: More logging for property cache / user enumeration.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 45874 2013-05-02 12:16:42Z vboxsync $ */
2/** @file
3 * VBoxServicePropCache - Guest property cache.
4 */
5
6/*
7 * Copyright (C) 2010-2012 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 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
220
221 Assert(pCache->uClientID);
222
223 /*
224 * Format the value first.
225 */
226 char *pszValue = NULL;
227 if (pszValueFormat)
228 {
229 va_list va;
230 va_start(va, pszValueFormat);
231 RTStrAPrintfV(&pszValue, pszValueFormat, va);
232 va_end(va);
233 if (!pszValue)
234 return VERR_NO_STR_MEMORY;
235 }
236
237 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
238
239 /* Lock the cache. */
240 int rc = RTCritSectEnter(&pCache->CritSect);
241 if (RT_SUCCESS(rc))
242 {
243 if (pNode == NULL)
244 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
245
246 AssertPtr(pNode);
247 if (pszValue) /* Do we have a value to check for? */
248 {
249 bool fUpdate = false;
250 /* Always update this property, no matter what? */
251 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE)
252 fUpdate = true;
253 /* Did the value change so we have to update? */
254 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
255 fUpdate = true;
256 /* No value stored at the moment but we have a value now? */
257 else if (pNode->pszValue == NULL)
258 fUpdate = true;
259
260 if (fUpdate)
261 {
262 /* Write the update. */
263 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
264 VBoxServiceVerbose(4, "[PropCache %p]: Written \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n",
265 pCache, pNode->pszName, pszValue, pNode->fFlags, rc);
266 if (RT_SUCCESS(rc)) /* Only update the node's value on successful write. */
267 {
268 RTStrFree(pNode->pszValue);
269 pNode->pszValue = RTStrDup(pszValue);
270 if (!pNode->pszValue)
271 rc = VERR_NO_MEMORY;
272 }
273 }
274 else
275 rc = VINF_NO_CHANGE; /* No update needed. */
276 }
277 else
278 {
279 /* No value specified. Deletion (or no action required). */
280 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
281 {
282 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
283 0, /* Flags */ NULL /* Value */);
284 VBoxServiceVerbose(4, "[PropCache %p]: Deleted \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n",
285 pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc);
286 if (RT_SUCCESS(rc)) /* Only delete property value on successful Vbgl deletion. */
287 {
288 /* Delete property (but do not remove from cache) if not deleted yet. */
289 RTStrFree(pNode->pszValue);
290 pNode->pszValue = NULL;
291 }
292 }
293 else
294 rc = VINF_NO_CHANGE; /* No update needed. */
295 }
296
297 /* Release cache. */
298 RTCritSectLeave(&pCache->CritSect);
299 }
300
301 VBoxServiceVerbose(4, "[PropCache %p]: Updating \"%s\" resulted in rc=%Rrc\n",
302 pCache, pszName, rc);
303
304 /* Delete temp stuff. */
305 RTStrFree(pszValue);
306 return rc;
307}
308
309
310/**
311 * Updates all cache values which are matching the specified path.
312 *
313 * @returns VBox status code.
314 *
315 * @param pCache The property cache.
316 * @param pszValue The value to set. A NULL will delete the value.
317 * @param fFlags Flags to set.
318 * @param pszPathFormat The path format string. May not be null and has
319 * to be an absolute path.
320 * @param ... Format arguments.
321 */
322int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags, const char *pszPathFormat, ...)
323{
324 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
325 AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER);
326
327 int rc = VERR_NOT_FOUND;
328 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
329 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
330 {
331 /*
332 * Format the value first.
333 */
334 char *pszPath = NULL;
335 va_list va;
336 va_start(va, pszPathFormat);
337 RTStrAPrintfV(&pszPath, pszPathFormat, va);
338 va_end(va);
339 if (!pszPath)
340 {
341 rc = VERR_NO_STR_MEMORY;
342 }
343 else
344 {
345 /* Iterate through all nodes and compare their paths. */
346 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
347 {
348 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
349 {
350 /** @todo Use some internal function to update the node directly, this is slow atm. */
351 rc = VBoxServicePropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
352 }
353 if (RT_FAILURE(rc))
354 break;
355 }
356 RTStrFree(pszPath);
357 }
358 RTCritSectLeave(&pCache->CritSect);
359 }
360 return rc;
361}
362
363
364/**
365 * Flushes the cache by writing every item regardless of its state.
366 *
367 * @param pCache The property cache.
368 */
369int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
370{
371 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
372
373 int rc = VINF_SUCCESS;
374 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
375 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
376 {
377 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
378 {
379 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNodeIt->pszName,
380 pNodeIt->fFlags, pNodeIt->pszValue);
381 if (RT_FAILURE(rc))
382 break;
383 }
384 RTCritSectLeave(&pCache->CritSect);
385 }
386 return rc;
387}
388
389
390/**
391 * Reset all temporary properties and destroy the cache.
392 *
393 * @param pCache The property cache.
394 */
395void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
396{
397 AssertPtrReturnVoid(pCache);
398 Assert(pCache->uClientID);
399
400 /* Lock the cache. */
401 int rc = RTCritSectEnter(&pCache->CritSect);
402 if (RT_SUCCESS(rc))
403 {
404 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
405 while (pNode)
406 {
407 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
408 ? NULL :
409 RTListNodeGetNext(&pNode->NodeSucc,
410 VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
411 RTListNodeRemove(&pNode->NodeSucc);
412
413 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY)
414 {
415 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
416 pNode->fFlags, pNode->pszValueReset);
417 }
418
419 AssertPtr(pNode->pszName);
420 RTStrFree(pNode->pszName);
421 RTStrFree(pNode->pszValue);
422 RTStrFree(pNode->pszValueReset);
423 pNode->fFlags = 0;
424
425 RTMemFree(pNode);
426
427 pNode = pNext;
428 }
429 RTCritSectLeave(&pCache->CritSect);
430 }
431
432 /* Destroy critical section. */
433 RTCritSectDelete(&pCache->CritSect);
434}
435
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