VirtualBox

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

Last change on this file since 82528 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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