VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestProp.cpp@ 27687

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

Additons/common/VBoxGuestLibR3: make saving and restoring video modes work with XFree86 and clean up the code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 33.9 KB
Line 
1/* $Id: VBoxGuestR3LibGuestProp.cpp 27687 2010-03-24 22:14:20Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties.
4 */
5
6/*
7 * Copyright (C) 2007 Sun Microsystems, Inc.
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <iprt/string.h>
36#include <iprt/mem.h>
37#include <iprt/assert.h>
38#include <iprt/cpp/autores.h>
39#include <iprt/stdarg.h>
40#include <VBox/log.h>
41#include <VBox/HostServices/GuestPropertySvc.h>
42
43#include "VBGLR3Internal.h"
44
45#ifdef VBOX_VBGLR3_XFREE86
46/* Rather than try to resolve all the header file conflicts, I will just
47 prototype what we need here. */
48extern "C" char* xf86strcpy(char*,const char*);
49# undef strcpy
50# define strcpy xf86strcpy
51extern "C" void* xf86memchr(const void*,int,xf86size_t);
52# undef memchr
53# define memchr xf86memchr
54#endif /* VBOX_VBGLR3_XFREE86 */
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59/**
60 * Structure containing information needed to enumerate through guest
61 * properties.
62 *
63 * @remarks typedef in VBoxGuestLib.h.
64 */
65struct VBGLR3GUESTPROPENUM
66{
67 /** @todo add a magic and validate the handle. */
68 /** The buffer containing the raw enumeration data */
69 char *pchBuf;
70 /** The end of the buffer */
71 char *pchBufEnd;
72 /** Pointer to the next entry to enumerate inside the buffer */
73 char *pchNext;
74};
75
76using namespace guestProp;
77
78/**
79 * Connects to the guest property service.
80 *
81 * @returns VBox status code
82 * @param pu32ClientId Where to put the client id on success. The client id
83 * must be passed to all the other calls to the service.
84 */
85VBGLR3DECL(int) VbglR3GuestPropConnect(uint32_t *pu32ClientId)
86{
87 VBoxGuestHGCMConnectInfo Info;
88 Info.result = VERR_WRONG_ORDER;
89 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
90 RT_ZERO(Info.Loc.u);
91 strcpy(Info.Loc.u.host.achName, "VBoxGuestPropSvc");
92 Info.u32ClientID = UINT32_MAX; /* try make valgrid shut up. */
93
94 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info));
95 if (RT_SUCCESS(rc))
96 {
97 rc = Info.result;
98 if (RT_SUCCESS(rc))
99 *pu32ClientId = Info.u32ClientID;
100 }
101 return rc;
102}
103
104
105/**
106 * Disconnect from the guest property service.
107 *
108 * @returns VBox status code.
109 * @param u32ClientId The client id returned by VbglR3InfoSvcConnect().
110 */
111VBGLR3DECL(int) VbglR3GuestPropDisconnect(uint32_t u32ClientId)
112{
113 VBoxGuestHGCMDisconnectInfo Info;
114 Info.result = VERR_WRONG_ORDER;
115 Info.u32ClientID = u32ClientId;
116
117 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info));
118 if (RT_SUCCESS(rc))
119 rc = Info.result;
120 return rc;
121}
122
123
124/**
125 * Write a property value.
126 *
127 * @returns VBox status code.
128 * @param u32ClientId The client id returned by VbglR3InvsSvcConnect().
129 * @param pszName The property to save to. Utf8
130 * @param pszValue The value to store. Utf8. If this is NULL then
131 * the property will be removed.
132 * @param pszFlags The flags for the property
133 */
134VBGLR3DECL(int) VbglR3GuestPropWrite(uint32_t u32ClientId, const char *pszName, const char *pszValue, const char *pszFlags)
135{
136 int rc;
137
138 if (pszValue != NULL)
139 {
140 SetProperty Msg;
141
142 Msg.hdr.result = VERR_WRONG_ORDER;
143 Msg.hdr.u32ClientID = u32ClientId;
144 Msg.hdr.u32Function = SET_PROP_VALUE;
145 Msg.hdr.cParms = 3;
146 VbglHGCMParmPtrSetString(&Msg.name, pszName);
147 VbglHGCMParmPtrSetString(&Msg.value, pszValue);
148 VbglHGCMParmPtrSetString(&Msg.flags, pszFlags);
149 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
150 if (RT_SUCCESS(rc))
151 rc = Msg.hdr.result;
152 }
153 else
154 {
155 DelProperty Msg;
156
157 Msg.hdr.result = VERR_WRONG_ORDER;
158 Msg.hdr.u32ClientID = u32ClientId;
159 Msg.hdr.u32Function = DEL_PROP;
160 Msg.hdr.cParms = 1;
161 VbglHGCMParmPtrSetString(&Msg.name, pszName);
162 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
163 if (RT_SUCCESS(rc))
164 rc = Msg.hdr.result;
165 }
166 return rc;
167}
168
169
170/**
171 * Write a property value.
172 *
173 * @returns VBox status code.
174 *
175 * @param u32ClientId The client id returned by VbglR3InvsSvcConnect().
176 * @param pszName The property to save to. Must be valid UTF-8.
177 * @param pszValue The value to store. Must be valid UTF-8.
178 * If this is NULL then the property will be removed.
179 *
180 * @note if the property already exists and pszValue is not NULL then the
181 * property's flags field will be left unchanged
182 */
183VBGLR3DECL(int) VbglR3GuestPropWriteValue(uint32_t u32ClientId, const char *pszName, const char *pszValue)
184{
185 int rc;
186
187 if (pszValue != NULL)
188 {
189 SetPropertyValue Msg;
190
191 Msg.hdr.result = VERR_WRONG_ORDER;
192 Msg.hdr.u32ClientID = u32ClientId;
193 Msg.hdr.u32Function = SET_PROP_VALUE;
194 Msg.hdr.cParms = 2;
195 VbglHGCMParmPtrSetString(&Msg.name, pszName);
196 VbglHGCMParmPtrSetString(&Msg.value, pszValue);
197 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
198 if (RT_SUCCESS(rc))
199 rc = Msg.hdr.result;
200 }
201 else
202 {
203 DelProperty Msg;
204
205 Msg.hdr.result = VERR_WRONG_ORDER;
206 Msg.hdr.u32ClientID = u32ClientId;
207 Msg.hdr.u32Function = DEL_PROP;
208 Msg.hdr.cParms = 1;
209 VbglHGCMParmPtrSetString(&Msg.name, pszName);
210 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
211 if (RT_SUCCESS(rc))
212 rc = Msg.hdr.result;
213 }
214 return rc;
215}
216
217#ifndef VBOX_VBGLR3_XFREE86
218/**
219 * Write a property value where the value is formatted in RTStrPrintfV fashion.
220 *
221 * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
222 *
223 * @param u32ClientId The client ID returned by VbglR3InvsSvcConnect().
224 * @param pszName The property to save to. Must be valid UTF-8.
225 * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
226 * @param va The format arguments.
227 */
228VBGLR3DECL(int) VbglR3GuestPropWriteValueV(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, va_list va)
229{
230 /*
231 * Format the value and pass it on to the setter.
232 */
233 int rc = VERR_NO_STR_MEMORY;
234 char *pszValue;
235 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
236 {
237 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
238 RTStrFree(pszValue);
239 }
240 return rc;
241}
242
243
244/**
245 * Write a property value where the value is formatted in RTStrPrintf fashion.
246 *
247 * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
248 *
249 * @param u32ClientId The client ID returned by VbglR3InvsSvcConnect().
250 * @param pszName The property to save to. Must be valid UTF-8.
251 * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
252 * @param ... The format arguments.
253 */
254VBGLR3DECL(int) VbglR3GuestPropWriteValueF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...)
255{
256 va_list va;
257 va_start(va, pszValueFormat);
258 int rc = VbglR3GuestPropWriteValueV(u32ClientId, pszName, pszValueFormat, va);
259 va_end(va);
260 return rc;
261}
262#endif /* VBOX_VBGLR3_XFREE86 */
263
264/**
265 * Retrieve a property.
266 *
267 * @returns VBox status code.
268 * @retval VINF_SUCCESS on success, pszValue, pu64Timestamp and pszFlags
269 * containing valid data.
270 * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pcBuf is not large
271 * enough. In this case the size needed will be placed in
272 * @a pcbBufActual if it is not NULL.
273 * @retval VERR_NOT_FOUND if the key wasn't found.
274 *
275 * @param u32ClientId The client id returned by VbglR3GuestPropConnect().
276 * @param pszName The value to read. Utf8
277 * @param pvBuf A scratch buffer to store the data retrieved into.
278 * The returned data is only valid for it's lifetime.
279 * @a ppszValue will point to the start of this buffer.
280 * @param cbBuf The size of @a pcBuf
281 * @param ppszValue Where to store the pointer to the value retrieved.
282 * Optional.
283 * @param pu64Timestamp Where to store the timestamp. Optional.
284 * @param pszFlags Where to store the pointer to the flags. Optional.
285 * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
286 * Optional.
287 */
288VBGLR3DECL(int) VbglR3GuestPropRead(uint32_t u32ClientId, const char *pszName,
289 void *pvBuf, uint32_t cbBuf,
290 char **ppszValue, uint64_t *pu64Timestamp,
291 char **ppszFlags,
292 uint32_t *pcbBufActual)
293{
294 /*
295 * Create the GET_PROP message and call the host.
296 */
297 GetProperty Msg;
298
299 Msg.hdr.result = VERR_WRONG_ORDER;
300 Msg.hdr.u32ClientID = u32ClientId;
301 Msg.hdr.u32Function = GET_PROP;
302 Msg.hdr.cParms = 4;
303 VbglHGCMParmPtrSetString(&Msg.name, pszName);
304 VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf);
305 VbglHGCMParmUInt64Set(&Msg.timestamp, 0);
306 VbglHGCMParmUInt32Set(&Msg.size, 0);
307
308 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
309 if (RT_SUCCESS(rc))
310 rc = Msg.hdr.result;
311
312 /*
313 * The cbBufActual parameter is also returned on overflow so the call can
314 * adjust his/her buffer.
315 */
316 if ( rc == VERR_BUFFER_OVERFLOW
317 || pcbBufActual != NULL)
318 {
319 int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
320 AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
321 }
322 if (RT_FAILURE(rc))
323 return rc;
324
325 /*
326 * Buffer layout: Value\0Flags\0.
327 *
328 * If the caller cares about any of these strings, make sure things are
329 * propertly terminated (paranoia).
330 */
331 if ( RT_SUCCESS(rc)
332 && (ppszValue != NULL || ppszFlags != NULL))
333 {
334 /* Validate / skip 'Name'. */
335 char *pszFlags = (char *)memchr(pvBuf, '\0', cbBuf) + 1;
336 AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
337 if (ppszValue)
338 *ppszValue = (char *)pvBuf;
339
340 if (ppszFlags)
341 {
342 /* Validate 'Flags'. */
343 void *pvEos = memchr(pszFlags, '\0', cbBuf - (pszFlags - (char *)pvBuf));
344 AssertPtrReturn(pvEos, VERR_TOO_MUCH_DATA);
345 *ppszFlags = pszFlags;
346 }
347 }
348
349 /* And the timestamp, if requested. */
350 if (pu64Timestamp != NULL)
351 {
352 rc = VbglHGCMParmUInt64Get(&Msg.timestamp, pu64Timestamp);
353 AssertRCReturn(rc, rc);
354 }
355
356 return VINF_SUCCESS;
357}
358
359#ifndef VBOX_VBGLR3_XFREE86
360/**
361 * Retrieve a property value, allocating space for it.
362 *
363 * @returns VBox status code.
364 * @retval VINF_SUCCESS on success, *ppszValue containing valid data.
365 * @retval VERR_NOT_FOUND if the key wasn't found.
366 * @retval VERR_TOO_MUCH_DATA if we were unable to determine the right size
367 * to allocate for the buffer. This can happen as the result of a
368 * race between our allocating space and the host changing the
369 * property value.
370 *
371 * @param u32ClientId The client id returned by VbglR3GuestPropConnect().
372 * @param pszName The value to read. Must be valid UTF-8.
373 * @param ppszValue Where to store the pointer to the value returned.
374 * This is always set to NULL or to the result, even
375 * on failure.
376 */
377VBGLR3DECL(int) VbglR3GuestPropReadValueAlloc(uint32_t u32ClientId,
378 const char *pszName,
379 char **ppszValue)
380{
381 /*
382 * Quick input validation.
383 */
384 AssertPtr(ppszValue);
385 *ppszValue = NULL;
386 AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
387
388 /*
389 * There is a race here between our reading the property size and the
390 * host changing the value before we read it. Try up to ten times and
391 * report the problem if that fails.
392 */
393 char *pszValue = NULL;
394 void *pvBuf = NULL;
395 uint32_t cchBuf = MAX_VALUE_LEN;
396 int rc = VERR_BUFFER_OVERFLOW;
397 for (unsigned i = 0; i < 10 && rc == VERR_BUFFER_OVERFLOW; ++i)
398 {
399 /* We leave a bit of space here in case the maximum value is raised. */
400 cchBuf += 1024;
401 void *pvTmpBuf = RTMemRealloc(pvBuf, cchBuf);
402 if (pvTmpBuf)
403 {
404 pvBuf = pvTmpBuf;
405 rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cchBuf,
406 &pszValue, NULL, NULL, &cchBuf);
407 }
408 else
409 rc = VERR_NO_MEMORY;
410 }
411 if (RT_SUCCESS(rc))
412 {
413 Assert(pszValue == (char *)pvBuf);
414 *ppszValue = pszValue;
415 }
416 else
417 {
418 RTMemFree(pvBuf);
419 if (rc == VERR_BUFFER_OVERFLOW)
420 /* VERR_BUFFER_OVERFLOW has a different meaning here as a
421 * return code, but we need to report the race. */
422 rc = VERR_TOO_MUCH_DATA;
423 }
424
425 return rc;
426}
427
428
429/**
430 * Free the memory used by VbglR3GuestPropReadValueAlloc for returning a
431 * value.
432 *
433 * @param pszValue the memory to be freed. NULL pointers will be ignored.
434 */
435VBGLR3DECL(void) VbglR3GuestPropReadValueFree(char *pszValue)
436{
437 RTMemFree(pszValue);
438}
439#endif /* VBOX_VBGLR3_XFREE86 */
440
441/**
442 * Retrieve a property value, using a user-provided buffer to store it.
443 *
444 * @returns VBox status code.
445 * @retval VINF_SUCCESS on success, pszValue containing valid data.
446 * @retval VERR_BUFFER_OVERFLOW and the size needed in pcchValueActual if the
447 * buffer provided was too small
448 * @retval VERR_NOT_FOUND if the key wasn't found.
449 *
450 * @note There is a race here between obtaining the size of the buffer
451 * needed to hold the value and the value being updated.
452 *
453 * @param u32ClientId The client id returned by VbglR3GuestPropConnect().
454 * @param pszName The value to read. Utf8
455 * @param pszValue Where to store the value retrieved.
456 * @param cchValue The size of the buffer pointed to by @a pszValue
457 * @param pcchValueActual Where to store the size of the buffer needed if
458 * the buffer supplied is too small. Optional.
459 */
460VBGLR3DECL(int) VbglR3GuestPropReadValue(uint32_t u32ClientId, const char *pszName,
461 char *pszValue, uint32_t cchValue,
462 uint32_t *pcchValueActual)
463{
464 void *pvBuf = pszValue;
465 uint32_t cchValueActual;
466 int rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cchValue,
467 &pszValue, NULL, NULL, &cchValueActual);
468 if (pcchValueActual != NULL)
469 *pcchValueActual = cchValueActual;
470 return rc;
471}
472
473
474#ifndef VBOX_VBGLR3_XFREE86
475/**
476 * Raw API for enumerating guest properties which match a given pattern.
477 *
478 * @returns VBox status code.
479 * @retval VINF_SUCCESS on success and pcBuf points to a packed array
480 * of the form <name>, <value>, <timestamp string>, <flags>,
481 * terminated by four empty strings. pcbBufActual will contain the
482 * total size of the array.
483 * @retval VERR_BUFFER_OVERFLOW if the buffer provided was too small. In
484 * this case pcbBufActual will contain the size of the buffer needed.
485 * @returns IPRT error code in other cases, and pchBufActual is undefined.
486 *
487 * @param u32ClientId The client ID returned by VbglR3GuestPropConnect
488 * @param paszPatterns A packed array of zero terminated strings, terminated
489 * by an empty string.
490 * @param pcBuf The buffer to store the results to.
491 * @param cbBuf The size of the buffer
492 * @param pcbBufActual Where to store the size of the returned data on
493 * success or the buffer size needed if @a pcBuf is too
494 * small.
495 */
496VBGLR3DECL(int) VbglR3GuestPropEnumRaw(uint32_t u32ClientId,
497 const char *pszzPatterns,
498 char *pcBuf,
499 uint32_t cbBuf,
500 uint32_t *pcbBufActual)
501{
502 EnumProperties Msg;
503
504 Msg.hdr.result = VERR_WRONG_ORDER;
505 Msg.hdr.u32ClientID = u32ClientId;
506 Msg.hdr.u32Function = ENUM_PROPS;
507 Msg.hdr.cParms = 3;
508 /* Get the length of the patterns array... */
509 size_t cchPatterns = 0;
510 for (size_t cchCurrent = strlen(pszzPatterns); cchCurrent != 0;
511 cchCurrent = strlen(pszzPatterns + cchPatterns))
512 cchPatterns += cchCurrent + 1;
513 /* ...including the terminator. */
514 ++cchPatterns;
515 VbglHGCMParmPtrSet(&Msg.patterns, (char *)pszzPatterns, (uint32_t)cchPatterns);
516 VbglHGCMParmPtrSet(&Msg.strings, pcBuf, cbBuf);
517 VbglHGCMParmUInt32Set(&Msg.size, 0);
518
519 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
520 if (RT_SUCCESS(rc))
521 rc = Msg.hdr.result;
522 if ( pcbBufActual
523 && ( RT_SUCCESS(rc)
524 || rc == VERR_BUFFER_OVERFLOW))
525 {
526 int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
527 if (!RT_SUCCESS(rc2))
528 rc = rc2;
529 }
530 return rc;
531}
532
533
534/**
535 * Start enumerating guest properties which match a given pattern.
536 * This function creates a handle which can be used to continue enumerating.
537 *
538 * @returns VBox status code.
539 * @retval VINF_SUCCESS on success, *ppHandle points to a handle for continuing
540 * the enumeration and *ppszName, *ppszValue, *pu64Timestamp and
541 * *ppszFlags are set.
542 * @retval VERR_NOT_FOUND if no matching properties were found. In this case
543 * the return parameters are not initialised.
544 * @retval VERR_TOO_MUCH_DATA if it was not possible to determine the amount
545 * of local space needed to store all the enumeration data. This is
546 * due to a race between allocating space and the host adding new
547 * data, so retrying may help here. Other parameters are left
548 * uninitialised
549 *
550 * @param u32ClientId The client id returned by VbglR3InfoSvcConnect().
551 * @param papszPatterns The patterns against which the properties are
552 * matched. Pass NULL if everything should be matched.
553 * @param cPatterns The number of patterns in @a papszPatterns. 0 means
554 * match everything.
555 * @param ppHandle where the handle for continued enumeration is stored
556 * on success. This must be freed with
557 * VbglR3GuestPropEnumFree when it is no longer needed.
558 * @param ppszName Where to store the first property name on success.
559 * Should not be freed. Optional.
560 * @param ppszValue Where to store the first property value on success.
561 * Should not be freed. Optional.
562 * @param ppszValue Where to store the first timestamp value on success.
563 * Optional.
564 * @param ppszFlags Where to store the first flags value on success.
565 * Should not be freed. Optional.
566 */
567VBGLR3DECL(int) VbglR3GuestPropEnum(uint32_t u32ClientId,
568 char const * const *papszPatterns,
569 uint32_t cPatterns,
570 PVBGLR3GUESTPROPENUM *ppHandle,
571 char const **ppszName,
572 char const **ppszValue,
573 uint64_t *pu64Timestamp,
574 char const **ppszFlags)
575{
576 /* Create the handle. */
577 RTMemAutoPtr<VBGLR3GUESTPROPENUM, VbglR3GuestPropEnumFree> Handle;
578 Handle = (PVBGLR3GUESTPROPENUM)RTMemAllocZ(sizeof(VBGLR3GUESTPROPENUM));
579 if (!Handle)
580 return VERR_NO_MEMORY;
581
582 /* Get the length of the pattern string, including the final terminator. */
583 size_t cchPatterns = 1;
584 for (uint32_t i = 0; i < cPatterns; ++i)
585 cchPatterns += strlen(papszPatterns[i]) + 1;
586
587 /* Pack the pattern array */
588 RTMemAutoPtr<char> Patterns;
589 Patterns = (char *)RTMemAlloc(cchPatterns);
590 size_t off = 0;
591 for (uint32_t i = 0; i < cPatterns; ++i)
592 {
593 size_t cb = strlen(papszPatterns[i]) + 1;
594 memcpy(&Patterns[off], papszPatterns[i], cb);
595 off += cb;
596 }
597 Patterns[off] = '\0';
598
599 /* Randomly chosen initial size for the buffer to hold the enumeration
600 * information. */
601 uint32_t cchBuf = 4096;
602 RTMemAutoPtr<char> Buf;
603
604 /* In reading the guest property data we are racing against the host
605 * adding more of it, so loop a few times and retry on overflow. */
606 int rc = VINF_SUCCESS;
607 for (int i = 0; i < 10; ++i)
608 {
609 if (!Buf.realloc(cchBuf))
610 {
611 rc = VERR_NO_MEMORY;
612 break;
613 }
614 rc = VbglR3GuestPropEnumRaw(u32ClientId, Patterns.get(),
615 Buf.get(), cchBuf, &cchBuf);
616 if (rc != VERR_BUFFER_OVERFLOW)
617 break;
618 cchBuf += 4096; /* Just to increase our chances */
619 }
620 if (RT_SUCCESS(rc))
621 {
622 /*
623 * Transfer ownership of the buffer to the handle structure and
624 * call VbglR3GuestPropEnumNext to retriev the first entry.
625 */
626 Handle->pchNext = Handle->pchBuf = Buf.release();
627 Handle->pchBufEnd = Handle->pchBuf + cchBuf;
628
629 const char *pszNameTmp;
630 if (!ppszName)
631 ppszName = &pszNameTmp;
632 rc = VbglR3GuestPropEnumNext(Handle.get(), ppszName, ppszValue,
633 pu64Timestamp, ppszFlags);
634 if (RT_SUCCESS(rc) && *ppszName != NULL)
635 *ppHandle = Handle.release();
636 else if (RT_SUCCESS(rc))
637 rc = VERR_NOT_FOUND; /* No matching properties found. */
638 }
639 else if (rc == VERR_BUFFER_OVERFLOW)
640 rc = VERR_TOO_MUCH_DATA;
641 return rc;
642}
643
644
645/**
646 * Get the next guest property.
647 *
648 * See @a VbglR3GuestPropEnum.
649 *
650 * @returns VBox status code.
651 *
652 * @param pHandle Handle obtained from @a VbglR3GuestPropEnum.
653 * @param ppszName Where to store the next property name. This will be
654 * set to NULL if there are no more properties to
655 * enumerate. This pointer should not be freed. Optional.
656 * @param ppszValue Where to store the next property value. This will be
657 * set to NULL if there are no more properties to
658 * enumerate. This pointer should not be freed. Optional.
659 * @param pu64Timestamp Where to store the next property timestamp. This
660 * will be set to zero if there are no more properties
661 * to enumerate. Optional.
662 * @param ppszFlags Where to store the next property flags. This will be
663 * set to NULL if there are no more properties to
664 * enumerate. This pointer should not be freed. Optional.
665 *
666 * @remarks While all output parameters are optional, you need at least one to
667 * figure out when to stop.
668 */
669VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle,
670 char const **ppszName,
671 char const **ppszValue,
672 uint64_t *pu64Timestamp,
673 char const **ppszFlags)
674{
675 /*
676 * The VBGLR3GUESTPROPENUM structure contains a buffer containing the raw
677 * properties data and a pointer into the buffer which tracks how far we
678 * have parsed so far. The buffer contains packed strings in groups of
679 * four - name, value, timestamp (as a decimal string) and flags. It is
680 * terminated by four empty strings. We can rely on this layout unless
681 * the caller has been poking about in the structure internals, in which
682 * case they must take responsibility for the results.
683 *
684 * Layout:
685 * Name\0Value\0Timestamp\0Flags\0
686 */
687 char *pchNext = pHandle->pchNext; /* The cursor. */
688 char *pchEnd = pHandle->pchBufEnd; /* End of buffer, for size calculations. */
689
690 char *pszName = pchNext;
691 char *pszValue = pchNext = (char *)memchr(pchNext, '\0', pchEnd - pchNext) + 1;
692 AssertPtrReturn(pchNext, VERR_PARSE_ERROR); /* 0x1 is also an invalid pointer :) */
693
694 char *pszTimestamp = pchNext = (char *)memchr(pchNext, '\0', pchEnd - pchNext) + 1;
695 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
696
697 char *pszFlags = pchNext = (char *)memchr(pchNext, '\0', pchEnd - pchNext) + 1;
698 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
699
700 /*
701 * Don't move the index pointer if we found the terminating "\0\0\0\0" entry.
702 * Don't try convert the timestamp either.
703 */
704 uint64_t u64Timestamp;
705 if (*pszName != '\0')
706 {
707 pchNext = (char *)memchr(pchNext, '\0', pchEnd - pchNext) + 1;
708 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
709
710 /* Convert the timestamp string into a number. */
711 int rc = RTStrToUInt64Full(pszTimestamp, 0, &u64Timestamp);
712 AssertRCSuccessReturn(rc, VERR_PARSE_ERROR);
713
714 pHandle->pchNext = pchNext;
715 AssertPtr(pchNext);
716 }
717 else
718 {
719 u64Timestamp = 0;
720 AssertMsgReturn(!*pszValue && !*pszTimestamp && !*pszFlags,
721 ("'%s' '%s' '%s'\n", pszValue, pszTimestamp, pszFlags),
722 VERR_PARSE_ERROR);
723 }
724
725 /*
726 * Everything is fine, set the return values.
727 */
728 if (ppszName)
729 *ppszName = *pszName != '\0' ? pszName : NULL;
730 if (ppszValue)
731 *ppszValue = *pszValue != '\0' ? pszValue : NULL;
732 if (pu64Timestamp)
733 *pu64Timestamp = u64Timestamp;
734 if (ppszFlags)
735 *ppszFlags = *pszFlags != '\0' ? pszFlags : NULL;
736 return VINF_SUCCESS;
737}
738
739
740/**
741 * Free an enumeration handle returned by @a VbglR3GuestPropEnum.
742 * @param pHandle the handle to free
743 */
744VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle)
745{
746 RTMemFree(pHandle->pchBuf);
747 RTMemFree(pHandle);
748}
749
750
751/**
752 * Deletes a set of keys.
753 *
754 * The set is specified in the same way as for VbglR3GuestPropEnum.
755 *
756 * @returns VBox status code. Stops on first failure.
757 * See also VbglR3GuestPropEnum.
758 *
759 * @param u32ClientId The client id returned by VbglR3InfoSvcConnect().
760 * @param papszPatterns The patterns against which the properties are
761 * matched. Pass NULL if everything should be matched.
762 * @param cPatterns The number of patterns in @a papszPatterns. 0 means
763 * match everything.
764 */
765VBGLR3DECL(int) VbglR3GuestPropDelSet(uint32_t u32ClientId,
766 const char * const *papszPatterns,
767 uint32_t cPatterns)
768{
769 PVBGLR3GUESTPROPENUM pHandle;
770 char const *pszName, *pszValue, *pszFlags;
771 uint64_t pu64Timestamp;
772 int rc = VbglR3GuestPropEnum(u32ClientId,
773 (char **)papszPatterns, /** @todo fix this cast. */
774 cPatterns,
775 &pHandle,
776 &pszName,
777 &pszValue,
778 &pu64Timestamp,
779 &pszFlags);
780
781 while (RT_SUCCESS(rc) && pszName)
782 {
783 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
784 if (!RT_SUCCESS(rc))
785 break;
786
787 rc = VbglR3GuestPropEnumNext(pHandle,
788 &pszName,
789 &pszValue,
790 &pu64Timestamp,
791 &pszFlags);
792 }
793
794 VbglR3GuestPropEnumFree(pHandle);
795 return rc;
796}
797
798
799/**
800 * Wait for notification of changes to a guest property. If this is called in
801 * a loop, the timestamp of the last notification seen can be passed as a
802 * parameter to be sure that no notifications are missed.
803 *
804 * @returns VBox status code.
805 * @retval VINF_SUCCESS on success, @a ppszName, @a ppszValue,
806 * @a pu64Timestamp and @a ppszFlags containing valid data.
807 * @retval VINF_NOT_FOUND if no previous notification could be found with the
808 * timestamp supplied. This will normally mean that a large number
809 * of notifications occurred in between.
810 * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pvBuf is not large
811 * enough. In this case the size needed will be placed in
812 * @a pcbBufActual if it is not NULL.
813 * @retval VERR_TIMEOUT if a timeout occurred before a notification was seen.
814 *
815 * @param u32ClientId The client id returned by VbglR3GuestPropConnect().
816 * @param pszPatterns The patterns that the property names must matchfor
817 * the change to be reported.
818 * @param pvBuf A scratch buffer to store the data retrieved into.
819 * The returned data is only valid for it's lifetime.
820 * @a ppszValue will point to the start of this buffer.
821 * @param cbBuf The size of @a pvBuf
822 * @param u64Timestamp The timestamp of the last event seen. Pass zero
823 * to wait for the next event.
824 * @param cMillies Timeout in milliseconds. Use RT_INDEFINITE_WAIT
825 * to wait indefinitely.
826 * @param ppszName Where to store the pointer to the name retrieved.
827 * Optional.
828 * @param ppszValue Where to store the pointer to the value retrieved.
829 * Optional.
830 * @param pu64Timestamp Where to store the timestamp. Optional.
831 * @param ppszFlags Where to store the pointer to the flags. Optional.
832 * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
833 * Optional.
834 */
835VBGLR3DECL(int) VbglR3GuestPropWait(uint32_t u32ClientId,
836 const char *pszPatterns,
837 void *pvBuf, uint32_t cbBuf,
838 uint64_t u64Timestamp, uint32_t cMillies,
839 char ** ppszName, char **ppszValue,
840 uint64_t *pu64Timestamp, char **ppszFlags,
841 uint32_t *pcbBufActual)
842{
843 /*
844 * Create the GET_NOTIFICATION message and call the host.
845 */
846 GetNotification Msg;
847
848 Msg.hdr.u32Timeout = cMillies;
849 Msg.hdr.fInterruptible = true;
850 Msg.hdr.info.result = VERR_WRONG_ORDER;
851 Msg.hdr.info.u32ClientID = u32ClientId;
852 Msg.hdr.info.u32Function = GET_NOTIFICATION;
853 Msg.hdr.info.cParms = 4;
854 VbglHGCMParmPtrSetString(&Msg.patterns, pszPatterns);
855 Msg.buffer.SetPtr(pvBuf, cbBuf);
856 Msg.timestamp.SetUInt64(u64Timestamp);
857 Msg.size.SetUInt32(0);
858
859 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL_TIMED(sizeof(Msg)), &Msg, sizeof(Msg));
860 if (RT_SUCCESS(rc))
861 rc = Msg.hdr.info.result;
862
863 /*
864 * The cbBufActual parameter is also returned on overflow so the caller can
865 * adjust their buffer.
866 */
867 if ( rc == VERR_BUFFER_OVERFLOW
868 || pcbBufActual != NULL)
869 {
870 int rc2 = Msg.size.GetUInt32(pcbBufActual);
871 AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
872 }
873 if (RT_FAILURE(rc))
874 return rc;
875
876 /*
877 * Buffer layout: Name\0Value\0Flags\0.
878 *
879 * If the caller cares about any of these strings, make sure things are
880 * propertly terminated (paranoia).
881 */
882 if ( RT_SUCCESS(rc)
883 && (ppszName != NULL || ppszValue != NULL || ppszFlags != NULL))
884 {
885 /* Validate / skip 'Name'. */
886 char *pszValue = (char *)memchr(pvBuf, '\0', cbBuf) + 1;
887 AssertPtrReturn(pszValue, VERR_TOO_MUCH_DATA);
888 if (ppszName)
889 *ppszName = (char *)pvBuf;
890
891 /* Validate / skip 'Value'. */
892 char *pszFlags = (char *)memchr(pszValue, '\0',
893 cbBuf - (pszValue - (char *)pvBuf)) + 1;
894 AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
895 if (ppszValue)
896 *ppszValue = pszValue;
897
898 if (ppszFlags)
899 {
900 /* Validate 'Flags'. */
901 void *pvEos = memchr(pszFlags, '\0', cbBuf - (pszFlags - (char *)pvBuf));
902 AssertPtrReturn(pvEos, VERR_TOO_MUCH_DATA);
903 *ppszFlags = pszFlags;
904 }
905 }
906
907 /* And the timestamp, if requested. */
908 if (pu64Timestamp != NULL)
909 {
910 rc = Msg.timestamp.GetUInt64(pu64Timestamp);
911 AssertRCReturn(rc, rc);
912 }
913
914 return VINF_SUCCESS;
915}
916#endif /* VBOX_VBGLR3_XFREE86 */
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