VirtualBox

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

Last change on this file since 57766 was 57766, checked in by vboxsync, 9 years ago

HostServices/GuestProperties: asynch host notifications.

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