VirtualBox

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

Last change on this file was 101720, checked in by vboxsync, 6 months ago

Additions: Guest Library: Fix valgrind warnings about uninitialized memory usage, bugref:10194.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use