VirtualBox

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

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

src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp: Fixed warnings found by Parfait (unused assignment). jiraref:VBP-1424

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

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