VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestProperties/service.cpp@ 72436

Last change on this file since 72436 was 70828, checked in by vboxsync, 7 years ago

Guest Properties: finer control of read-only-guest range.
bugref:9047: Clean up Linux guest vboxuser device
In r120474/public r70727 we made the whole /VirtualBox/GuestAdd/ Guest
Property range read-only to guests; some of those are used by the guest to
pass back information though, so split up the range to only include certain
subranges.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 57.0 KB
Line 
1/* $Id: service.cpp 70828 2018-01-31 12:59:34Z vboxsync $ */
2/** @file
3 * Guest Property Service: Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_svc_guest_properties Guest Property HGCM Service
19 *
20 * This HGCM service allows the guest to set and query values in a property
21 * store on the host. The service proxies the guest requests to the service
22 * owner on the host using a request callback provided by the owner, and is
23 * notified of changes to properties made by the host. It forwards these
24 * notifications to clients in the guest which have expressed interest and
25 * are waiting for notification.
26 *
27 * The service currently consists of two threads. One of these is the main
28 * HGCM service thread which deals with requests from the guest and from the
29 * host. The second thread sends the host asynchronous notifications of
30 * changes made by the guest and deals with notification timeouts.
31 *
32 * Guest requests to wait for notification are added to a list of open
33 * notification requests and completed when a corresponding guest property
34 * is changed or when the request times out.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_HGCM
42#include <VBox/HostServices/GuestPropertySvc.h>
43
44#include <VBox/log.h>
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/cpp/autores.h>
48#include <iprt/cpp/utils.h>
49#include <iprt/err.h>
50#include <iprt/mem.h>
51#include <iprt/req.h>
52#include <iprt/string.h>
53#include <iprt/thread.h>
54#include <iprt/time.h>
55#include <VBox/vmm/dbgf.h>
56
57#include <string>
58#include <list>
59
60/** @todo Delete the old !ASYNC_HOST_NOTIFY code and remove this define. */
61#define ASYNC_HOST_NOTIFY
62
63namespace guestProp {
64
65/**
66 * Structure for holding a property
67 */
68struct Property
69{
70 /** The string space core record. */
71 RTSTRSPACECORE mStrCore;
72 /** The name of the property */
73 std::string mName;
74 /** The property value */
75 std::string mValue;
76 /** The timestamp of the property */
77 uint64_t mTimestamp;
78 /** The property flags */
79 uint32_t mFlags;
80
81 /** Default constructor */
82 Property() : mTimestamp(0), mFlags(GUEST_PROP_F_NILFLAG)
83 {
84 RT_ZERO(mStrCore);
85 }
86 /** Constructor with const char * */
87 Property(const char *pcszName, const char *pcszValue,
88 uint64_t u64Timestamp, uint32_t u32Flags)
89 : mName(pcszName), mValue(pcszValue), mTimestamp(u64Timestamp),
90 mFlags(u32Flags)
91 {
92 RT_ZERO(mStrCore);
93 mStrCore.pszString = mName.c_str();
94 }
95 /** Constructor with std::string */
96 Property(std::string name, std::string value, uint64_t u64Timestamp,
97 uint32_t u32Flags)
98 : mName(name), mValue(value), mTimestamp(u64Timestamp),
99 mFlags(u32Flags) {}
100
101 /** Does the property name match one of a set of patterns? */
102 bool Matches(const char *pszPatterns) const
103 {
104 return ( pszPatterns[0] == '\0' /* match all */
105 || RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
106 mName.c_str(), RTSTR_MAX,
107 NULL)
108 );
109 }
110
111 /** Are two properties equal? */
112 bool operator==(const Property &prop)
113 {
114 if (mTimestamp != prop.mTimestamp)
115 return false;
116 if (mFlags != prop.mFlags)
117 return false;
118 if (mName != prop.mName)
119 return false;
120 if (mValue != prop.mValue)
121 return false;
122 return true;
123 }
124
125 /* Is the property nil? */
126 bool isNull()
127 {
128 return mName.empty();
129 }
130};
131/** The properties list type */
132typedef std::list <Property> PropertyList;
133
134/**
135 * Structure for holding an uncompleted guest call
136 */
137struct GuestCall
138{
139 uint32_t u32ClientId;
140 /** The call handle */
141 VBOXHGCMCALLHANDLE mHandle;
142 /** The function that was requested */
143 uint32_t mFunction;
144 /** Number of call parameters. */
145 uint32_t mParmsCnt;
146 /** The call parameters */
147 VBOXHGCMSVCPARM *mParms;
148 /** The default return value, used for passing warnings */
149 int mRc;
150
151 /** The standard constructor */
152 GuestCall(void) : u32ClientId(0), mFunction(0), mParmsCnt(0) {}
153 /** The normal constructor */
154 GuestCall(uint32_t aClientId, VBOXHGCMCALLHANDLE aHandle, uint32_t aFunction,
155 uint32_t aParmsCnt, VBOXHGCMSVCPARM aParms[], int aRc)
156 : u32ClientId(aClientId), mHandle(aHandle), mFunction(aFunction),
157 mParmsCnt(aParmsCnt), mParms(aParms), mRc(aRc) {}
158};
159/** The guest call list type */
160typedef std::list <GuestCall> CallList;
161
162/**
163 * Class containing the shared information service functionality.
164 */
165class Service : public RTCNonCopyable
166{
167private:
168 /** Type definition for use in callback functions */
169 typedef Service SELF;
170 /** HGCM helper functions. */
171 PVBOXHGCMSVCHELPERS mpHelpers;
172 /** Global flags for the service */
173 uint32_t mfGlobalFlags;
174 /** The property string space handle. */
175 RTSTRSPACE mhProperties;
176 /** The number of properties. */
177 unsigned mcProperties;
178 /** The list of property changes for guest notifications;
179 * only used for timestamp tracking in notifications at the moment */
180 PropertyList mGuestNotifications;
181 /** The list of outstanding guest notification calls */
182 CallList mGuestWaiters;
183 /** @todo we should have classes for thread and request handler thread */
184 /** Callback function supplied by the host for notification of updates
185 * to properties */
186 PFNHGCMSVCEXT mpfnHostCallback;
187 /** User data pointer to be supplied to the host callback function */
188 void *mpvHostData;
189 /** The previous timestamp.
190 * This is used by getCurrentTimestamp() to decrease the chance of
191 * generating duplicate timestamps. */
192 uint64_t mPrevTimestamp;
193 /** The number of consecutive timestamp adjustments that we've made.
194 * Together with mPrevTimestamp, this defines a set of obsolete timestamp
195 * values: {(mPrevTimestamp - mcTimestampAdjustments), ..., mPrevTimestamp} */
196 uint64_t mcTimestampAdjustments;
197
198 /**
199 * Get the next property change notification from the queue of saved
200 * notification based on the timestamp of the last notification seen.
201 * Notifications will only be reported if the property name matches the
202 * pattern given.
203 *
204 * @returns iprt status value
205 * @returns VWRN_NOT_FOUND if the last notification was not found in the queue
206 * @param pszPatterns the patterns to match the property name against
207 * @param u64Timestamp the timestamp of the last notification
208 * @param pProp where to return the property found. If none is
209 * found this will be set to nil.
210 * @thread HGCM
211 */
212 int getOldNotification(const char *pszPatterns, uint64_t u64Timestamp,
213 Property *pProp)
214 {
215 AssertPtrReturn(pszPatterns, VERR_INVALID_POINTER);
216 /* Zero means wait for a new notification. */
217 AssertReturn(u64Timestamp != 0, VERR_INVALID_PARAMETER);
218 AssertPtrReturn(pProp, VERR_INVALID_POINTER);
219 int rc = getOldNotificationInternal(pszPatterns, u64Timestamp, pProp);
220#ifdef VBOX_STRICT
221 /*
222 * ENSURE that pProp is the first event in the notification queue that:
223 * - Appears later than u64Timestamp
224 * - Matches the pszPatterns
225 */
226 /** @todo r=bird: This incorrectly ASSUMES that mTimestamp is unique.
227 * The timestamp resolution can be very coarse on windows for instance. */
228 PropertyList::const_iterator it = mGuestNotifications.begin();
229 for (; it != mGuestNotifications.end()
230 && it->mTimestamp != u64Timestamp; ++it)
231 {}
232 if (it == mGuestNotifications.end()) /* Not found */
233 it = mGuestNotifications.begin();
234 else
235 ++it; /* Next event */
236 for (; it != mGuestNotifications.end()
237 && it->mTimestamp != pProp->mTimestamp; ++it)
238 Assert(!it->Matches(pszPatterns));
239 if (pProp->mTimestamp != 0)
240 {
241 Assert(*pProp == *it);
242 Assert(pProp->Matches(pszPatterns));
243 }
244#endif /* VBOX_STRICT */
245 return rc;
246 }
247
248 /**
249 * Check whether we have permission to change a property.
250 *
251 * @returns Strict VBox status code.
252 * @retval VINF_SUCCESS if we do.
253 * @retval VERR_PERMISSION_DENIED if the value is read-only for the requesting
254 * side.
255 * @retval VINF_PERMISSION_DENIED if the side is globally marked read-only.
256 *
257 * @param fFlags the flags on the property in question
258 * @param isGuest is the guest or the host trying to make the change?
259 */
260 int checkPermission(uint32_t fFlags, bool isGuest)
261 {
262 if (fFlags & (isGuest ? GUEST_PROP_F_RDONLYGUEST : GUEST_PROP_F_RDONLYHOST))
263 return VERR_PERMISSION_DENIED;
264 if (isGuest && (mfGlobalFlags & GUEST_PROP_F_RDONLYGUEST))
265 return VINF_PERMISSION_DENIED;
266 return VINF_SUCCESS;
267 }
268
269 /**
270 * Check whether the property name is reserved for host changes only.
271 *
272 * @returns Boolean true (host reserved) or false (available to guest).
273 *
274 * @param pszName The property name to check.
275 */
276 bool checkHostReserved(const char *pszName)
277 {
278 if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/VBoxService/"))
279 return true;
280 if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/PAM/"))
281 return true;
282 if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/Greeter/"))
283 return true;
284 if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/SharedFolders/"))
285 return true;
286 if (RTStrStartsWith(pszName, "/VirtualBox/HostInfo/"))
287 return true;
288 return false;
289 }
290
291 /**
292 * Gets a property.
293 *
294 * @returns Pointer to the property if found, NULL if not.
295 *
296 * @param pszName The name of the property to get.
297 */
298 Property *getPropertyInternal(const char *pszName)
299 {
300 return (Property *)RTStrSpaceGet(&mhProperties, pszName);
301 }
302
303public:
304 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
305 : mpHelpers(pHelpers)
306 , mfGlobalFlags(GUEST_PROP_F_NILFLAG)
307 , mhProperties(NULL)
308 , mcProperties(0)
309 , mpfnHostCallback(NULL)
310 , mpvHostData(NULL)
311 , mPrevTimestamp(0)
312 , mcTimestampAdjustments(0)
313#ifdef ASYNC_HOST_NOTIFY
314 , mhThreadNotifyHost(NIL_RTTHREAD)
315 , mhReqQNotifyHost(NIL_RTREQQUEUE)
316#endif
317 { }
318
319 /**
320 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload}
321 * Simply deletes the service object
322 */
323 static DECLCALLBACK(int) svcUnload(void *pvService)
324 {
325 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
326 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
327 int rc = pSelf->uninit();
328 AssertRC(rc);
329 if (RT_SUCCESS(rc))
330 delete pSelf;
331 return rc;
332 }
333
334 /**
335 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect}
336 * Stub implementation of pfnConnect and pfnDisconnect.
337 */
338 static DECLCALLBACK(int) svcConnectDisconnect(void * /* pvService */,
339 uint32_t /* u32ClientID */,
340 void * /* pvClient */)
341 {
342 return VINF_SUCCESS;
343 }
344
345 /**
346 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
347 * Wraps to the call member function
348 */
349 static DECLCALLBACK(void) svcCall(void * pvService,
350 VBOXHGCMCALLHANDLE callHandle,
351 uint32_t u32ClientID,
352 void *pvClient,
353 uint32_t u32Function,
354 uint32_t cParms,
355 VBOXHGCMSVCPARM paParms[])
356 {
357 AssertLogRelReturnVoid(VALID_PTR(pvService));
358 LogFlowFunc(("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
359 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
360 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
361 LogFlowFunc(("returning\n"));
362 }
363
364 /**
365 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall}
366 * Wraps to the hostCall member function
367 */
368 static DECLCALLBACK(int) svcHostCall(void *pvService,
369 uint32_t u32Function,
370 uint32_t cParms,
371 VBOXHGCMSVCPARM paParms[])
372 {
373 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
374 LogFlowFunc(("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
375 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
376 int rc = pSelf->hostCall(u32Function, cParms, paParms);
377 LogFlowFunc(("rc=%Rrc\n", rc));
378 return rc;
379 }
380
381 /**
382 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension}
383 * Installs a host callback for notifications of property changes.
384 */
385 static DECLCALLBACK(int) svcRegisterExtension(void *pvService,
386 PFNHGCMSVCEXT pfnExtension,
387 void *pvExtension)
388 {
389 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
390 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
391 pSelf->mpfnHostCallback = pfnExtension;
392 pSelf->mpvHostData = pvExtension;
393 return VINF_SUCCESS;
394 }
395
396#ifdef ASYNC_HOST_NOTIFY
397 int initialize();
398#endif
399
400private:
401 static DECLCALLBACK(int) reqThreadFn(RTTHREAD ThreadSelf, void *pvUser);
402 uint64_t getCurrentTimestamp(void);
403 int validateName(const char *pszName, uint32_t cbName);
404 int validateValue(const char *pszValue, uint32_t cbValue);
405 int setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
406 int getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
407 int setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
408 int delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
409 int enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
410 int getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms,
411 VBOXHGCMSVCPARM paParms[]);
412 int getOldNotificationInternal(const char *pszPattern,
413 uint64_t u64Timestamp, Property *pProp);
414 int getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property prop);
415 int doNotifications(const char *pszProperty, uint64_t u64Timestamp);
416 int notifyHost(const char *pszName, const char *pszValue,
417 uint64_t u64Timestamp, const char *pszFlags);
418
419 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
420 void *pvClient, uint32_t eFunction, uint32_t cParms,
421 VBOXHGCMSVCPARM paParms[]);
422 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
423 int uninit();
424 void dbgInfoShow(PCDBGFINFOHLP pHlp);
425 static DECLCALLBACK(void) dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs);
426
427#ifdef ASYNC_HOST_NOTIFY
428 /* Thread for handling host notifications. */
429 RTTHREAD mhThreadNotifyHost;
430 /* Queue for handling requests for notifications. */
431 RTREQQUEUE mhReqQNotifyHost;
432 static DECLCALLBACK(int) threadNotifyHost(RTTHREAD self, void *pvUser);
433#endif
434
435 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(Service);
436};
437
438
439/**
440 * Gets the current timestamp.
441 *
442 * Since the RTTimeNow resolution can be very coarse, this method takes some
443 * simple steps to try avoid returning the same timestamp for two consecutive
444 * calls. Code like getOldNotification() more or less assumes unique
445 * timestamps.
446 *
447 * @returns Nanosecond timestamp.
448 */
449uint64_t Service::getCurrentTimestamp(void)
450{
451 RTTIMESPEC time;
452 uint64_t u64NanoTS = RTTimeSpecGetNano(RTTimeNow(&time));
453 if (mPrevTimestamp - u64NanoTS > mcTimestampAdjustments)
454 mcTimestampAdjustments = 0;
455 else
456 {
457 mcTimestampAdjustments++;
458 u64NanoTS = mPrevTimestamp + 1;
459 }
460 this->mPrevTimestamp = u64NanoTS;
461 return u64NanoTS;
462}
463
464/**
465 * Check that a string fits our criteria for a property name.
466 *
467 * @returns IPRT status code
468 * @param pszName the string to check, must be valid Utf8
469 * @param cbName the number of bytes @a pszName points to, including the
470 * terminating '\0'
471 * @thread HGCM
472 */
473int Service::validateName(const char *pszName, uint32_t cbName)
474{
475 LogFlowFunc(("cbName=%d\n", cbName));
476 int rc = VINF_SUCCESS;
477 if (RT_SUCCESS(rc) && (cbName < 2))
478 rc = VERR_INVALID_PARAMETER;
479 for (unsigned i = 0; RT_SUCCESS(rc) && i < cbName; ++i)
480 if (pszName[i] == '*' || pszName[i] == '?' || pszName[i] == '|')
481 rc = VERR_INVALID_PARAMETER;
482 LogFlowFunc(("returning %Rrc\n", rc));
483 return rc;
484}
485
486
487/**
488 * Check a string fits our criteria for the value of a guest property.
489 *
490 * @returns IPRT status code
491 * @param pszValue the string to check, must be valid Utf8
492 * @param cbValue the length in bytes of @a pszValue, including the
493 * terminator
494 * @thread HGCM
495 */
496int Service::validateValue(const char *pszValue, uint32_t cbValue)
497{
498 LogFlowFunc(("cbValue=%d\n", cbValue)); RT_NOREF1(pszValue);
499
500 int rc = VINF_SUCCESS;
501 if (RT_SUCCESS(rc) && cbValue == 0)
502 rc = VERR_INVALID_PARAMETER;
503 if (RT_SUCCESS(rc))
504 LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL));
505 LogFlowFunc(("returning %Rrc\n", rc));
506 return rc;
507}
508
509/**
510 * Set a block of properties in the property registry, checking the validity
511 * of the arguments passed.
512 *
513 * @returns iprt status value
514 * @param cParms the number of HGCM parameters supplied
515 * @param paParms the array of HGCM parameters
516 * @thread HGCM
517 */
518int Service::setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
519{
520 const char **papszNames;
521 const char **papszValues;
522 const char **papszFlags;
523 uint64_t *pau64Timestamps;
524 uint32_t cbDummy;
525 int rc = VINF_SUCCESS;
526
527 /*
528 * Get and validate the parameters
529 */
530 if ( cParms != 4
531 || RT_FAILURE(paParms[0].getPointer((void **)&papszNames, &cbDummy))
532 || RT_FAILURE(paParms[1].getPointer((void **)&papszValues, &cbDummy))
533 || RT_FAILURE(paParms[2].getPointer((void **)&pau64Timestamps, &cbDummy))
534 || RT_FAILURE(paParms[3].getPointer((void **)&papszFlags, &cbDummy))
535 )
536 rc = VERR_INVALID_PARAMETER;
537 /** @todo validate the array sizes... */
538 else
539 {
540 for (unsigned i = 0; RT_SUCCESS(rc) && papszNames[i] != NULL; ++i)
541 {
542 if ( !RT_VALID_PTR(papszNames[i])
543 || !RT_VALID_PTR(papszValues[i])
544 || !RT_VALID_PTR(papszFlags[i])
545 )
546 rc = VERR_INVALID_POINTER;
547 else
548 {
549 uint32_t fFlagsIgn;
550 rc = GuestPropValidateFlags(papszFlags[i], &fFlagsIgn);
551 }
552 }
553 if (RT_SUCCESS(rc))
554 {
555 /*
556 * Add the properties. No way to roll back here.
557 */
558 for (unsigned i = 0; papszNames[i] != NULL; ++i)
559 {
560 uint32_t fFlags;
561 rc = GuestPropValidateFlags(papszFlags[i], &fFlags);
562 AssertRCBreak(rc);
563 /*
564 * Handle names which are read-only for the guest.
565 */
566 if (checkHostReserved(papszNames[i]))
567 fFlags |= GUEST_PROP_F_RDONLYGUEST;
568
569 Property *pProp = getPropertyInternal(papszNames[i]);
570 if (pProp)
571 {
572 /* Update existing property. */
573 pProp->mValue = papszValues[i];
574 pProp->mTimestamp = pau64Timestamps[i];
575 pProp->mFlags = fFlags;
576 }
577 else
578 {
579 /* Create a new property */
580 pProp = new Property(papszNames[i], papszValues[i], pau64Timestamps[i], fFlags);
581 if (!pProp)
582 {
583 rc = VERR_NO_MEMORY;
584 break;
585 }
586 if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore))
587 mcProperties++;
588 else
589 {
590 delete pProp;
591 rc = VERR_INTERNAL_ERROR_3;
592 AssertFailedBreak();
593 }
594 }
595 }
596 }
597 }
598
599 return rc;
600}
601
602/**
603 * Retrieve a value from the property registry by name, checking the validity
604 * of the arguments passed. If the guest has not allocated enough buffer
605 * space for the value then we return VERR_OVERFLOW and set the size of the
606 * buffer needed in the "size" HGCM parameter. If the name was not found at
607 * all, we return VERR_NOT_FOUND.
608 *
609 * @returns iprt status value
610 * @param cParms the number of HGCM parameters supplied
611 * @param paParms the array of HGCM parameters
612 * @thread HGCM
613 */
614int Service::getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
615{
616 int rc;
617 const char *pcszName = NULL; /* shut up gcc */
618 char *pchBuf = NULL; /* shut up MSC */
619 uint32_t cbName;
620 uint32_t cbBuf = 0; /* shut up MSC */
621
622 /*
623 * Get and validate the parameters
624 */
625 LogFlowThisFunc(("\n"));
626 if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */
627 || RT_FAILURE(paParms[0].getString(&pcszName, &cbName)) /* name */
628 || RT_FAILURE(paParms[1].getBuffer((void **)&pchBuf, &cbBuf)) /* buffer */
629 )
630 rc = VERR_INVALID_PARAMETER;
631 else
632 rc = validateName(pcszName, cbName);
633 if (RT_FAILURE(rc))
634 {
635 LogFlowThisFunc(("rc = %Rrc\n", rc));
636 return rc;
637 }
638
639 /*
640 * Read and set the values we will return
641 */
642
643 /* Get the property. */
644 Property *pProp = getPropertyInternal(pcszName);
645 if (pProp)
646 {
647 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
648 rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
649 if (RT_SUCCESS(rc))
650 {
651 /* Check that the buffer is big enough */
652 size_t const cbFlags = strlen(szFlags) + 1;
653 size_t const cbValue = pProp->mValue.size() + 1;
654 size_t const cbNeeded = cbValue + cbFlags;
655 paParms[3].setUInt32((uint32_t)cbNeeded);
656 if (cbBuf >= cbNeeded)
657 {
658 /* Write the value, flags and timestamp */
659 memcpy(pchBuf, pProp->mValue.c_str(), cbValue);
660 memcpy(pchBuf + cbValue, szFlags, cbFlags);
661
662 paParms[2].setUInt64(pProp->mTimestamp);
663
664 /*
665 * Done! Do exit logging and return.
666 */
667 Log2(("Queried string %s, value=%s, timestamp=%lld, flags=%s\n",
668 pcszName, pProp->mValue.c_str(), pProp->mTimestamp, szFlags));
669 }
670 else
671 rc = VERR_BUFFER_OVERFLOW;
672 }
673 }
674 else
675 rc = VERR_NOT_FOUND;
676
677 LogFlowThisFunc(("rc = %Rrc (%s)\n", rc, pcszName));
678 return rc;
679}
680
681/**
682 * Set a value in the property registry by name, checking the validity
683 * of the arguments passed.
684 *
685 * @returns iprt status value
686 * @param cParms the number of HGCM parameters supplied
687 * @param paParms the array of HGCM parameters
688 * @param isGuest is this call coming from the guest (or the host)?
689 * @throws std::bad_alloc if an out of memory condition occurs
690 * @thread HGCM
691 */
692int Service::setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
693{
694 int rc = VINF_SUCCESS;
695 const char *pcszName = NULL; /* shut up gcc */
696 const char *pcszValue = NULL; /* ditto */
697 const char *pcszFlags = NULL;
698 uint32_t cchName = 0; /* ditto */
699 uint32_t cchValue = 0; /* ditto */
700 uint32_t cchFlags = 0;
701 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
702 uint64_t u64TimeNano = getCurrentTimestamp();
703
704 LogFlowThisFunc(("\n"));
705
706 /*
707 * General parameter correctness checking.
708 */
709 if ( RT_SUCCESS(rc)
710 && ( (cParms < 2) || (cParms > 3) /* Hardcoded value as the next lines depend on it. */
711 || RT_FAILURE(paParms[0].getString(&pcszName, &cchName)) /* name */
712 || RT_FAILURE(paParms[1].getString(&pcszValue, &cchValue)) /* value */
713 || ( (3 == cParms)
714 && RT_FAILURE(paParms[2].getString(&pcszFlags, &cchFlags)) /* flags */
715 )
716 )
717 )
718 rc = VERR_INVALID_PARAMETER;
719
720 /*
721 * Check the values passed in the parameters for correctness.
722 */
723 if (RT_SUCCESS(rc))
724 rc = validateName(pcszName, cchName);
725 if (RT_SUCCESS(rc))
726 rc = validateValue(pcszValue, cchValue);
727 if ((3 == cParms) && RT_SUCCESS(rc))
728 rc = RTStrValidateEncodingEx(pcszFlags, cchFlags,
729 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
730 if ((3 == cParms) && RT_SUCCESS(rc))
731 rc = GuestPropValidateFlags(pcszFlags, &fFlags);
732 if (RT_FAILURE(rc))
733 {
734 LogFlowThisFunc(("rc = %Rrc\n", rc));
735 return rc;
736 }
737
738 /*
739 * If the property already exists, check its flags to see if we are allowed
740 * to change it.
741 */
742 Property *pProp = getPropertyInternal(pcszName);
743 rc = checkPermission(pProp ? pProp->mFlags : GUEST_PROP_F_NILFLAG, isGuest);
744 /*
745 * Handle names which are read-only for the guest.
746 */
747 if (rc == VINF_SUCCESS && checkHostReserved(pcszName))
748 {
749 if (isGuest)
750 rc = VERR_PERMISSION_DENIED;
751 else
752 fFlags |= GUEST_PROP_F_RDONLYGUEST;
753 }
754 if (rc == VINF_SUCCESS)
755 {
756 /*
757 * Set the actual value
758 */
759 if (pProp)
760 {
761 pProp->mValue = pcszValue;
762 pProp->mTimestamp = u64TimeNano;
763 pProp->mFlags = fFlags;
764 }
765 else if (mcProperties < GUEST_PROP_MAX_PROPS)
766 {
767 try
768 {
769 /* Create a new string space record. */
770 pProp = new Property(pcszName, pcszValue, u64TimeNano, fFlags);
771 AssertPtr(pProp);
772
773 if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore))
774 mcProperties++;
775 else
776 {
777 AssertFailed();
778 delete pProp;
779
780 rc = VERR_ALREADY_EXISTS;
781 }
782 }
783 catch (std::bad_alloc)
784 {
785 rc = VERR_NO_MEMORY;
786 }
787 }
788 else
789 rc = VERR_TOO_MUCH_DATA;
790
791 /*
792 * Send a notification to the guest and host and return.
793 */
794 // if (isGuest) /* Notify the host even for properties that the host
795 // * changed. Less efficient, but ensures consistency. */
796 int rc2 = doNotifications(pcszName, u64TimeNano);
797 if (RT_SUCCESS(rc))
798 rc = rc2;
799 }
800
801 LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc));
802 return rc;
803}
804
805
806/**
807 * Remove a value in the property registry by name, checking the validity
808 * of the arguments passed.
809 *
810 * @returns iprt status value
811 * @param cParms the number of HGCM parameters supplied
812 * @param paParms the array of HGCM parameters
813 * @param isGuest is this call coming from the guest (or the host)?
814 * @thread HGCM
815 */
816int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
817{
818 int rc;
819 const char *pcszName = NULL; /* shut up gcc */
820 uint32_t cbName;
821
822 LogFlowThisFunc(("\n"));
823
824 /*
825 * Check the user-supplied parameters.
826 */
827 if ( (cParms == 1) /* Hardcoded value as the next lines depend on it. */
828 && RT_SUCCESS(paParms[0].getString(&pcszName, &cbName)) /* name */
829 )
830 rc = validateName(pcszName, cbName);
831 else
832 rc = VERR_INVALID_PARAMETER;
833 if (RT_FAILURE(rc))
834 {
835 LogFlowThisFunc(("rc=%Rrc\n", rc));
836 return rc;
837 }
838
839 /*
840 * If the property exists, check its flags to see if we are allowed
841 * to change it.
842 */
843 Property *pProp = getPropertyInternal(pcszName);
844 if (pProp)
845 rc = checkPermission(pProp->mFlags, isGuest);
846
847 /*
848 * And delete the property if all is well.
849 */
850 if (rc == VINF_SUCCESS && pProp)
851 {
852 uint64_t u64Timestamp = getCurrentTimestamp();
853 PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&mhProperties, pProp->mStrCore.pszString);
854 AssertPtr(pStrCore); NOREF(pStrCore);
855 mcProperties--;
856 delete pProp;
857 // if (isGuest) /* Notify the host even for properties that the host
858 // * changed. Less efficient, but ensures consistency. */
859 int rc2 = doNotifications(pcszName, u64Timestamp);
860 if (RT_SUCCESS(rc))
861 rc = rc2;
862 }
863
864 LogFlowThisFunc(("%s: rc=%Rrc\n", pcszName, rc));
865 return rc;
866}
867
868/**
869 * Enumeration data shared between enumPropsCallback and Service::enumProps.
870 */
871typedef struct ENUMDATA
872{
873 const char *pszPattern; /**< The pattern to match properties against. */
874 char *pchCur; /**< The current buffer postion. */
875 size_t cbLeft; /**< The amount of available buffer space. */
876 size_t cbNeeded; /**< The amount of needed buffer space. */
877} ENUMDATA;
878
879/**
880 * @callback_method_impl{FNRTSTRSPACECALLBACK}
881 */
882static DECLCALLBACK(int) enumPropsCallback(PRTSTRSPACECORE pStr, void *pvUser)
883{
884 Property *pProp = (Property *)pStr;
885 ENUMDATA *pEnum = (ENUMDATA *)pvUser;
886
887 /* Included in the enumeration? */
888 if (!pProp->Matches(pEnum->pszPattern))
889 return 0;
890
891 /* Convert the non-string members into strings. */
892 char szTimestamp[256];
893 size_t const cbTimestamp = RTStrFormatNumber(szTimestamp, pProp->mTimestamp, 10, 0, 0, 0) + 1;
894
895 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
896 int rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
897 if (RT_FAILURE(rc))
898 return rc;
899 size_t const cbFlags = strlen(szFlags) + 1;
900
901 /* Calculate the buffer space requirements. */
902 size_t const cbName = pProp->mName.length() + 1;
903 size_t const cbValue = pProp->mValue.length() + 1;
904 size_t const cbRequired = cbName + cbValue + cbTimestamp + cbFlags;
905 pEnum->cbNeeded += cbRequired;
906
907 /* Sufficient buffer space? */
908 if (cbRequired > pEnum->cbLeft)
909 {
910 pEnum->cbLeft = 0;
911 return 0; /* don't quit */
912 }
913 pEnum->cbLeft -= cbRequired;
914
915 /* Append the property to the buffer. */
916 char *pchCur = pEnum->pchCur;
917 pEnum->pchCur += cbRequired;
918
919 memcpy(pchCur, pProp->mName.c_str(), cbName);
920 pchCur += cbName;
921
922 memcpy(pchCur, pProp->mValue.c_str(), cbValue);
923 pchCur += cbValue;
924
925 memcpy(pchCur, szTimestamp, cbTimestamp);
926 pchCur += cbTimestamp;
927
928 memcpy(pchCur, szFlags, cbFlags);
929 pchCur += cbFlags;
930
931 Assert(pchCur == pEnum->pchCur);
932 return 0;
933}
934
935/**
936 * Enumerate guest properties by mask, checking the validity
937 * of the arguments passed.
938 *
939 * @returns iprt status value
940 * @param cParms the number of HGCM parameters supplied
941 * @param paParms the array of HGCM parameters
942 * @thread HGCM
943 */
944int Service::enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
945{
946 int rc = VINF_SUCCESS;
947
948 /*
949 * Get the HGCM function arguments.
950 */
951 char const *pchPatterns = NULL;
952 char *pchBuf = NULL;
953 uint32_t cbPatterns = 0;
954 uint32_t cbBuf = 0;
955 LogFlowThisFunc(("\n"));
956 if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
957 || RT_FAILURE(paParms[0].getString(&pchPatterns, &cbPatterns)) /* patterns */
958 || RT_FAILURE(paParms[1].getBuffer((void **)&pchBuf, &cbBuf)) /* return buffer */
959 )
960 rc = VERR_INVALID_PARAMETER;
961 if (RT_SUCCESS(rc) && cbPatterns > GUEST_PROP_MAX_PATTERN_LEN)
962 rc = VERR_TOO_MUCH_DATA;
963
964 /*
965 * First repack the patterns into the format expected by RTStrSimplePatternMatch()
966 */
967 char szPatterns[GUEST_PROP_MAX_PATTERN_LEN];
968 if (RT_SUCCESS(rc))
969 {
970 for (unsigned i = 0; i < cbPatterns - 1; ++i)
971 if (pchPatterns[i] != '\0')
972 szPatterns[i] = pchPatterns[i];
973 else
974 szPatterns[i] = '|';
975 szPatterns[cbPatterns - 1] = '\0';
976 }
977
978 /*
979 * Next enumerate into the buffer.
980 */
981 if (RT_SUCCESS(rc))
982 {
983 ENUMDATA EnumData;
984 EnumData.pszPattern = szPatterns;
985 EnumData.pchCur = pchBuf;
986 EnumData.cbLeft = cbBuf;
987 EnumData.cbNeeded = 0;
988 rc = RTStrSpaceEnumerate(&mhProperties, enumPropsCallback, &EnumData);
989 AssertRCSuccess(rc);
990 if (RT_SUCCESS(rc))
991 {
992 paParms[2].setUInt32((uint32_t)(EnumData.cbNeeded + 4));
993 if (EnumData.cbLeft >= 4)
994 {
995 /* The final terminators. */
996 EnumData.pchCur[0] = '\0';
997 EnumData.pchCur[1] = '\0';
998 EnumData.pchCur[2] = '\0';
999 EnumData.pchCur[3] = '\0';
1000 }
1001 else
1002 rc = VERR_BUFFER_OVERFLOW;
1003 }
1004 }
1005
1006 return rc;
1007}
1008
1009
1010/** Helper query used by getOldNotification */
1011int Service::getOldNotificationInternal(const char *pszPatterns,
1012 uint64_t u64Timestamp,
1013 Property *pProp)
1014{
1015 /* We count backwards, as the guest should normally be querying the
1016 * most recent events. */
1017 int rc = VWRN_NOT_FOUND;
1018 PropertyList::reverse_iterator it = mGuestNotifications.rbegin();
1019 for (; it != mGuestNotifications.rend(); ++it)
1020 if (it->mTimestamp == u64Timestamp)
1021 {
1022 rc = VINF_SUCCESS;
1023 break;
1024 }
1025
1026 /* Now look for an event matching the patterns supplied. The base()
1027 * member conveniently points to the following element. */
1028 PropertyList::iterator base = it.base();
1029 for (; base != mGuestNotifications.end(); ++base)
1030 if (base->Matches(pszPatterns))
1031 {
1032 *pProp = *base;
1033 return rc;
1034 }
1035 *pProp = Property();
1036 return rc;
1037}
1038
1039
1040/** Helper query used by getNotification */
1041int Service::getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property prop)
1042{
1043 AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); /* Basic sanity checking. */
1044
1045 /* Format the data to write to the buffer. */
1046 std::string buffer;
1047 uint64_t u64Timestamp;
1048 char *pchBuf;
1049 uint32_t cbBuf;
1050
1051 int rc = paParms[2].getBuffer((void **)&pchBuf, &cbBuf);
1052 if (RT_SUCCESS(rc))
1053 {
1054 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
1055 rc = GuestPropWriteFlags(prop.mFlags, szFlags);
1056 if (RT_SUCCESS(rc))
1057 {
1058 buffer += prop.mName;
1059 buffer += '\0';
1060 buffer += prop.mValue;
1061 buffer += '\0';
1062 buffer += szFlags;
1063 buffer += '\0';
1064 u64Timestamp = prop.mTimestamp;
1065
1066 /* Write out the data. */
1067 if (RT_SUCCESS(rc))
1068 {
1069 paParms[1].setUInt64(u64Timestamp);
1070 paParms[3].setUInt32((uint32_t)buffer.size());
1071 if (buffer.size() <= cbBuf)
1072 buffer.copy(pchBuf, cbBuf);
1073 else
1074 rc = VERR_BUFFER_OVERFLOW;
1075 }
1076 }
1077 }
1078 return rc;
1079}
1080
1081
1082/**
1083 * Get the next guest notification.
1084 *
1085 * @returns iprt status value
1086 * @param u32ClientId the client ID
1087 * @param callHandle handle
1088 * @param cParms the number of HGCM parameters supplied
1089 * @param paParms the array of HGCM parameters
1090 * @thread HGCM
1091 * @throws can throw std::bad_alloc
1092 */
1093int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle,
1094 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1095{
1096 int rc = VINF_SUCCESS;
1097 char *pszPatterns = NULL; /* shut up gcc */
1098 char *pchBuf;
1099 uint32_t cchPatterns = 0;
1100 uint32_t cbBuf = 0;
1101 uint64_t u64Timestamp;
1102
1103 /*
1104 * Get the HGCM function arguments and perform basic verification.
1105 */
1106 LogFlowThisFunc(("\n"));
1107 if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */
1108 || RT_FAILURE(paParms[0].getString(&pszPatterns, &cchPatterns)) /* patterns */
1109 || RT_FAILURE(paParms[1].getUInt64(&u64Timestamp)) /* timestamp */
1110 || RT_FAILURE(paParms[2].getBuffer((void **)&pchBuf, &cbBuf)) /* return buffer */
1111 )
1112 rc = VERR_INVALID_PARAMETER;
1113 else
1114 {
1115 LogFlow(("pszPatterns=%s, u64Timestamp=%llu\n", pszPatterns, u64Timestamp));
1116
1117 /*
1118 * If no timestamp was supplied or no notification was found in the queue
1119 * of old notifications, enqueue the request in the waiting queue.
1120 */
1121 Property prop;
1122 if (RT_SUCCESS(rc) && u64Timestamp != 0)
1123 rc = getOldNotification(pszPatterns, u64Timestamp, &prop);
1124 if (RT_SUCCESS(rc))
1125 {
1126 if (prop.isNull())
1127 {
1128 /*
1129 * Check if the client already had the same request.
1130 * Complete the old request with an error in this case.
1131 * Protection against clients, which cancel and resubmits requests.
1132 */
1133 CallList::iterator it = mGuestWaiters.begin();
1134 while (it != mGuestWaiters.end())
1135 {
1136 const char *pszPatternsExisting;
1137 uint32_t cchPatternsExisting;
1138 int rc3 = it->mParms[0].getString(&pszPatternsExisting, &cchPatternsExisting);
1139
1140 if ( RT_SUCCESS(rc3)
1141 && u32ClientId == it->u32ClientId
1142 && RTStrCmp(pszPatterns, pszPatternsExisting) == 0)
1143 {
1144 /* Complete the old request. */
1145 mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED);
1146 it = mGuestWaiters.erase(it);
1147 }
1148 else
1149 ++it;
1150 }
1151
1152 mGuestWaiters.push_back(GuestCall(u32ClientId, callHandle, GUEST_PROP_FN_GET_NOTIFICATION,
1153 cParms, paParms, rc));
1154 rc = VINF_HGCM_ASYNC_EXECUTE;
1155 }
1156 /*
1157 * Otherwise reply at once with the enqueued notification we found.
1158 */
1159 else
1160 {
1161 int rc2 = getNotificationWriteOut(cParms, paParms, prop);
1162 if (RT_FAILURE(rc2))
1163 rc = rc2;
1164 }
1165 }
1166 }
1167
1168 LogFlowThisFunc(("returning rc=%Rrc\n", rc));
1169 return rc;
1170}
1171
1172
1173/**
1174 * Notify the service owner and the guest that a property has been
1175 * added/deleted/changed
1176 * @param pszProperty the name of the property which has changed
1177 * @param u64Timestamp the time at which the change took place
1178 *
1179 * @thread HGCM service
1180 */
1181int Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp)
1182{
1183 AssertPtrReturn(pszProperty, VERR_INVALID_POINTER);
1184 LogFlowThisFunc(("pszProperty=%s, u64Timestamp=%llu\n", pszProperty, u64Timestamp));
1185 /* Ensure that our timestamp is different to the last one. */
1186 if ( !mGuestNotifications.empty()
1187 && u64Timestamp == mGuestNotifications.back().mTimestamp)
1188 ++u64Timestamp;
1189
1190 /*
1191 * Try to find the property. Create a change event if we find it and a
1192 * delete event if we do not.
1193 */
1194 Property prop;
1195 prop.mName = pszProperty;
1196 prop.mTimestamp = u64Timestamp;
1197 /* prop is currently a delete event for pszProperty */
1198 Property const * const pProp = getPropertyInternal(pszProperty);
1199 if (pProp)
1200 {
1201 /* Make prop into a change event. */
1202 prop.mValue = pProp->mValue;
1203 prop.mFlags = pProp->mFlags;
1204 }
1205
1206 /* Release guest waiters if applicable and add the event
1207 * to the queue for guest notifications */
1208 int rc = VINF_SUCCESS;
1209 try
1210 {
1211 CallList::iterator it = mGuestWaiters.begin();
1212 while (it != mGuestWaiters.end())
1213 {
1214 const char *pszPatterns;
1215 uint32_t cchPatterns;
1216 it->mParms[0].getString(&pszPatterns, &cchPatterns);
1217 if (prop.Matches(pszPatterns))
1218 {
1219 GuestCall curCall = *it;
1220 int rc2 = getNotificationWriteOut(curCall.mParmsCnt, curCall.mParms, prop);
1221 if (RT_SUCCESS(rc2))
1222 rc2 = curCall.mRc;
1223 mpHelpers->pfnCallComplete(curCall.mHandle, rc2);
1224 it = mGuestWaiters.erase(it);
1225 }
1226 else
1227 ++it;
1228 }
1229
1230 mGuestNotifications.push_back(prop);
1231
1232 /** @todo r=andy This list does not have a purpose but for tracking
1233 * the timestamps ... */
1234 if (mGuestNotifications.size() > GUEST_PROP_MAX_GUEST_NOTIFICATIONS)
1235 mGuestNotifications.pop_front();
1236 }
1237 catch (std::bad_alloc)
1238 {
1239 rc = VERR_NO_MEMORY;
1240 }
1241
1242 if ( RT_SUCCESS(rc)
1243 && mpfnHostCallback)
1244 {
1245 /*
1246 * Host notifications - first case: if the property exists then send its
1247 * current value
1248 */
1249 if (pProp)
1250 {
1251 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
1252 /* Send out a host notification */
1253 const char *pszValue = prop.mValue.c_str();
1254 rc = GuestPropWriteFlags(prop.mFlags, szFlags);
1255 if (RT_SUCCESS(rc))
1256 rc = notifyHost(pszProperty, pszValue, u64Timestamp, szFlags);
1257 }
1258 /*
1259 * Host notifications - second case: if the property does not exist then
1260 * send the host an empty value
1261 */
1262 else
1263 {
1264 /* Send out a host notification */
1265 rc = notifyHost(pszProperty, "", u64Timestamp, "");
1266 }
1267 }
1268
1269 LogFlowThisFunc(("returning rc=%Rrc\n", rc));
1270 return rc;
1271}
1272
1273#ifdef ASYNC_HOST_NOTIFY
1274static DECLCALLBACK(void) notifyHostAsyncWorker(PFNHGCMSVCEXT pfnHostCallback,
1275 void *pvHostData,
1276 PGUESTPROPHOSTCALLBACKDATA pHostCallbackData)
1277{
1278 pfnHostCallback(pvHostData, 0 /*u32Function*/,
1279 (void *)pHostCallbackData,
1280 sizeof(GUESTPROPHOSTCALLBACKDATA));
1281 RTMemFree(pHostCallbackData);
1282}
1283#endif
1284
1285/**
1286 * Notify the service owner that a property has been added/deleted/changed.
1287 * @returns IPRT status value
1288 * @param pszName the property name
1289 * @param pszValue the new value, or NULL if the property was deleted
1290 * @param u64Timestamp the time of the change
1291 * @param pszFlags the new flags string
1292 */
1293int Service::notifyHost(const char *pszName, const char *pszValue,
1294 uint64_t u64Timestamp, const char *pszFlags)
1295{
1296 LogFlowFunc(("pszName=%s, pszValue=%s, u64Timestamp=%llu, pszFlags=%s\n",
1297 pszName, pszValue, u64Timestamp, pszFlags));
1298#ifdef ASYNC_HOST_NOTIFY
1299 int rc = VINF_SUCCESS;
1300
1301 /* Allocate buffer for the callback data and strings. */
1302 size_t cbName = pszName? strlen(pszName): 0;
1303 size_t cbValue = pszValue? strlen(pszValue): 0;
1304 size_t cbFlags = pszFlags? strlen(pszFlags): 0;
1305 size_t cbAlloc = sizeof(GUESTPROPHOSTCALLBACKDATA) + cbName + cbValue + cbFlags + 3;
1306 PGUESTPROPHOSTCALLBACKDATA pHostCallbackData = (PGUESTPROPHOSTCALLBACKDATA)RTMemAlloc(cbAlloc);
1307 if (pHostCallbackData)
1308 {
1309 uint8_t *pu8 = (uint8_t *)pHostCallbackData;
1310 pu8 += sizeof(GUESTPROPHOSTCALLBACKDATA);
1311
1312 pHostCallbackData->u32Magic = GUESTPROPHOSTCALLBACKDATA_MAGIC;
1313
1314 pHostCallbackData->pcszName = (const char *)pu8;
1315 memcpy(pu8, pszName, cbName);
1316 pu8 += cbName;
1317 *pu8++ = 0;
1318
1319 pHostCallbackData->pcszValue = (const char *)pu8;
1320 memcpy(pu8, pszValue, cbValue);
1321 pu8 += cbValue;
1322 *pu8++ = 0;
1323
1324 pHostCallbackData->u64Timestamp = u64Timestamp;
1325
1326 pHostCallbackData->pcszFlags = (const char *)pu8;
1327 memcpy(pu8, pszFlags, cbFlags);
1328 pu8 += cbFlags;
1329 *pu8++ = 0;
1330
1331 rc = RTReqQueueCallEx(mhReqQNotifyHost, NULL, 0, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1332 (PFNRT)notifyHostAsyncWorker, 3,
1333 mpfnHostCallback, mpvHostData, pHostCallbackData);
1334 if (RT_FAILURE(rc))
1335 {
1336 RTMemFree(pHostCallbackData);
1337 }
1338 }
1339 else
1340 {
1341 rc = VERR_NO_MEMORY;
1342 }
1343#else
1344 GUESTPROPHOSTCALLBACKDATA HostCallbackData;
1345 HostCallbackData.u32Magic = GUESTPROPHOSTCALLBACKDATA_MAGIC;
1346 HostCallbackData.pcszName = pszName;
1347 HostCallbackData.pcszValue = pszValue;
1348 HostCallbackData.u64Timestamp = u64Timestamp;
1349 HostCallbackData.pcszFlags = pszFlags;
1350 int rc = mpfnHostCallback(mpvHostData, 0 /*u32Function*/,
1351 (void *)(&HostCallbackData),
1352 sizeof(HostCallbackData));
1353#endif
1354 LogFlowFunc(("returning rc=%Rrc\n", rc));
1355 return rc;
1356}
1357
1358
1359/**
1360 * Handle an HGCM service call.
1361 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
1362 * @note All functions which do not involve an unreasonable delay will be
1363 * handled synchronously. If needed, we will add a request handler
1364 * thread in future for those which do.
1365 *
1366 * @thread HGCM
1367 */
1368void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
1369 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
1370 VBOXHGCMSVCPARM paParms[])
1371{
1372 int rc = VINF_SUCCESS;
1373 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %p\n",
1374 u32ClientID, eFunction, cParms, paParms));
1375
1376 try
1377 {
1378 switch (eFunction)
1379 {
1380 /* The guest wishes to read a property */
1381 case GUEST_PROP_FN_GET_PROP:
1382 LogFlowFunc(("GET_PROP\n"));
1383 rc = getProperty(cParms, paParms);
1384 break;
1385
1386 /* The guest wishes to set a property */
1387 case GUEST_PROP_FN_SET_PROP:
1388 LogFlowFunc(("SET_PROP\n"));
1389 rc = setProperty(cParms, paParms, true);
1390 break;
1391
1392 /* The guest wishes to set a property value */
1393 case GUEST_PROP_FN_SET_PROP_VALUE:
1394 LogFlowFunc(("SET_PROP_VALUE\n"));
1395 rc = setProperty(cParms, paParms, true);
1396 break;
1397
1398 /* The guest wishes to remove a configuration value */
1399 case GUEST_PROP_FN_DEL_PROP:
1400 LogFlowFunc(("DEL_PROP\n"));
1401 rc = delProperty(cParms, paParms, true);
1402 break;
1403
1404 /* The guest wishes to enumerate all properties */
1405 case GUEST_PROP_FN_ENUM_PROPS:
1406 LogFlowFunc(("ENUM_PROPS\n"));
1407 rc = enumProps(cParms, paParms);
1408 break;
1409
1410 /* The guest wishes to get the next property notification */
1411 case GUEST_PROP_FN_GET_NOTIFICATION:
1412 LogFlowFunc(("GET_NOTIFICATION\n"));
1413 rc = getNotification(u32ClientID, callHandle, cParms, paParms);
1414 break;
1415
1416 default:
1417 rc = VERR_NOT_IMPLEMENTED;
1418 }
1419 }
1420 catch (std::bad_alloc)
1421 {
1422 rc = VERR_NO_MEMORY;
1423 }
1424 LogFlowFunc(("rc = %Rrc\n", rc));
1425 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1426 {
1427 mpHelpers->pfnCallComplete (callHandle, rc);
1428 }
1429}
1430
1431/**
1432 * Enumeration data shared between dbgInfoCallback and Service::dbgInfoShow.
1433 */
1434typedef struct ENUMDBGINFO
1435{
1436 PCDBGFINFOHLP pHlp;
1437} ENUMDBGINFO;
1438
1439static DECLCALLBACK(int) dbgInfoCallback(PRTSTRSPACECORE pStr, void *pvUser)
1440{
1441 Property *pProp = (Property *)pStr;
1442 PCDBGFINFOHLP pHlp = ((ENUMDBGINFO*)pvUser)->pHlp;
1443
1444 char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
1445 int rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
1446 if (RT_FAILURE(rc))
1447 RTStrPrintf(szFlags, sizeof(szFlags), "???");
1448
1449 pHlp->pfnPrintf(pHlp, "%s: '%s', %RU64",
1450 pProp->mName.c_str(), pProp->mValue.c_str(), pProp->mTimestamp);
1451 if (strlen(szFlags))
1452 pHlp->pfnPrintf(pHlp, " (%s)", szFlags);
1453 pHlp->pfnPrintf(pHlp, "\n");
1454 return 0;
1455}
1456
1457void Service::dbgInfoShow(PCDBGFINFOHLP pHlp)
1458{
1459 ENUMDBGINFO EnumData = { pHlp };
1460 RTStrSpaceEnumerate(&mhProperties, dbgInfoCallback, &EnumData);
1461}
1462
1463/**
1464 * Handler for debug info.
1465 *
1466 * @param pvUser user pointer.
1467 * @param pHlp The info helper functions.
1468 * @param pszArgs Arguments, ignored.
1469 */
1470void Service::dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs)
1471{
1472 RT_NOREF1(pszArgs);
1473 SELF *pSelf = reinterpret_cast<SELF *>(pvUser);
1474 pSelf->dbgInfoShow(pHlp);
1475}
1476
1477
1478/**
1479 * Service call handler for the host.
1480 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall}
1481 * @thread hgcm
1482 */
1483int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1484{
1485 int rc = VINF_SUCCESS;
1486
1487 LogFlowFunc(("fn = %d, cParms = %d, pparms = %p\n",
1488 eFunction, cParms, paParms));
1489
1490 try
1491 {
1492 switch (eFunction)
1493 {
1494 /* The host wishes to set a block of properties */
1495 case GUEST_PROP_FN_HOST_SET_PROPS:
1496 LogFlowFunc(("SET_PROPS_HOST\n"));
1497 rc = setPropertyBlock(cParms, paParms);
1498 break;
1499
1500 /* The host wishes to read a configuration value */
1501 case GUEST_PROP_FN_HOST_GET_PROP:
1502 LogFlowFunc(("GET_PROP_HOST\n"));
1503 rc = getProperty(cParms, paParms);
1504 break;
1505
1506 /* The host wishes to set a configuration value */
1507 case GUEST_PROP_FN_HOST_SET_PROP:
1508 LogFlowFunc(("SET_PROP_HOST\n"));
1509 rc = setProperty(cParms, paParms, false);
1510 break;
1511
1512 /* The host wishes to set a configuration value */
1513 case GUEST_PROP_FN_HOST_SET_PROP_VALUE:
1514 LogFlowFunc(("SET_PROP_VALUE_HOST\n"));
1515 rc = setProperty(cParms, paParms, false);
1516 break;
1517
1518 /* The host wishes to remove a configuration value */
1519 case GUEST_PROP_FN_HOST_DEL_PROP:
1520 LogFlowFunc(("DEL_PROP_HOST\n"));
1521 rc = delProperty(cParms, paParms, false);
1522 break;
1523
1524 /* The host wishes to enumerate all properties */
1525 case GUEST_PROP_FN_HOST_ENUM_PROPS:
1526 LogFlowFunc(("ENUM_PROPS\n"));
1527 rc = enumProps(cParms, paParms);
1528 break;
1529
1530 /* The host wishes to set global flags for the service */
1531 case GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS:
1532 LogFlowFunc(("SET_GLOBAL_FLAGS_HOST\n"));
1533 if (cParms == 1)
1534 {
1535 uint32_t fFlags;
1536 rc = paParms[0].getUInt32(&fFlags);
1537 if (RT_SUCCESS(rc))
1538 mfGlobalFlags = fFlags;
1539 }
1540 else
1541 rc = VERR_INVALID_PARAMETER;
1542 break;
1543
1544 case GUEST_PROP_FN_HOST_GET_DBGF_INFO:
1545 if (cParms != 2)
1546 return VERR_INVALID_PARAMETER;
1547 paParms[0].u.pointer.addr = (void*)(uintptr_t)dbgInfo;
1548 paParms[1].u.pointer.addr = (void*)this;
1549 break;
1550
1551 default:
1552 rc = VERR_NOT_SUPPORTED;
1553 break;
1554 }
1555 }
1556 catch (std::bad_alloc)
1557 {
1558 rc = VERR_NO_MEMORY;
1559 }
1560
1561 LogFlowFunc(("rc = %Rrc\n", rc));
1562 return rc;
1563}
1564
1565#ifdef ASYNC_HOST_NOTIFY
1566/* static */
1567DECLCALLBACK(int) Service::threadNotifyHost(RTTHREAD hThreadSelf, void *pvUser)
1568{
1569 RT_NOREF1(hThreadSelf);
1570 Service *pThis = (Service *)pvUser;
1571 int rc = VINF_SUCCESS;
1572
1573 LogFlowFunc(("ENTER: %p\n", pThis));
1574
1575 for (;;)
1576 {
1577 rc = RTReqQueueProcess(pThis->mhReqQNotifyHost, RT_INDEFINITE_WAIT);
1578
1579 AssertMsg(rc == VWRN_STATE_CHANGED,
1580 ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n",
1581 rc));
1582 if (rc == VWRN_STATE_CHANGED)
1583 {
1584 break;
1585 }
1586 }
1587
1588 LogFlowFunc(("LEAVE: %Rrc\n", rc));
1589 return rc;
1590}
1591
1592static DECLCALLBACK(int) wakeupNotifyHost(void)
1593{
1594 /* Returning a VWRN_* will cause RTReqQueueProcess return. */
1595 return VWRN_STATE_CHANGED;
1596}
1597
1598int Service::initialize()
1599{
1600 /* The host notification thread and queue. */
1601 int rc = RTReqQueueCreate(&mhReqQNotifyHost);
1602 if (RT_SUCCESS(rc))
1603 {
1604 rc = RTThreadCreate(&mhThreadNotifyHost,
1605 threadNotifyHost,
1606 this,
1607 0 /* default stack size */,
1608 RTTHREADTYPE_DEFAULT,
1609 RTTHREADFLAGS_WAITABLE,
1610 "GSTPROPNTFY");
1611 }
1612
1613 if (RT_FAILURE(rc))
1614 {
1615 if (mhReqQNotifyHost != NIL_RTREQQUEUE)
1616 {
1617 RTReqQueueDestroy(mhReqQNotifyHost);
1618 mhReqQNotifyHost = NIL_RTREQQUEUE;
1619 }
1620 }
1621
1622 return rc;
1623}
1624
1625/**
1626 * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys Property.}
1627 */
1628static DECLCALLBACK(int) destroyProperty(PRTSTRSPACECORE pStr, void *pvUser)
1629{
1630 RT_NOREF(pvUser);
1631 Property *pProp = RT_FROM_MEMBER(pStr, struct Property, mStrCore);
1632 delete pProp;
1633 return 0;
1634}
1635
1636#endif
1637
1638int Service::uninit()
1639{
1640#ifdef ASYNC_HOST_NOTIFY
1641 if (mhReqQNotifyHost != NIL_RTREQQUEUE)
1642 {
1643 /* Stop the thread */
1644 PRTREQ pReq;
1645 int rc = RTReqQueueCall(mhReqQNotifyHost, &pReq, 10000, (PFNRT)wakeupNotifyHost, 0);
1646 if (RT_SUCCESS(rc))
1647 RTReqRelease(pReq);
1648 rc = RTThreadWait(mhThreadNotifyHost, 10000, NULL);
1649 AssertRC(rc);
1650 rc = RTReqQueueDestroy(mhReqQNotifyHost);
1651 AssertRC(rc);
1652 mhReqQNotifyHost = NIL_RTREQQUEUE;
1653 mhThreadNotifyHost = NIL_RTTHREAD;
1654 RTStrSpaceDestroy(&mhProperties, destroyProperty, NULL);
1655 mhProperties = NULL;
1656 }
1657#endif
1658
1659 return VINF_SUCCESS;
1660}
1661
1662} /* namespace guestProp */
1663
1664using guestProp::Service;
1665
1666/**
1667 * @copydoc VBOXHGCMSVCLOAD
1668 */
1669extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1670{
1671 int rc = VERR_IPE_UNINITIALIZED_STATUS;
1672
1673 LogFlowFunc(("ptable = %p\n", ptable));
1674
1675 if (!RT_VALID_PTR(ptable))
1676 rc = VERR_INVALID_PARAMETER;
1677 else
1678 {
1679 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1680
1681 if ( ptable->cbSize != sizeof(VBOXHGCMSVCFNTABLE)
1682 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1683 rc = VERR_VERSION_MISMATCH;
1684 else
1685 {
1686 Service *pService = NULL;
1687 /* No exceptions may propagate outside. */
1688 try
1689 {
1690 pService = new Service(ptable->pHelpers);
1691 rc = VINF_SUCCESS;
1692 }
1693 catch (int rcThrown)
1694 {
1695 rc = rcThrown;
1696 }
1697 catch (...)
1698 {
1699 rc = VERR_UNEXPECTED_EXCEPTION;
1700 }
1701
1702 if (RT_SUCCESS(rc))
1703 {
1704 /* We do not maintain connections, so no client data is needed. */
1705 ptable->cbClient = 0;
1706
1707 ptable->pfnUnload = Service::svcUnload;
1708 ptable->pfnConnect = Service::svcConnectDisconnect;
1709 ptable->pfnDisconnect = Service::svcConnectDisconnect;
1710 ptable->pfnCall = Service::svcCall;
1711 ptable->pfnHostCall = Service::svcHostCall;
1712 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1713 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1714 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1715
1716 /* Service specific initialization. */
1717 ptable->pvService = pService;
1718
1719#ifdef ASYNC_HOST_NOTIFY
1720 rc = pService->initialize();
1721 if (RT_FAILURE(rc))
1722 {
1723 delete pService;
1724 pService = NULL;
1725 }
1726#endif
1727 }
1728 else
1729 Assert(!pService);
1730 }
1731 }
1732
1733 LogFlowFunc(("returning %Rrc\n", rc));
1734 return rc;
1735}
1736
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