VirtualBox

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

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

GuestProps: r=bird: Another Q for Michael.

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