VirtualBox

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

Last change on this file since 31915 was 31915, checked in by vboxsync, 15 years ago

Comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 31915 2010-08-24 12:20:32Z 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.215389.xyz. 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 va_end(va);
116 }
117 else
118 {
119 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
120 }
121 return rc;
122}
123
124
125/**
126 * Creates a property cache.
127 *
128 * @returns IPRT status code.
129 * @param pCache Pointer to the cache.
130 * @param uClientId The HGCM handle of to the guest property service.
131 */
132int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
133{
134 AssertPtr(pCache);
135 /** @todo Prevent init the cache twice!
136 * r=bird: Use a magic. */
137 RTListInit(&pCache->NodeHead);
138 pCache->uClientID = uClientId;
139 return RTCritSectInit(&pCache->CritSect);
140}
141
142
143/**
144 * Updates a cache entry without submitting any changes to the host.
145 * This is handy for defining default values/flags.
146 *
147 * @returns VBox status code.
148 *
149 * @param pCache The property cache.
150 * @param pszName The property name.
151 * @param fFlags The property flags to set.
152 * @param pszValueReset The property reset value.
153 */
154int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache,
155 const char *pszName, uint32_t fFlags, const char *pszValueReset)
156{
157 AssertPtr(pCache);
158 AssertPtr(pszName);
159 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
160 if (pNode == NULL)
161 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
162
163 int rc;
164 if (pNode != NULL)
165 {
166 rc = RTCritSectEnter(&pCache->CritSect);
167 if (RT_SUCCESS(rc))
168 {
169 pNode->fFlags = fFlags;
170 if (pszValueReset)
171 {
172 if (pNode->pszValueReset)
173 RTStrFree(pNode->pszValueReset);
174 pNode->pszValueReset = RTStrDup(pszValueReset);
175 }
176 rc = RTCritSectLeave(&pCache->CritSect);
177 }
178 }
179 else
180 rc = VERR_NO_MEMORY;
181 return rc;
182}
183
184
185/**
186 * Updates the local guest property cache and writes it to HGCM if outdated.
187 *
188 * @returns VBox status code.
189 *
190 * @param pCache The property cache.
191 * @param pszName The property name.
192 * @param pszValueFormat The property format string. If this is NULL then
193 * the property will be deleted (if possible).
194 * @param ... Format arguments.
195 */
196int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
197{
198 char *pszValue = NULL;
199 int rc;
200 if (pszValueFormat)
201 {
202 va_list va;
203 va_start(va, pszValueFormat);
204 RTStrAPrintfV(&pszValue, pszValueFormat, va);
205 va_end(va);
206 }
207 rc = VBoxServicePropCacheUpdateEx(pCache, pszName, 0 /* Not used */, NULL /* Not used */, pszValue);
208 if (pszValue)
209 RTStrFree(pszValue);
210 return rc;
211}
212
213
214/**
215 * Updates the local guest property cache and writes it to HGCM if outdated.
216 *
217 * @returns VBox status code.
218 *
219 * @param pCache The property cache.
220 * @param pszName The property name.
221 * @param fFlags The property flags to set.
222 * @param pszValueReset The property reset value.
223 * @param pszValueFormat The property format string. If this is NULL then
224 * the property will be deleted (if possible).
225 * @param ... Format arguments.
226 */
227int VBoxServicePropCacheUpdateEx(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags,
228 const char *pszValueReset, const char *pszValueFormat, ...)
229{
230 AssertPtr(pCache);
231 Assert(pCache->uClientID);
232 AssertPtr(pszName);
233
234 /*
235 * Format the value first.
236 */
237 char *pszValue = NULL;
238 if (pszValueFormat)
239 {
240 va_list va;
241 va_start(va, pszValueFormat);
242 RTStrAPrintfV(&pszValue, pszValueFormat, va);
243 va_end(va);
244 if (!pszValue)
245 return VERR_NO_STR_MEMORY;
246 }
247
248 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
249
250 /* Lock the cache. */
251 int rc = RTCritSectEnter(&pCache->CritSect);
252 if (RT_SUCCESS(rc))
253 {
254 if (pNode == NULL)
255 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
256
257 AssertPtr(pNode);
258 if (pszValue) /* Do we have a value to check for? */
259 {
260 bool fUpdate = false;
261 /* Always update this property, no matter what? */
262 if (pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE)
263 fUpdate = true;
264 /* Did the value change so we have to update? */
265 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
266 fUpdate = true;
267 /* No value stored at the moment but we have a value now? */
268 else if (pNode->pszValue == NULL)
269 fUpdate = true;
270
271 if (fUpdate)
272 {
273 /* Write the update. */
274 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, fFlags, pszValue);
275 RTStrFree(pNode->pszValue);
276 pNode->pszValue = RTStrDup(pszValue);
277 }
278 else
279 rc = VINF_NO_CHANGE; /* No update needed. */
280 }
281 else
282 {
283 /* No value specified. Deletion (or no action required). */
284 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
285 {
286 /* Delete property (but do not remove from cache) if not deleted yet. */
287 RTStrFree(pNode->pszValue);
288 pNode->pszValue = NULL;
289 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
290 0, /* Flags */ NULL /* Value */);
291 }
292 else
293 rc = VINF_NO_CHANGE; /* No update needed. */
294 }
295
296 /* Update rest of the fields. */
297 if (pszValueReset)
298 {
299 if (pNode->pszValueReset)
300 RTStrFree(pNode->pszValueReset);
301 pNode->pszValueReset = RTStrDup(pszValueReset);
302 }
303 if (fFlags)
304 pNode->fFlags = fFlags;
305
306 /* Release cache. */
307 int rc2 = RTCritSectLeave(&pCache->CritSect);
308 if (RT_SUCCESS(rc))
309 rc2 = rc;
310 }
311
312 /* Delete temp stuff. */
313 RTStrFree(pszValue);
314
315 return rc;
316}
317
318
319/**
320 * Updates all cache values which are matching the specified path.
321 *
322 * @returns VBox status code.
323 *
324 * @param pCache The property cache.
325 * @param pszValue The value to set. A NULL will delete the value.
326 * @param fFlags Flags to set.
327 * @param pszPathFormat The path format string. May not be null and has
328 * to be an absolute path.
329 * @param ... Format arguments.
330 */
331int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags, const char *pszPathFormat, ...)
332{
333 AssertPtr(pCache);
334 AssertPtr(pszPathFormat);
335 int rc = VERR_NOT_FOUND;
336 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
337 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
338 {
339 /*
340 * Format the value first.
341 */
342 char *pszPath = NULL;
343 va_list va;
344 va_start(va, pszPathFormat);
345 RTStrAPrintfV(&pszPath, pszPathFormat, va);
346 va_end(va);
347 if (!pszPath)
348 return VERR_NO_STR_MEMORY;
349
350 /* Iterate through all nodes and compare their paths. */
351 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
352 {
353 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
354 {
355 /** @todo Use some internal function to update the node directly, this is slow atm. */
356 rc = VBoxServicePropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
357 }
358 if (RT_FAILURE(rc))
359 break;
360 }
361 RTStrFree(pszPath);
362 RTCritSectLeave(&pCache->CritSect);
363 }
364 return rc;
365}
366
367
368/**
369 * Flushes the cache by writing every item regardless of its state.
370 *
371 * @param pCache The property cache.
372 */
373int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
374{
375 AssertPtr(pCache);
376 int rc = VINF_SUCCESS;
377 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
378 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
379 {
380 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
381 {
382 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNodeIt->pszName,
383 pNodeIt->fFlags, pNodeIt->pszValue);
384 if (RT_FAILURE(rc))
385 break;
386 }
387 RTCritSectLeave(&pCache->CritSect);
388 }
389 return rc;
390}
391
392
393/**
394 * Reset all temporary properties and destroy the cache.
395 *
396 * @param pCache The property cache.
397 */
398void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
399{
400 AssertPtr(pCache);
401 Assert(pCache->uClientID);
402
403 /* Lock the cache. */
404 int rc = RTCritSectEnter(&pCache->CritSect);
405 if (RT_SUCCESS(rc))
406 {
407 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListNodeGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
408 while (pNode)
409 {
410 /*
411 * When destroying the cache and we have a temporary value, remove the
412 * (eventually) set TRANSIENT flag from it so that it doesn't get deleted
413 * by the host side in order to put the actual reset value in it.
414 */
415 if ((pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY) == 0)
416 vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, 0 /* Flags */, pNode->pszValueReset);
417
418 AssertPtr(pNode->pszName);
419 RTStrFree(pNode->pszName);
420 RTStrFree(pNode->pszValue);
421 RTStrFree(pNode->pszValueReset);
422 pNode->fFlags = 0;
423
424 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeGetNext(&pNode->NodeSucc, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
425 RTListNodeRemove(&pNode->NodeSucc);
426 RTMemFree(pNode);
427
428 if (pNext && RTListNodeIsLast(&pCache->NodeHead, &pNext->NodeSucc))
429 break;
430 pNode = pNext;
431 }
432 RTCritSectLeave(&pCache->CritSect);
433 }
434
435 /* Destroy critical section. */
436 RTCritSectDelete(&pCache->CritSect);
437}
438
Note: See TracBrowser for help on using the repository browser.

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