VirtualBox

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

Last change on this file since 31881 was 31881, checked in by vboxsync, 15 years ago

VBoxService/PropCache: Remove TRANSIENT flag from temporary properties when cache is destroyed gracefully.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 31881 2010-08-24 07:14:36Z vboxsync $ */
2/** @file
3 * VBoxServicePropCache - Guest property cache.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/** Internal functions, not for public use. */
34PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t uFlags);
35PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName);
36
37
38/** @todo Docs */
39PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t uFlags)
40{
41 AssertPtr(pCache);
42 AssertPtr(pszName);
43 /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
44 * map.
45 * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
46 * implementation (AVL tree). However, this is not important at the
47 * moment. */
48 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt, pNode = NULL;
49 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
50 {
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 */
66PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
67{
68 AssertPtr(pszName);
69 PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
70 if (pNode)
71 {
72 pNode->pszName = RTStrDup(pszName);
73 pNode->pszValue = NULL;
74 pNode->fFlags = 0;
75 pNode->pszValueReset = NULL;
76
77 int rc = RTCritSectEnter(&pCache->CritSect);
78 if (RT_SUCCESS(rc))
79 {
80 /*rc =*/ RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
81 rc = RTCritSectLeave(&pCache->CritSect);
82 }
83 }
84 return pNode;
85}
86
87
88/** @todo Docs */
89int vboxServicePropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
90{
91 AssertPtr(pszName);
92 int rc;
93 if (pszValueFormat != NULL)
94 {
95 va_list va;
96 va_start(va, pszValueFormat);
97
98 char *pszValue;
99 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
100 {
101 /*
102 * Because a value can be temporary we have to make sure it also
103 * gets deleted when the property cache did not have the chance to
104 * gracefully clean it up (due to a hard VM reset etc), so set this
105 * guest property using the TRANSIENT flag.
106 */
107 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue,
108 (fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY)
109 ? "TRANSIENT"
110 : NULL);
111 RTStrFree(pszValue);
112 }
113 va_end(va);
114 }
115 else
116 {
117 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
118 }
119 return rc;
120}
121
122
123/**
124 * Creates a property cache.
125 *
126 * @returns IPRT status code.
127 * @param pCache Pointer to the cache.
128 * @param uClientId The HGCM handle of to the guest property service.
129 */
130int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
131{
132 AssertPtr(pCache);
133 /** @todo Prevent init the cache twice!
134 * r=bird: Use a magic. */
135 RTListInit(&pCache->NodeHead);
136 pCache->uClientID = uClientId;
137 return RTCritSectInit(&pCache->CritSect);
138}
139
140
141/** @todo Docs */
142int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache,
143 const char *pszName, uint32_t fFlags, const char *pszValueReset)
144{
145 AssertPtr(pCache);
146 AssertPtr(pszName);
147 PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0);
148 if (pNode == NULL)
149 pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName);
150
151 int rc;
152 if (pNode != NULL)
153 {
154 rc = RTCritSectEnter(&pCache->CritSect);
155 if (RT_SUCCESS(rc))
156 {
157 pNode->fFlags = fFlags;
158 if (pszValueReset)
159 {
160 if (pNode->pszValueReset)
161 RTStrFree(pNode->pszValueReset);
162 pNode->pszValueReset = RTStrDup(pszValueReset);
163 }
164 rc = RTCritSectLeave(&pCache->CritSect);
165 }
166 }
167 else
168 rc = VERR_NO_MEMORY;
169 return rc;
170}
171
172
173/**
174 * Updates the local guest property cache and writes it to HGCM if outdated.
175 *
176 * @returns VBox status code. Errors will be logged.
177 *
178 * @param pCache The property cache.
179 * @param pszName The property name.
180 * @param pszValueFormat The property format string. If this is NULL then
181 * the property will be deleted (if possible).
182 * @param ... Format arguments.
183 */
184int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
185{
186 char *pszValue = NULL;
187 int rc;
188 if (pszValueFormat)
189 {
190 va_list va;
191 va_start(va, pszValueFormat);
192 RTStrAPrintfV(&pszValue, pszValueFormat, va);
193 va_end(va);
194 }
195 rc = VBoxServicePropCacheUpdateEx(pCache, pszName, 0 /* Not used */, NULL /* Not used */, pszValue);
196 if (pszValue)
197 RTStrFree(pszValue);
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. Errors will be logged.
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 VBoxServicePropCacheUpdateEx(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags,
214 const char *pszValueReset, const char *pszValueFormat, ...)
215{
216 AssertPtr(pCache);
217 Assert(pCache->uClientID);
218 AssertPtr(pszName);
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 = vboxServicePropCacheFindInternal(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 = vboxServicePropCacheInsertEntryInternal(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 & VBOXSERVICEPROPCACHEFLAG_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 = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, fFlags, pszValue);
261 RTStrFree(pNode->pszValue);
262 pNode->pszValue = RTStrDup(pszValue);
263 }
264 else
265 rc = VINF_NO_CHANGE; /* No update needed. */
266 }
267 else
268 {
269 /* No value specified. Deletion (or no action required). */
270 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
271 {
272 /* Delete property (but do not remove from cache) if not deleted yet. */
273 RTStrFree(pNode->pszValue);
274 pNode->pszValue = NULL;
275 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName,
276 0, /* Flags */ NULL /* Value */);
277 }
278 else
279 rc = VINF_NO_CHANGE; /* No update needed. */
280 }
281
282 /* Update rest of the fields. */
283 if (pszValueReset)
284 {
285 if (pNode->pszValueReset)
286 RTStrFree(pNode->pszValueReset);
287 pNode->pszValueReset = RTStrDup(pszValueReset);
288 }
289 if (fFlags)
290 pNode->fFlags = fFlags;
291
292 /* Release cache. */
293 int rc2 = RTCritSectLeave(&pCache->CritSect);
294 if (RT_SUCCESS(rc))
295 rc2 = rc;
296 }
297
298 /* Delete temp stuff. */
299 RTStrFree(pszValue);
300
301 return rc;
302}
303
304
305/**
306 * Flushes the cache by writing every item regardless of its state.
307 *
308 * @param pCache The property cache.
309 */
310int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
311{
312 AssertPtr(pCache);
313 int rc = VINF_SUCCESS;
314 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL;
315 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
316 {
317 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
318 {
319 rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNodeIt->pszName,
320 pNodeIt->fFlags, pNodeIt->pszValue);
321 if (RT_FAILURE(rc))
322 break;
323 }
324 RTCritSectLeave(&pCache->CritSect);
325 }
326 return rc;
327}
328
329
330/**
331 * Reset all temporary properties and destroy the cache.
332 *
333 * @param pCache The property cache.
334 */
335void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
336{
337 AssertPtr(pCache);
338 Assert(pCache->uClientID);
339
340 /* Lock the cache. */
341 int rc = RTCritSectEnter(&pCache->CritSect);
342 if (RT_SUCCESS(rc))
343 {
344 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListNodeGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
345 while (pNode)
346 {
347 /*
348 * When destroying the cache and we have a temporary value, remove the
349 * (eventually) set TRANSIENT flag from it so that it doesn't get deleted
350 * by the host side in order to put the actual reset value in it.
351 */
352 if ((pNode->fFlags & VBOXSERVICEPROPCACHEFLAG_TEMPORARY) == 0)
353 vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, 0 /* Flags */, pNode->pszValueReset);
354
355 AssertPtr(pNode->pszName);
356 RTStrFree(pNode->pszName);
357 RTStrFree(pNode->pszValue);
358 RTStrFree(pNode->pszValueReset);
359 pNode->fFlags = 0;
360
361 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeGetNext(&pNode->NodeSucc, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
362 RTListNodeRemove(&pNode->NodeSucc);
363 RTMemFree(pNode);
364
365 if (pNext && RTListNodeIsLast(&pCache->NodeHead, &pNext->NodeSucc))
366 break;
367 pNode = pNext;
368 }
369 RTCritSectLeave(&pCache->CritSect);
370 }
371
372 /* Destroy critical section. */
373 RTCritSectDelete(&pCache->CritSect);
374}
375
Note: See TracBrowser for help on using the repository browser.

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