VirtualBox

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

Last change on this file since 98613 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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