VirtualBox

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

Last change on this file since 31386 was 31386, checked in by vboxsync, 14 years ago

X11 additions: one other reference to a libc symbol

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