VirtualBox

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

Last change on this file since 68462 was 68462, checked in by vboxsync, 7 years ago

VBoxGuestLib: Use common VbglR3HGCMDisconnect to disconnect.

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