VirtualBox

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

Last change on this file since 108621 was 108621, checked in by vboxsync, 8 weeks ago

Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp: clang build fixes, bugref:10391

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette