VirtualBox

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

最後變更 在這個檔案從107835是 107733,由 vboxsync 提交於 2 月 前

VBoxService/VBoxServicePropCache.cpp: Docs, renaming (to match the rest).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 16.2 KB
 
1/* $Id: VBoxServicePropCache.cpp 107733 2025-01-14 08:56:10Z vboxsync $ */
2/** @file
3 * VBoxServicePropCache - Guest property cache.
4 */
5
6/*
7 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/assert.h>
33#include <iprt/list.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36
37#include <VBox/VBoxGuestLib.h>
38#include <VBox/HostServices/GuestPropertySvc.h> /* For GUEST_PROP_MAX_VALUE_LEN */
39#include "VBoxServiceInternal.h"
40#include "VBoxServiceUtils.h"
41#include "VBoxServicePropCache.h"
42
43
44/**
45 * Searches a property within a property cache.
46 *
47 * @returns A pointer to the found property cache entry on success, or NULL if not found.
48 * @param pCache The property cache.
49 * @param pszName Name of property to search for. Case sensitive.
50 * @param fFlags Search flags. Currently unused and must be 0.
51 */
52static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName,
53 uint32_t fFlags)
54{
55 RT_NOREF1(fFlags);
56 AssertPtrReturn(pCache, NULL);
57 AssertPtrReturn(pszName, NULL);
58
59 /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
60 * map.
61 * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
62 * implementation (AVL tree). However, this is not important at the
63 * moment. */
64 PVBOXSERVICEVEPROPCACHEENTRY pNode = NULL;
65 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
66 {
67 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
68 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
69 {
70 if (strcmp(pNodeIt->pszName, pszName) == 0)
71 {
72 pNode = pNodeIt;
73 break;
74 }
75 }
76 RTCritSectLeave(&pCache->CritSect);
77 }
78 return pNode;
79}
80
81
82/**
83 * Inserts (appends) a property into a property cache.
84 *
85 * @returns A pointer to the inserted property cache entry on success, or NULL on failure.
86 * @param pCache The property cache.
87 * @param pszName Name of property to insert. Case sensitive.
88 */
89static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
90{
91 AssertPtrReturn(pCache, NULL);
92 AssertPtrReturn(pszName, NULL);
93
94 PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
95 if (pNode)
96 {
97 pNode->pszName = RTStrDup(pszName);
98 AssertPtrReturnStmt(pNode->pszName, RTMemFree(pNode), NULL);
99 pNode->pszValue = NULL;
100 pNode->fFlags = 0;
101 pNode->pszValueReset = NULL;
102
103 int rc = RTCritSectEnter(&pCache->CritSect);
104 if (RT_SUCCESS(rc))
105 {
106 RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
107 RTCritSectLeave(&pCache->CritSect);
108
109 return pNode;
110 }
111
112 RTStrFree(pNode->pszName);
113 RTMemFree(pNode);
114 }
115
116 return NULL;
117}
118
119
120/**
121 * Writes a new value to a property.
122 *
123 * @returns VBox status code.
124 * @param uClientId The HGCM handle of to the guest property service.
125 * @param pszName Name of property to write value for. Case sensitive.
126 * @param fFlags Property cache flags of type VGSVCPROPCACHE_FLAGS_XXX.
127 * @param pszValueFormat Format string of value to write.
128 */
129static int vgsvcPropCacheWritePropF(uint32_t uClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
130{
131 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
132
133 int rc;
134 if (pszValueFormat != NULL)
135 {
136 va_list va;
137 va_start(va, pszValueFormat);
138
139 char *pszValue;
140 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
141 {
142 if (fFlags & VGSVCPROPCACHE_FLAGS_TRANSIENT)
143 {
144 /*
145 * Because a value can be temporary we have to make sure it also
146 * gets deleted when the property cache did not have the chance to
147 * gracefully clean it up (due to a hard VM reset etc), so set this
148 * guest property using the TRANSRESET flag..
149 */
150 rc = VbglR3GuestPropWrite(uClientId, pszName, pszValue, "TRANSRESET");
151 if (rc == VERR_PARSE_ERROR)
152 {
153 /* Host does not support the "TRANSRESET" flag, so only
154 * use the "TRANSIENT" flag -- better than nothing :-). */
155 rc = VbglR3GuestPropWrite(uClientId, pszName, pszValue, "TRANSIENT");
156 /** @todo r=bird: Remember that the host doesn't support
157 * this. */
158 }
159 }
160 else
161 rc = VbglR3GuestPropWriteValue(uClientId, pszName, pszValue /* No transient flags set */);
162 RTStrFree(pszValue);
163 }
164 else
165 rc = VERR_NO_MEMORY;
166 va_end(va);
167 }
168 else
169 rc = VbglR3GuestPropWriteValue(uClientId, pszName, NULL);
170 return rc;
171}
172
173
174/**
175 * Creates a property cache.
176 *
177 * @returns VBox status code.
178 * @param pCache Pointer to the cache.
179 * @param uClientId The HGCM handle of to the guest property service.
180 */
181int VGSvcPropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
182{
183 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
184 /** @todo Prevent init the cache twice!
185 * r=bird: Use a magic. */
186 RTListInit(&pCache->NodeHead);
187 pCache->uClientID = uClientId;
188 return RTCritSectInit(&pCache->CritSect);
189}
190
191
192/**
193 * Updates a cache entry without submitting any changes to the host.
194 *
195 * This is handy for defining default values/flags.
196 *
197 * @returns VBox status code.
198 * @param pCache The property cache.
199 * @param pszName The property name.
200 * @param fFlags The property flags to set.
201 * @param pszValueReset The property reset value.
202 */
203int VGSvcPropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags, const char *pszValueReset)
204{
205 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
206 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
207 PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
208 if (pNode == NULL)
209 pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
210
211 int rc;
212 if (pNode != NULL)
213 {
214 rc = RTCritSectEnter(&pCache->CritSect);
215 if (RT_SUCCESS(rc))
216 {
217 pNode->fFlags = fFlags;
218 if (pszValueReset)
219 {
220 if (pNode->pszValueReset)
221 RTStrFree(pNode->pszValueReset);
222 pNode->pszValueReset = RTStrDup(pszValueReset);
223 AssertPtr(pNode->pszValueReset);
224 }
225 rc = RTCritSectLeave(&pCache->CritSect);
226 }
227 }
228 else
229 rc = VERR_NO_MEMORY;
230 return rc;
231}
232
233
234/**
235 * Updates the local guest property cache and writes it to HGCM if outdated.
236 *
237 * @returns VBox status code.
238 * @retval VERR_BUFFER_OVERFLOW if the property name or value exceeds the limit.
239 * @param pCache The property cache.
240 * @param pszName The property name.
241 * @param pszValueFormat The property format string. If this is NULL then
242 * the property will be deleted (if possible).
243 * @param ... Format arguments.
244 */
245int VGSvcPropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
246{
247 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
248 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
249
250 Assert(pCache->uClientID);
251
252 if (RTStrNLen(pszName, GUEST_PROP_MAX_NAME_LEN) > GUEST_PROP_MAX_NAME_LEN - 1 /* Terminator */)
253 return VERR_BUFFER_OVERFLOW;
254
255 /*
256 * Format the value first.
257 */
258 char *pszValue = NULL;
259 if (pszValueFormat)
260 {
261 va_list va;
262 va_start(va, pszValueFormat);
263 RTStrAPrintfV(&pszValue, pszValueFormat, va);
264 va_end(va);
265 if (!pszValue)
266 return VERR_NO_STR_MEMORY;
267 if (RTStrNLen(pszValue, GUEST_PROP_MAX_VALUE_LEN) > GUEST_PROP_MAX_VALUE_LEN - 1 /* Terminator */)
268 {
269 RTStrFree(pszValue);
270 return VERR_BUFFER_OVERFLOW;
271 }
272 }
273
274 PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
275
276 /* Lock the cache. */
277 int rc = RTCritSectEnter(&pCache->CritSect);
278 if (RT_SUCCESS(rc))
279 {
280 if (pNode == NULL)
281 pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
282
283 AssertPtr(pNode);
284 if (pszValue) /* Do we have a value to check for? */
285 {
286 bool fUpdate = false;
287 /* Always update this property, no matter what? */
288 if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE)
289 fUpdate = true;
290 /* Did the value change so we have to update? */
291 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
292 fUpdate = true;
293 /* No value stored at the moment but we have a value now? */
294 else if (pNode->pszValue == NULL)
295 fUpdate = true;
296
297 if (fUpdate)
298 {
299 /* Write the update. */
300 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
301 VGSvcVerbose(4, "[PropCache %p]: Written '%s'='%s' (flags: %x), rc=%Rrc\n",
302 pCache, pNode->pszName, pszValue, pNode->fFlags, rc);
303 if (RT_SUCCESS(rc)) /* Only update the node's value on successful write. */
304 {
305 RTStrFree(pNode->pszValue);
306 pNode->pszValue = RTStrDup(pszValue);
307 if (!pNode->pszValue)
308 rc = VERR_NO_MEMORY;
309 }
310 }
311 else
312 rc = VINF_NO_CHANGE; /* No update needed. */
313 }
314 else
315 {
316 /* No value specified. Deletion (or no action required). */
317 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
318 {
319 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName,
320 0, /* Flags */ NULL /* Value */);
321 VGSvcVerbose(4, "[PropCache %p]: Deleted '%s'='%s' (flags: %x), rc=%Rrc\n",
322 pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc);
323 if (RT_SUCCESS(rc)) /* Only delete property value on successful Vbgl deletion. */
324 {
325 /* Delete property (but do not remove from cache) if not deleted yet. */
326 RTStrFree(pNode->pszValue);
327 pNode->pszValue = NULL;
328 }
329 }
330 else
331 rc = VINF_NO_CHANGE; /* No update needed. */
332 }
333
334 /* Release cache. */
335 RTCritSectLeave(&pCache->CritSect);
336 }
337
338 VGSvcVerbose(4, "[PropCache %p]: Updating '%s' resulted in rc=%Rrc\n", pCache, pszName, rc);
339
340 /* Delete temp stuff. */
341 RTStrFree(pszValue);
342 return rc;
343}
344
345
346/**
347 * Updates all cache values which are matching the specified path.
348 *
349 * @returns VBox status code.
350 * @param pCache The property cache.
351 * @param pszValue The value to set. A NULL will delete the value.
352 * @param fFlags Flags to set.
353 * @param pszPathFormat The path format string. May not be null and has
354 * to be an absolute path.
355 * @param ... Format arguments.
356 */
357int VGSvcPropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags,
358 const char *pszPathFormat, ...)
359{
360 RT_NOREF1(fFlags);
361 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
362 AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER);
363
364 int rc = VERR_NOT_FOUND;
365 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
366 {
367 /*
368 * Format the value first.
369 */
370 char *pszPath = NULL;
371 va_list va;
372 va_start(va, pszPathFormat);
373 RTStrAPrintfV(&pszPath, pszPathFormat, va);
374 va_end(va);
375 if (!pszPath)
376 {
377 rc = VERR_NO_STR_MEMORY;
378 }
379 else
380 {
381 /* Iterate through all nodes and compare their paths. */
382 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
383 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
384 {
385 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
386 {
387 /** @todo Use some internal function to update the node directly, this is slow atm. */
388 rc = VGSvcPropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
389 }
390 if (RT_FAILURE(rc))
391 break;
392 }
393 RTStrFree(pszPath);
394 }
395 RTCritSectLeave(&pCache->CritSect);
396 }
397 return rc;
398}
399
400
401/**
402 * Flushes the cache by writing every item regardless of its state.
403 *
404 * @returns VBox status code.
405 * @param pCache The property cache.
406 */
407int VGSvcPropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
408{
409 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
410
411 int rc = VINF_SUCCESS;
412 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
413 {
414 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
415 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
416 {
417 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNodeIt->pszName, pNodeIt->fFlags, pNodeIt->pszValue);
418 if (RT_FAILURE(rc))
419 break;
420 }
421 RTCritSectLeave(&pCache->CritSect);
422 }
423 return rc;
424}
425
426
427/**
428 * Reset all temporary properties and destroy the cache.
429 *
430 * @param pCache The property cache.
431 */
432void VGSvcPropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
433{
434 AssertPtrReturnVoid(pCache);
435 Assert(pCache->uClientID);
436
437 /* Lock the cache. */
438 int rc = RTCritSectEnter(&pCache->CritSect);
439 if (RT_SUCCESS(rc))
440 {
441 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
442 while (pNode)
443 {
444 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
445 ? NULL :
446 RTListNodeGetNext(&pNode->NodeSucc,
447 VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
448 RTListNodeRemove(&pNode->NodeSucc);
449
450 if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_TEMPORARY)
451 vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pNode->pszValueReset);
452
453 AssertPtr(pNode->pszName);
454 RTStrFree(pNode->pszName);
455 RTStrFree(pNode->pszValue);
456 RTStrFree(pNode->pszValueReset);
457 pNode->fFlags = 0;
458
459 RTMemFree(pNode);
460
461 pNode = pNext;
462 }
463 RTCritSectLeave(&pCache->CritSect);
464 }
465
466 /* Destroy critical section. */
467 RTCritSectDelete(&pCache->CritSect);
468}
469
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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