VirtualBox

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

Last change on this file since 60641 was 58029, checked in by vboxsync, 9 years ago

VBoxService: Using prefix 'VGSvc', code style/width cleanups. No real changes.

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