VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp@ 74140

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

VBoxGuest: VBGL_HGCM_HDR_INIT_TIMED, consistent use of VbglHGCMParm helpers.

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