VirtualBox

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

Last change on this file since 63206 was 62851, checked in by vboxsync, 8 years ago

GAs/common: warnings

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

© 2023 Oracle
ContactPrivacy policyTerms of Use