/** @file * Guest property service - Common header for host service and guest clients. */ /* * Copyright (C) 2006-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ #ifndef VBOX_INCLUDED_HostServices_GuestPropertySvc_h #define VBOX_INCLUDED_HostServices_GuestPropertySvc_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif #include #include #include #include #include #include /** Maximum size for property names (including the terminator). */ #define GUEST_PROP_MAX_NAME_LEN 64 /** Maximum size for property values (including the terminator). */ #define GUEST_PROP_MAX_VALUE_LEN 1024 /** Maximum number of properties per guest. */ #define GUEST_PROP_MAX_PROPS 256 /** Maximum size for enumeration patterns (including the terminators). */ #define GUEST_PROP_MAX_PATTERN_LEN 1024 /** Maximum number of changes we remember for guest notifications. */ #define GUEST_PROP_MAX_GUEST_NOTIFICATIONS 256 /** Maximum number of current pending waits per client. */ #define GUEST_PROP_MAX_GUEST_CONCURRENT_WAITS 16 /** @name GUEST_PROP_F_XXX - The guest property flag values which are currently accepted. * @{ */ #define GUEST_PROP_F_NILFLAG UINT32_C(0) /** Transient until VM gets shut down. */ #define GUEST_PROP_F_TRANSIENT RT_BIT_32(1) #define GUEST_PROP_F_RDONLYGUEST RT_BIT_32(2) #define GUEST_PROP_F_RDONLYHOST RT_BIT_32(3) /** Transient until VM gets a reset / restarts. * Implies TRANSIENT. */ #define GUEST_PROP_F_TRANSRESET RT_BIT_32(4) #define GUEST_PROP_F_READONLY (GUEST_PROP_F_RDONLYGUEST | GUEST_PROP_F_RDONLYHOST) #define GUEST_PROP_F_ALLFLAGS (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_READONLY | GUEST_PROP_F_TRANSRESET) /** @} */ /** * Check that a string fits our criteria for a property name. * * @returns IPRT status code * @param pszName The string to check, must be valid Utf8 * @param cbName The number of bytes @a pszName points to, including the * terminating character. */ DECLINLINE(int) GuestPropValidateName(const char *pszName, size_t cbName) { if ( cbName < 2 /* Property name is expected to be at least 1 charecter long plus terminating character. */ || cbName > GUEST_PROP_MAX_NAME_LEN) return VERR_INVALID_PARAMETER; AssertPtrReturn(pszName, VERR_INVALID_POINTER); AssertReturn(memchr(pszName, '*', cbName) == NULL, VERR_INVALID_PARAMETER); AssertReturn(memchr(pszName, '?', cbName) == NULL, VERR_INVALID_PARAMETER); AssertReturn(memchr(pszName, '|', cbName) == NULL, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } /** * Check a string fits our criteria for the value of a guest property. * * @returns IPRT status code * @retval VINF_SUCCESS if guest property value corresponds to all criteria. * @retval VERR_TOO_MUCH_DATA if guest property value size exceeds limits. * @retval VERR_INVALID_PARAMETER if guest property does not correspond to all other criteria. * @param pszValue The string to check, must be valid utf-8. * @param cbValue The size of of @a pszValue in bytes, including the * terminator. * @thread HGCM */ DECLINLINE(int) GuestPropValidateValue(const char *pszValue, size_t cbValue) { AssertPtrReturn(pszValue, VERR_INVALID_POINTER); /* Zero-length values are possible, however buffer should contain terminating character at least. */ AssertReturn(cbValue > 0, VERR_INVALID_PARAMETER); if (cbValue > GUEST_PROP_MAX_VALUE_LEN) return VERR_TOO_MUCH_DATA; return VINF_SUCCESS; } /** * Get the name of a flag as a string. * @returns the name, or NULL if fFlag is invalid. * @param fFlag The flag, GUEST_PROP_F_XXX. * @param pcchName Where to return the name length. */ DECLINLINE(const char *) GuestPropFlagNameAndLen(uint32_t fFlag, size_t *pcchName) { switch (fFlag) { case GUEST_PROP_F_TRANSIENT: *pcchName = sizeof("TRANSIENT") - 1; return "TRANSIENT"; case GUEST_PROP_F_RDONLYGUEST: *pcchName = sizeof("RDONLYGUEST") - 1; return "RDONLYGUEST"; case GUEST_PROP_F_RDONLYHOST: *pcchName = sizeof("RDONLYHOST") - 1; return "RDONLYHOST"; case GUEST_PROP_F_READONLY: *pcchName = sizeof("READONLY") - 1; return "READONLY"; case GUEST_PROP_F_TRANSRESET: *pcchName = sizeof("TRANSRESET") - 1; return "TRANSRESET"; default: *pcchName = 0; return NULL; } } /** * Maximum length for the property flags field. We only ever return one of * RDONLYGUEST, RDONLYHOST and RDONLY */ #define GUEST_PROP_MAX_FLAGS_LEN sizeof("TRANSIENT, RDONLYGUEST, TRANSRESET") /** * Parse a guest properties flags string for flag names and make sure that * there is no junk text in the string. * * @returns IPRT status code * @retval VERR_INVALID_PARAMETER if the flag string is not valid * @param pcszFlags the flag string to parse * @param pfFlags where to store the parse result. May not be NULL. * @note This function is also inline because it must be accessible from * several modules and it does not seem reasonable to put it into * its own library. */ DECLINLINE(int) GuestPropValidateFlags(const char *pcszFlags, uint32_t *pfFlags) { static const uint32_t s_aFlagList[] = { GUEST_PROP_F_TRANSIENT, GUEST_PROP_F_READONLY, GUEST_PROP_F_RDONLYGUEST, GUEST_PROP_F_RDONLYHOST, GUEST_PROP_F_TRANSRESET }; const char *pcszNext = pcszFlags; int rc = VINF_SUCCESS; uint32_t fFlags = 0; AssertLogRelReturn(RT_VALID_PTR(pfFlags), VERR_INVALID_POINTER); if (pcszFlags) { while (*pcszNext == ' ') ++pcszNext; while ((*pcszNext != '\0') && RT_SUCCESS(rc)) { unsigned i; rc = VERR_PARSE_ERROR; for (i = 0; i < RT_ELEMENTS(s_aFlagList); ++i) { size_t cchFlagName; const char *pszFlagName = GuestPropFlagNameAndLen(s_aFlagList[i], &cchFlagName); if (RTStrNICmpAscii(pcszNext, pszFlagName, cchFlagName) == 0) { char ch; fFlags |= s_aFlagList[i]; pcszNext += cchFlagName; while ((ch = *pcszNext) == ' ') ++pcszNext; rc = VINF_SUCCESS; if (ch == ',') { ++pcszNext; while (*pcszNext == ' ') ++pcszNext; } else if (ch != '\0') rc = VERR_PARSE_ERROR; break; } } } } if (RT_SUCCESS(rc)) *pfFlags = fFlags; return rc; } /** * Write out flags to a string. * @returns IPRT status code * @param fFlags the flags to write out * @param pszFlags where to write the flags string. This must point to * a buffer of size (at least) GUEST_PROP_MAX_FLAGS_LEN. */ DECLINLINE(int) GuestPropWriteFlags(uint32_t fFlags, char *pszFlags) { /* Putting READONLY before the other RDONLY flags keeps the result short. */ static const uint32_t s_aFlagList[] = { GUEST_PROP_F_TRANSIENT, GUEST_PROP_F_READONLY, GUEST_PROP_F_RDONLYGUEST, GUEST_PROP_F_RDONLYHOST, GUEST_PROP_F_TRANSRESET }; int rc = VINF_SUCCESS; AssertLogRelReturn(RT_VALID_PTR(pszFlags), VERR_INVALID_POINTER); if ((fFlags & ~GUEST_PROP_F_ALLFLAGS) == GUEST_PROP_F_NILFLAG) { char *pszNext; unsigned i; /* TRANSRESET implies TRANSIENT. For compatability with old clients we always set TRANSIENT when TRANSRESET appears. */ if (fFlags & GUEST_PROP_F_TRANSRESET) fFlags |= GUEST_PROP_F_TRANSIENT; pszNext = pszFlags; for (i = 0; i < RT_ELEMENTS(s_aFlagList); ++i) { if (s_aFlagList[i] == (fFlags & s_aFlagList[i])) { size_t cchFlagName; const char *pszFlagName = GuestPropFlagNameAndLen(s_aFlagList[i], &cchFlagName); memcpy(pszNext, pszFlagName, cchFlagName); pszNext += cchFlagName; fFlags &= ~s_aFlagList[i]; if (fFlags != GUEST_PROP_F_NILFLAG) { *pszNext++ = ','; *pszNext++ = ' '; } } } *pszNext = '\0'; Assert((uintptr_t)(pszNext - pszFlags) < GUEST_PROP_MAX_FLAGS_LEN); Assert(fFlags == GUEST_PROP_F_NILFLAG); /* bad s_aFlagList */ } else rc = VERR_INVALID_PARAMETER; return rc; } /** @name The service functions which are callable by host. * @{ */ /** Set properties in a block. * The parameters are pointers to NULL-terminated arrays containing the * parameters. These are, in order, name, value, timestamp, flags. Strings are * stored as pointers to mutable utf8 data. All parameters must be supplied. */ #define GUEST_PROP_FN_HOST_SET_PROPS 1 /** Get the value attached to a guest property. * The parameter format matches that of GET_PROP. */ #define GUEST_PROP_FN_HOST_GET_PROP 2 /** Set the value attached to a guest property. * The parameter format matches that of SET_PROP. */ #define GUEST_PROP_FN_HOST_SET_PROP 3 /** Set the value attached to a guest property. * The parameter format matches that of SET_PROP_VALUE. */ #define GUEST_PROP_FN_HOST_SET_PROP_VALUE 4 /** Remove a guest property. * The parameter format matches that of DEL_PROP. */ #define GUEST_PROP_FN_HOST_DEL_PROP 5 /** Enumerate guest properties. * The parameter format matches that of ENUM_PROPS. */ #define GUEST_PROP_FN_HOST_ENUM_PROPS 6 /** Set global flags for the service. * Currently RDONLYGUEST is supported. Takes one 32-bit unsigned integer * parameter for the flags. */ #define GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS 7 /** @} */ /** @name The service functions which are called by guest. * * @note The numbers may not change! * @{ */ /** Get a guest property */ #define GUEST_PROP_FN_GET_PROP 1 /** Set a guest property */ #define GUEST_PROP_FN_SET_PROP 2 /** Set just the value of a guest property */ #define GUEST_PROP_FN_SET_PROP_VALUE 3 /** Delete a guest property */ #define GUEST_PROP_FN_DEL_PROP 4 /** Enumerate guest properties */ #define GUEST_PROP_FN_ENUM_PROPS 5 /** Poll for guest notifications */ #define GUEST_PROP_FN_GET_NOTIFICATION 6 /** @} */ /** * Data structure to pass to the service extension callback. * We use this to notify the host of changes to properties. */ typedef struct GUESTPROPHOSTCALLBACKDATA { /** Magic number to identify the structure (GUESTPROPHOSTCALLBACKDATA_MAGIC). */ uint32_t u32Magic; /** The name of the property that was changed */ const char *pcszName; /** The new property value, or NULL if the property was deleted */ const char *pcszValue; /** The timestamp of the modification */ uint64_t u64Timestamp; /** The flags field of the modified property */ const char *pcszFlags; } GUESTPROPHOSTCALLBACKDATA; /** Poitner to a data structure to pass to the service extension callback. */ typedef GUESTPROPHOSTCALLBACKDATA *PGUESTPROPHOSTCALLBACKDATA; /** Magic number for sanity checking the HOSTCALLBACKDATA structure */ #define GUESTPROPHOSTCALLBACKDATA_MAGIC UINT32_C(0x69c87a78) /** * HGCM parameter structures. Packing is explicitly defined as this is a wire format. */ /** The guest is requesting the value of a property */ typedef struct GuestPropMsgGetProperty { VBGLIOCHGCMCALL hdr; /** * The property name (IN pointer) * This must fit to a number of criteria, namely * - Only Utf8 strings are allowed * - Less than or equal to MAX_NAME_LEN bytes in length * - Zero terminated */ HGCMFunctionParameter name; /** * The returned string data will be placed here. (OUT pointer) * This call returns two null-terminated strings which will be placed one * after another: value and flags. */ HGCMFunctionParameter buffer; /** * The property timestamp. (OUT uint64_t) */ HGCMFunctionParameter timestamp; /** * If the buffer provided was large enough this will contain the size of * the returned data. Otherwise it will contain the size of the buffer * needed to hold the data and VERR_BUFFER_OVERFLOW will be returned. * (OUT uint32_t) */ HGCMFunctionParameter size; } GuestPropMsgGetProperty; AssertCompileSize(GuestPropMsgGetProperty, 40 + 4 * (ARCH_BITS == 64 ? 16 : 12)); /** The guest is requesting to change a property */ typedef struct GuestPropMsgSetProperty { VBGLIOCHGCMCALL hdr; /** * The property name. (IN pointer) * This must fit to a number of criteria, namely * - Only Utf8 strings are allowed * - Less than or equal to MAX_NAME_LEN bytes in length * - Zero terminated */ HGCMFunctionParameter name; /** * The value of the property (IN pointer) * Criteria as for the name parameter, but with length less than or equal to * MAX_VALUE_LEN. */ HGCMFunctionParameter value; /** * The property flags (IN pointer) * This is a comma-separated list of the format flag=value * The length must be less than or equal to GUEST_PROP_MAX_FLAGS_LEN and only * known flag names and values will be accepted. */ HGCMFunctionParameter flags; } GuestPropMsgSetProperty; AssertCompileSize(GuestPropMsgSetProperty, 40 + 3 * (ARCH_BITS == 64 ? 16 : 12)); /** The guest is requesting to change the value of a property */ typedef struct GuestPropMsgSetPropertyValue { VBGLIOCHGCMCALL hdr; /** * The property name. (IN pointer) * This must fit to a number of criteria, namely * - Only Utf8 strings are allowed * - Less than or equal to MAX_NAME_LEN bytes in length * - Zero terminated */ HGCMFunctionParameter name; /** * The value of the property (IN pointer) * Criteria as for the name parameter, but with length less than or equal to * MAX_VALUE_LEN. */ HGCMFunctionParameter value; } GuestPropMsgSetPropertyValue; AssertCompileSize(GuestPropMsgSetPropertyValue, 40 + 2 * (ARCH_BITS == 64 ? 16 : 12)); /** The guest is requesting to remove a property */ typedef struct GuestPropMsgDelProperty { VBGLIOCHGCMCALL hdr; /** * The property name. This must fit to a number of criteria, namely * - Only Utf8 strings are allowed * - Less than or equal to MAX_NAME_LEN bytes in length * - Zero terminated */ HGCMFunctionParameter name; } GuestPropMsgDelProperty; AssertCompileSize(GuestPropMsgDelProperty, 40 + 1 * (ARCH_BITS == 64 ? 16 : 12)); /** The guest is requesting to enumerate properties */ typedef struct GuestPropMsgEnumProperties { VBGLIOCHGCMCALL hdr; /** * Array of patterns to match the properties against, separated by '|' * characters. For backwards compatibility, '\\0' is also accepted * as a separater. * (IN pointer) * If only a single, empty pattern is given then match all. */ HGCMFunctionParameter patterns; /** * On success, null-separated array of strings in which the properties are * returned. (OUT pointer) * The number of strings in the array is always a multiple of four, * and in sequences of name, value, timestamp (hexadecimal string) and the * flags as a comma-separated list in the format "name=value". The list * is terminated by an empty string after a "flags" entry (or at the * start). */ HGCMFunctionParameter strings; /** * On success, the size of the returned data. If the buffer provided is * too small, the size of buffer needed. (OUT uint32_t) */ HGCMFunctionParameter size; } GuestPropMsgEnumProperties; AssertCompileSize(GuestPropMsgEnumProperties, 40 + 3 * (ARCH_BITS == 64 ? 16 : 12)); /** * The guest is polling for notifications on changes to properties, specifying * a set of patterns to match the names of changed properties against and * optionally the timestamp of the last notification seen. * On success, VINF_SUCCESS will be returned and the buffer will contain * details of a property notification. If no new notification is available * which matches one of the specified patterns, the call will block until one * is. * If the last notification could not be found by timestamp, VWRN_NOT_FOUND * will be returned and the oldest available notification will be returned. * If a zero timestamp is specified, the call will always wait for a new * notification to arrive. * If the buffer supplied was not large enough to hold the notification, * VERR_BUFFER_OVERFLOW will be returned and the size parameter will contain * the size of the buffer needed. * * The protocol for a guest to obtain notifications is to call * GET_NOTIFICATION in a loop. On the first call, the ingoing timestamp * parameter should be set to zero. On subsequent calls, it should be set to * the outgoing timestamp from the previous call. */ typedef struct GuestPropMsgGetNotification { VBGLIOCHGCMCALL hdr; /** * A list of patterns to match the guest event name against, separated by * vertical bars (|) (IN pointer) * An empty string means match all. */ HGCMFunctionParameter patterns; /** * The timestamp of the last change seen (IN uint64_t) * This may be zero, in which case the oldest available change will be * sent. If the service does not remember an event matching the * timestamp, then VWRN_NOT_FOUND will be returned, and the guest should * assume that it has missed a certain number of notifications. * * The timestamp of the change being notified of (OUT uint64_t) * Undefined on failure. */ HGCMFunctionParameter timestamp; /** * The returned data, if any, will be placed here. (OUT pointer) * This call returns three null-terminated strings which will be placed * one after another: name, value and flags. For a delete notification, * value and flags will be empty strings. Undefined on failure. */ HGCMFunctionParameter buffer; /** * On success, the size of the returned data. (OUT uint32_t) * On buffer overflow, the size of the buffer needed to hold the data. * Undefined on failure. */ HGCMFunctionParameter size; } GuestPropMsgGetNotification; AssertCompileSize(GuestPropMsgGetNotification, 40 + 4 * (ARCH_BITS == 64 ? 16 : 12)); #endif /* !VBOX_INCLUDED_HostServices_GuestPropertySvc_h */