VirtualBox

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

Last change on this file since 77938 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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