VirtualBox

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

Last change on this file since 25561 was 25417, checked in by vboxsync, 15 years ago

HostServices/GuestProperties: fixed a comment

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 48.9 KB
Line 
1/* $Id: service.cpp 25417 2009-12-15 22:21:39Z vboxsync $ */
2/** @file
3 * Guest Property Service: Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/** @page pg_svc_guest_properties Guest Property HGCM Service
23 *
24 * This HGCM service allows the guest to set and query values in a property
25 * store on the host. The service proxies the guest requests to the service
26 * owner on the host using a request callback provided by the owner, and is
27 * notified of changes to properties made by the host. It forwards these
28 * notifications to clients in the guest which have expressed interest and
29 * are waiting for notification.
30 *
31 * The service currently consists of two threads. One of these is the main
32 * HGCM service thread which deals with requests from the guest and from the
33 * host. The second thread sends the host asynchronous notifications of
34 * changes made by the guest and deals with notification timeouts.
35 *
36 * Guest requests to wait for notification are added to a list of open
37 * notification requests and completed when a corresponding guest property
38 * is changed or when the request times out.
39 */
40
41/*******************************************************************************
42* Header Files *
43*******************************************************************************/
44#define LOG_GROUP LOG_GROUP_HGCM
45#include <VBox/HostServices/GuestPropertySvc.h>
46
47#include <VBox/log.h>
48#include <iprt/asm.h>
49#include <iprt/assert.h>
50#include <iprt/cpp/autores.h>
51#include <iprt/cpp/utils.h>
52#include <iprt/err.h>
53#include <iprt/mem.h>
54#include <iprt/req.h>
55#include <iprt/string.h>
56#include <iprt/thread.h>
57#include <iprt/time.h>
58
59#include <memory> /* for auto_ptr */
60#include <string>
61#include <list>
62
63namespace guestProp {
64
65/**
66 * Structure for holding a property
67 */
68struct Property
69{
70 /** The name of the property */
71 std::string mName;
72 /** The property value */
73 std::string mValue;
74 /** The timestamp of the property */
75 uint64_t mTimestamp;
76 /** The property flags */
77 uint32_t mFlags;
78
79 /** Default constructor */
80 Property() : mTimestamp(0), mFlags(NILFLAG) {}
81 /** Constructor with const char * */
82 Property(const char *pcszName, const char *pcszValue,
83 uint64_t u64Timestamp, uint32_t u32Flags)
84 : mName(pcszName), mValue(pcszValue), mTimestamp(u64Timestamp),
85 mFlags(u32Flags) {}
86 /** Constructor with std::string */
87 Property(std::string name, std::string value, uint64_t u64Timestamp,
88 uint32_t u32Flags)
89 : mName(name), mValue(value), mTimestamp(u64Timestamp),
90 mFlags(u32Flags) {}
91
92 /** Does the property name match one of a set of patterns? */
93 bool Matches(const char *pszPatterns) const
94 {
95 return ( pszPatterns[0] == '\0' /* match all */
96 || RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
97 mName.c_str(), RTSTR_MAX,
98 NULL)
99 );
100 }
101
102 /** Are two properties equal? */
103 bool operator== (const Property &prop)
104 {
105 return ( mName == prop.mName
106 && mValue == prop.mValue
107 && mTimestamp == prop.mTimestamp
108 && mFlags == prop.mFlags
109 );
110 }
111
112 /* Is the property nil? */
113 bool isNull()
114 {
115 return mName.empty();
116 }
117};
118/** The properties list type */
119typedef std::list <Property> PropertyList;
120
121/**
122 * Structure for holding an uncompleted guest call
123 */
124struct GuestCall
125{
126 /** The call handle */
127 VBOXHGCMCALLHANDLE mHandle;
128 /** The function that was requested */
129 uint32_t mFunction;
130 /** The call parameters */
131 VBOXHGCMSVCPARM *mParms;
132 /** The default return value, used for passing warnings */
133 int mRc;
134
135 /** The standard constructor */
136 GuestCall() : mFunction(0) {}
137 /** The normal contructor */
138 GuestCall(VBOXHGCMCALLHANDLE aHandle, uint32_t aFunction,
139 VBOXHGCMSVCPARM aParms[], int aRc)
140 : mHandle(aHandle), mFunction(aFunction), mParms(aParms),
141 mRc(aRc) {}
142};
143/** The guest call list type */
144typedef std::list <GuestCall> CallList;
145
146/**
147 * Class containing the shared information service functionality.
148 */
149class Service : public stdx::non_copyable
150{
151private:
152 /** Type definition for use in callback functions */
153 typedef Service SELF;
154 /** HGCM helper functions. */
155 PVBOXHGCMSVCHELPERS mpHelpers;
156 /** Global flags for the service */
157 ePropFlags meGlobalFlags;
158 /** The property list */
159 PropertyList mProperties;
160 /** The list of property changes for guest notifications */
161 PropertyList mGuestNotifications;
162 /** The list of outstanding guest notification calls */
163 CallList mGuestWaiters;
164 /** @todo we should have classes for thread and request handler thread */
165 /** Queue of outstanding property change notifications */
166 RTREQQUEUE *mReqQueue;
167 /** Request that we've left pending in a call to flushNotifications. */
168 PRTREQ mPendingDummyReq;
169 /** Thread for processing the request queue */
170 RTTHREAD mReqThread;
171 /** Tell the thread that it should exit */
172 bool volatile mfExitThread;
173 /** Callback function supplied by the host for notification of updates
174 * to properties */
175 PFNHGCMSVCEXT mpfnHostCallback;
176 /** User data pointer to be supplied to the host callback function */
177 void *mpvHostData;
178
179 /**
180 * Get the next property change notification from the queue of saved
181 * notification based on the timestamp of the last notification seen.
182 * Notifications will only be reported if the property name matches the
183 * pattern given.
184 *
185 * @returns iprt status value
186 * @returns VWRN_NOT_FOUND if the last notification was not found in the queue
187 * @param pszPatterns the patterns to match the property name against
188 * @param u64Timestamp the timestamp of the last notification
189 * @param pProp where to return the property found. If none is
190 * found this will be set to nil.
191 * @thread HGCM
192 */
193 int getOldNotification(const char *pszPatterns, uint64_t u64Timestamp,
194 Property *pProp)
195 {
196 AssertPtrReturn(pszPatterns, VERR_INVALID_POINTER);
197 /* Zero means wait for a new notification. */
198 AssertReturn(u64Timestamp != 0, VERR_INVALID_PARAMETER);
199 AssertPtrReturn(pProp, VERR_INVALID_POINTER);
200 int rc = getOldNotificationInternal(pszPatterns, u64Timestamp, pProp);
201#ifdef VBOX_STRICT
202 /*
203 * ENSURE that pProp is the first event in the notification queue that:
204 * - Appears later than u64Timestamp
205 * - Matches the pszPatterns
206 */
207 PropertyList::const_iterator it = mGuestNotifications.begin();
208 for (; it != mGuestNotifications.end()
209 && it->mTimestamp != u64Timestamp; ++it) {}
210 if (it == mGuestNotifications.end()) /* Not found */
211 it = mGuestNotifications.begin();
212 else
213 ++it; /* Next event */
214 for (; it != mGuestNotifications.end()
215 && it->mTimestamp != pProp->mTimestamp; ++it)
216 Assert(!it->Matches(pszPatterns));
217 if (pProp->mTimestamp != 0)
218 {
219 Assert(*pProp == *it);
220 Assert(pProp->Matches(pszPatterns));
221 }
222#endif /* VBOX_STRICT */
223 return rc;
224 }
225
226 /**
227 * Check whether we have permission to change a property.
228 *
229 * @returns Strict VBox status code.
230 * @retval VINF_SUCCESS if we do.
231 * @retval VERR_PERMISSION_DENIED if the value is read-only for the requesting
232 * side.
233 * @retval VINF_PERMISSION_DENIED if the side is globally marked read-only.
234 *
235 * @param eFlags the flags on the property in question
236 * @param isGuest is the guest or the host trying to make the change?
237 */
238 int checkPermission(ePropFlags eFlags, bool isGuest)
239 {
240 if (eFlags & (isGuest ? RDONLYGUEST : RDONLYHOST))
241 return VERR_PERMISSION_DENIED;
242 if (isGuest && (meGlobalFlags & RDONLYGUEST))
243 return VINF_PERMISSION_DENIED;
244 return VINF_SUCCESS;
245 }
246
247public:
248 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
249 : mpHelpers(pHelpers)
250 , meGlobalFlags(NILFLAG)
251 , mPendingDummyReq(NULL)
252 , mfExitThread(false)
253 , mpfnHostCallback(NULL)
254 , mpvHostData(NULL)
255 {
256 int rc = RTReqCreateQueue(&mReqQueue);
257#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
258 if (RT_SUCCESS(rc))
259 rc = RTThreadCreate(&mReqThread, reqThreadFn, this, 0,
260 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
261 "GuestPropReq");
262#endif
263 if (RT_FAILURE(rc))
264 throw rc;
265 }
266
267 /**
268 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
269 * Simply deletes the service object
270 */
271 static DECLCALLBACK(int) svcUnload (void *pvService)
272 {
273 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
274 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
275 int rc = pSelf->uninit();
276 AssertRC(rc);
277 if (RT_SUCCESS(rc))
278 delete pSelf;
279 return rc;
280 }
281
282 /**
283 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
284 * Stub implementation of pfnConnect and pfnDisconnect.
285 */
286 static DECLCALLBACK(int) svcConnectDisconnect (void * /* pvService */,
287 uint32_t /* u32ClientID */,
288 void * /* pvClient */)
289 {
290 return VINF_SUCCESS;
291 }
292
293 /**
294 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
295 * Wraps to the call member function
296 */
297 static DECLCALLBACK(void) svcCall (void * pvService,
298 VBOXHGCMCALLHANDLE callHandle,
299 uint32_t u32ClientID,
300 void *pvClient,
301 uint32_t u32Function,
302 uint32_t cParms,
303 VBOXHGCMSVCPARM paParms[])
304 {
305 AssertLogRelReturnVoid(VALID_PTR(pvService));
306 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
307 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
308 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
309 LogFlowFunc (("returning\n"));
310 }
311
312 /**
313 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
314 * Wraps to the hostCall member function
315 */
316 static DECLCALLBACK(int) svcHostCall (void *pvService,
317 uint32_t u32Function,
318 uint32_t cParms,
319 VBOXHGCMSVCPARM paParms[])
320 {
321 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
322 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
323 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
324 int rc = pSelf->hostCall(u32Function, cParms, paParms);
325 LogFlowFunc (("rc=%Rrc\n", rc));
326 return rc;
327 }
328
329 /**
330 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
331 * Installs a host callback for notifications of property changes.
332 */
333 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
334 PFNHGCMSVCEXT pfnExtension,
335 void *pvExtension)
336 {
337 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
338 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
339 pSelf->mpfnHostCallback = pfnExtension;
340 pSelf->mpvHostData = pvExtension;
341 return VINF_SUCCESS;
342 }
343private:
344 static DECLCALLBACK(int) reqThreadFn(RTTHREAD ThreadSelf, void *pvUser);
345 int validateName(const char *pszName, uint32_t cbName);
346 int validateValue(const char *pszValue, uint32_t cbValue);
347 int setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
348 int getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
349 int setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
350 int delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
351 int enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
352 int flushNotifications(uint32_t cMsTimeout);
353 int getNotification(VBOXHGCMCALLHANDLE callHandle, uint32_t cParms,
354 VBOXHGCMSVCPARM paParms[]);
355 int getOldNotificationInternal(const char *pszPattern,
356 uint64_t u64Timestamp, Property *pProp);
357 int getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop);
358 void doNotifications(const char *pszProperty, uint64_t u64Timestamp);
359 static DECLCALLBACK(int) reqNotify(PFNHGCMSVCEXT pfnCallback,
360 void *pvData, char *pszName,
361 char *pszValue, uint32_t u32TimeHigh,
362 uint32_t u32TimeLow, char *pszFlags);
363 /**
364 * Empty request function for terminating the request thread.
365 * @returns VINF_EOF to cause the request processing function to return
366 * @todo return something more appropriate
367 */
368 static DECLCALLBACK(int) reqVoid() { return VINF_EOF; }
369
370 void call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
371 void *pvClient, uint32_t eFunction, uint32_t cParms,
372 VBOXHGCMSVCPARM paParms[]);
373 int hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
374 int uninit ();
375};
376
377
378/**
379 * Thread function for processing the request queue
380 * @copydoc FNRTTHREAD
381 */
382/* static */
383DECLCALLBACK(int) Service::reqThreadFn(RTTHREAD ThreadSelf, void *pvUser)
384{
385 SELF *pSelf = reinterpret_cast<SELF *>(pvUser);
386 while (!pSelf->mfExitThread)
387 RTReqProcess(pSelf->mReqQueue, RT_INDEFINITE_WAIT);
388 return VINF_SUCCESS;
389}
390
391
392/**
393 * Check that a string fits our criteria for a property name.
394 *
395 * @returns IPRT status code
396 * @param pszName the string to check, must be valid Utf8
397 * @param cbName the number of bytes @a pszName points to, including the
398 * terminating '\0'
399 * @thread HGCM
400 */
401int Service::validateName(const char *pszName, uint32_t cbName)
402{
403 LogFlowFunc(("cbName=%d\n", cbName));
404 int rc = VINF_SUCCESS;
405 if (RT_SUCCESS(rc) && (cbName < 2))
406 rc = VERR_INVALID_PARAMETER;
407 for (unsigned i = 0; RT_SUCCESS(rc) && i < cbName; ++i)
408 if (pszName[i] == '*' || pszName[i] == '?' || pszName[i] == '|')
409 rc = VERR_INVALID_PARAMETER;
410 LogFlowFunc(("returning %Rrc\n", rc));
411 return rc;
412}
413
414
415/**
416 * Check a string fits our criteria for the value of a guest property.
417 *
418 * @returns IPRT status code
419 * @param pszValue the string to check, must be valid Utf8
420 * @param cbValue the length in bytes of @a pszValue, including the
421 * terminator
422 * @thread HGCM
423 */
424int Service::validateValue(const char *pszValue, uint32_t cbValue)
425{
426 LogFlowFunc(("cbValue=%d\n", cbValue));
427
428 int rc = VINF_SUCCESS;
429 if (RT_SUCCESS(rc) && cbValue == 0)
430 rc = VERR_INVALID_PARAMETER;
431 if (RT_SUCCESS(rc))
432 LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL));
433 LogFlowFunc(("returning %Rrc\n", rc));
434 return rc;
435}
436
437/**
438 * Set a block of properties in the property registry, checking the validity
439 * of the arguments passed.
440 *
441 * @returns iprt status value
442 * @param cParms the number of HGCM parameters supplied
443 * @param paParms the array of HGCM parameters
444 * @thread HGCM
445 */
446int Service::setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
447{
448 char **ppNames, **ppValues, **ppFlags;
449 uint64_t *pTimestamps;
450 uint32_t cbDummy;
451 int rc = VINF_SUCCESS;
452
453 /*
454 * Get and validate the parameters
455 */
456 if ( (cParms != 4)
457 || RT_FAILURE(paParms[0].getPointer ((void **) &ppNames, &cbDummy))
458 || RT_FAILURE(paParms[1].getPointer ((void **) &ppValues, &cbDummy))
459 || RT_FAILURE(paParms[2].getPointer ((void **) &pTimestamps, &cbDummy))
460 || RT_FAILURE(paParms[3].getPointer ((void **) &ppFlags, &cbDummy))
461 )
462 rc = VERR_INVALID_PARAMETER;
463
464 /*
465 * Add the properties to the end of the list. If we succeed then we
466 * will remove duplicates afterwards.
467 */
468 /* Remember the last property before we started adding, for rollback or
469 * cleanup. */
470 PropertyList::iterator itEnd = mProperties.end();
471 if (!mProperties.empty())
472 --itEnd;
473 try
474 {
475 for (unsigned i = 0; RT_SUCCESS(rc) && ppNames[i] != NULL; ++i)
476 {
477 uint32_t fFlags;
478 if ( !VALID_PTR(ppNames[i])
479 || !VALID_PTR(ppValues[i])
480 || !VALID_PTR(ppFlags[i])
481 )
482 rc = VERR_INVALID_POINTER;
483 if (RT_SUCCESS(rc))
484 rc = validateFlags(ppFlags[i], &fFlags);
485 if (RT_SUCCESS(rc))
486 mProperties.push_back(Property(ppNames[i], ppValues[i],
487 pTimestamps[i], fFlags));
488 }
489 }
490 catch (std::bad_alloc)
491 {
492 rc = VERR_NO_MEMORY;
493 }
494
495 /*
496 * If all went well then remove the duplicate elements.
497 */
498 if (RT_SUCCESS(rc) && itEnd != mProperties.end())
499 {
500 ++itEnd;
501 for (unsigned i = 0; ppNames[i] != NULL; ++i)
502 for (PropertyList::iterator it = mProperties.begin(); it != itEnd; ++it)
503 if (it->mName.compare(ppNames[i]) == 0)
504 {
505 mProperties.erase(it);
506 break;
507 }
508 }
509
510 /*
511 * If something went wrong then rollback. This is possible because we
512 * haven't deleted anything yet.
513 */
514 if (RT_FAILURE(rc))
515 {
516 if (itEnd != mProperties.end())
517 ++itEnd;
518 mProperties.erase(itEnd, mProperties.end());
519 }
520 return rc;
521}
522
523/**
524 * Retrieve a value from the property registry by name, checking the validity
525 * of the arguments passed. If the guest has not allocated enough buffer
526 * space for the value then we return VERR_OVERFLOW and set the size of the
527 * buffer needed in the "size" HGCM parameter. If the name was not found at
528 * all, we return VERR_NOT_FOUND.
529 *
530 * @returns iprt status value
531 * @param cParms the number of HGCM parameters supplied
532 * @param paParms the array of HGCM parameters
533 * @thread HGCM
534 */
535int Service::getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
536{
537 int rc = VINF_SUCCESS;
538 const char *pcszName = NULL; /* shut up gcc */
539 char *pchBuf;
540 uint32_t cchName, cchBuf;
541 char szFlags[MAX_FLAGS_LEN];
542
543 /*
544 * Get and validate the parameters
545 */
546 LogFlowThisFunc(("\n"));
547 if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */
548 || RT_FAILURE (paParms[0].getString(&pcszName, &cchName)) /* name */
549 || RT_FAILURE (paParms[1].getBuffer((void **) &pchBuf, &cchBuf)) /* buffer */
550 )
551 rc = VERR_INVALID_PARAMETER;
552 else
553 rc = validateName(pcszName, cchName);
554
555 /*
556 * Read and set the values we will return
557 */
558
559 /* Get the value size */
560 PropertyList::const_iterator it;
561 if (RT_SUCCESS(rc))
562 {
563 rc = VERR_NOT_FOUND;
564 for (it = mProperties.begin(); it != mProperties.end(); ++it)
565 if (it->mName.compare(pcszName) == 0)
566 {
567 rc = VINF_SUCCESS;
568 break;
569 }
570 }
571 if (RT_SUCCESS(rc))
572 rc = writeFlags(it->mFlags, szFlags);
573 if (RT_SUCCESS(rc))
574 {
575 /* Check that the buffer is big enough */
576 size_t cchBufActual = it->mValue.size() + 1 + strlen(szFlags);
577 paParms[3].setUInt32 ((uint32_t)cchBufActual);
578 if (cchBufActual <= cchBuf)
579 {
580 /* Write the value, flags and timestamp */
581 it->mValue.copy(pchBuf, cchBuf, 0);
582 pchBuf[it->mValue.size()] = '\0'; /* Terminate the value */
583 strcpy(pchBuf + it->mValue.size() + 1, szFlags);
584 paParms[2].setUInt64 (it->mTimestamp);
585
586 /*
587 * Done! Do exit logging and return.
588 */
589 Log2(("Queried string %s, value=%s, timestamp=%lld, flags=%s\n",
590 pcszName, it->mValue.c_str(), it->mTimestamp, szFlags));
591 }
592 else
593 rc = VERR_BUFFER_OVERFLOW;
594 }
595
596 LogFlowThisFunc(("rc = %Rrc\n", rc));
597 return rc;
598}
599
600/**
601 * Set a value in the property registry by name, checking the validity
602 * of the arguments passed.
603 *
604 * @returns iprt status value
605 * @param cParms the number of HGCM parameters supplied
606 * @param paParms the array of HGCM parameters
607 * @param isGuest is this call coming from the guest (or the host)?
608 * @throws std::bad_alloc if an out of memory condition occurs
609 * @thread HGCM
610 */
611int Service::setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
612{
613 int rc = VINF_SUCCESS;
614 const char *pcszName = NULL; /* shut up gcc */
615 const char *pcszValue = NULL; /* ditto */
616 const char *pcszFlags = NULL;
617 uint32_t cchName, cchValue, cchFlags = 0;
618 uint32_t fFlags = NILFLAG;
619 RTTIMESPEC time;
620 uint64_t u64TimeNano = RTTimeSpecGetNano(RTTimeNow(&time));
621
622 LogFlowThisFunc(("\n"));
623 /*
624 * First of all, make sure that we won't exceed the maximum number of properties.
625 */
626 if (mProperties.size() >= MAX_PROPS)
627 rc = VERR_TOO_MUCH_DATA;
628
629 /*
630 * General parameter correctness checking.
631 */
632 if ( RT_SUCCESS(rc)
633 && ( (cParms < 2) || (cParms > 3) /* Hardcoded value as the next lines depend on it. */
634 || RT_FAILURE(paParms[0].getString(&pcszName, &cchName)) /* name */
635 || RT_FAILURE(paParms[1].getString(&pcszValue, &cchValue)) /* value */
636 || ( (3 == cParms)
637 && RT_FAILURE(paParms[2].getString(&pcszFlags, &cchFlags)) /* flags */
638 )
639 )
640 )
641 rc = VERR_INVALID_PARAMETER;
642
643 /*
644 * Check the values passed in the parameters for correctness.
645 */
646 if (RT_SUCCESS(rc))
647 rc = validateName(pcszName, cchName);
648 if (RT_SUCCESS(rc))
649 rc = validateValue(pcszValue, cchValue);
650 if ((3 == cParms) && RT_SUCCESS(rc))
651 rc = RTStrValidateEncodingEx(pcszFlags, cchFlags,
652 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
653 if ((3 == cParms) && RT_SUCCESS(rc))
654 rc = validateFlags(pcszFlags, &fFlags);
655 if (RT_SUCCESS(rc))
656 {
657 /*
658 * If the property already exists, check its flags to see if we are allowed
659 * to change it.
660 */
661 PropertyList::iterator it;
662 bool found = false;
663 for (it = mProperties.begin(); it != mProperties.end(); ++it)
664 if (it->mName.compare(pcszName) == 0)
665 {
666 found = true;
667 break;
668 }
669
670 rc = checkPermission(found ? (ePropFlags)it->mFlags : NILFLAG,
671 isGuest);
672 if (rc == VINF_SUCCESS)
673 {
674 /*
675 * Set the actual value
676 */
677 if (found)
678 {
679 it->mValue = pcszValue;
680 it->mTimestamp = u64TimeNano;
681 it->mFlags = fFlags;
682 }
683 else /* This can throw. No problem as we have nothing to roll back. */
684 mProperties.push_back(Property(pcszName, pcszValue, u64TimeNano, fFlags));
685
686 /*
687 * Send a notification to the host and return.
688 */
689 // if (isGuest) /* Notify the host even for properties that the host
690 // * changed. Less efficient, but ensures consistency. */
691 doNotifications(pcszName, u64TimeNano);
692 Log2(("Set string %s, rc=%Rrc, value=%s\n", pcszName, rc, pcszValue));
693 }
694 }
695
696 LogFlowThisFunc(("rc = %Rrc\n", rc));
697 return rc;
698}
699
700
701/**
702 * Remove a value in the property registry by name, checking the validity
703 * of the arguments passed.
704 *
705 * @returns iprt status value
706 * @param cParms the number of HGCM parameters supplied
707 * @param paParms the array of HGCM parameters
708 * @param isGuest is this call coming from the guest (or the host)?
709 * @thread HGCM
710 */
711int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
712{
713 int rc = VINF_SUCCESS;
714 const char *pcszName = NULL; /* shut up gcc */
715 uint32_t cbName;
716
717 LogFlowThisFunc(("\n"));
718
719 /*
720 * Check the user-supplied parameters.
721 */
722 if ( (cParms == 1) /* Hardcoded value as the next lines depend on it. */
723 && RT_SUCCESS(paParms[0].getString(&pcszName, &cbName)) /* name */
724 )
725 rc = validateName(pcszName, cbName);
726 else
727 rc = VERR_INVALID_PARAMETER;
728 if (RT_SUCCESS(rc))
729 {
730 /*
731 * If the property exists, check its flags to see if we are allowed
732 * to change it.
733 */
734 PropertyList::iterator it;
735 bool found = false;
736 for (it = mProperties.begin(); it != mProperties.end(); ++it)
737 if (it->mName.compare(pcszName) == 0)
738 {
739 found = true;
740 rc = checkPermission((ePropFlags)it->mFlags, isGuest);
741 break;
742 }
743
744 /*
745 * And delete the property if all is well.
746 */
747 if (rc == VINF_SUCCESS && found)
748 {
749 RTTIMESPEC time;
750 uint64_t u64Timestamp = RTTimeSpecGetNano(RTTimeNow(&time));
751 mProperties.erase(it);
752 // if (isGuest) /* Notify the host even for properties that the host
753 // * changed. Less efficient, but ensures consistency. */
754 doNotifications(pcszName, u64Timestamp);
755 }
756 }
757
758 LogFlowThisFunc(("rc = %Rrc\n", rc));
759 return rc;
760}
761
762/**
763 * Enumerate guest properties by mask, checking the validity
764 * of the arguments passed.
765 *
766 * @returns iprt status value
767 * @param cParms the number of HGCM parameters supplied
768 * @param paParms the array of HGCM parameters
769 * @thread HGCM
770 */
771int Service::enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
772{
773 int rc = VINF_SUCCESS;
774
775 /*
776 * Get the HGCM function arguments.
777 */
778 char *pcchPatterns = NULL, *pchBuf = NULL;
779 uint32_t cchPatterns = 0, cchBuf = 0;
780 LogFlowThisFunc(("\n"));
781 if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
782 || RT_FAILURE(paParms[0].getString(&pcchPatterns, &cchPatterns)) /* patterns */
783 || RT_FAILURE(paParms[1].getBuffer((void **) &pchBuf, &cchBuf)) /* return buffer */
784 )
785 rc = VERR_INVALID_PARAMETER;
786 if (RT_SUCCESS(rc) && cchPatterns > MAX_PATTERN_LEN)
787 rc = VERR_TOO_MUCH_DATA;
788
789 /*
790 * First repack the patterns into the format expected by RTStrSimplePatternMatch()
791 */
792 char pszPatterns[MAX_PATTERN_LEN];
793 for (unsigned i = 0; i < cchPatterns - 1; ++i)
794 if (pcchPatterns[i] != '\0')
795 pszPatterns[i] = pcchPatterns[i];
796 else
797 pszPatterns[i] = '|';
798 pszPatterns[cchPatterns - 1] = '\0';
799
800 /*
801 * Next enumerate into a temporary buffer. This can throw, but this is
802 * not a problem as we have nothing to roll back.
803 */
804 std::string buffer;
805 for (PropertyList::const_iterator it = mProperties.begin();
806 RT_SUCCESS(rc) && (it != mProperties.end()); ++it)
807 {
808 if (it->Matches(pszPatterns))
809 {
810 char szFlags[MAX_FLAGS_LEN];
811 char szTimestamp[256];
812 uint32_t cchTimestamp;
813 buffer += it->mName;
814 buffer += '\0';
815 buffer += it->mValue;
816 buffer += '\0';
817 cchTimestamp = RTStrFormatNumber(szTimestamp, it->mTimestamp,
818 10, 0, 0, 0);
819 buffer.append(szTimestamp, cchTimestamp);
820 buffer += '\0';
821 rc = writeFlags(it->mFlags, szFlags);
822 if (RT_SUCCESS(rc))
823 buffer += szFlags;
824 buffer += '\0';
825 }
826 }
827 buffer.append(4, '\0'); /* The final terminators */
828
829 /*
830 * Finally write out the temporary buffer to the real one if it is not too
831 * small.
832 */
833 if (RT_SUCCESS(rc))
834 {
835 paParms[2].setUInt32 ((uint32_t)buffer.size());
836 /* Copy the memory if it fits into the guest buffer */
837 if (buffer.size() <= cchBuf)
838 buffer.copy(pchBuf, cchBuf);
839 else
840 rc = VERR_BUFFER_OVERFLOW;
841 }
842 return rc;
843}
844
845/**
846 * Flushes the notifications.
847 *
848 * @returns iprt status value
849 * @param cMsTimeout The timeout in milliseconds.
850 * @thread HGCM
851 */
852int Service::flushNotifications(uint32_t cMsTimeout)
853{
854 LogFlowThisFunc(("cMsTimeout=%RU32\n", cMsTimeout));
855 int rc;
856
857#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
858 /*
859 * Wait for the thread to finish processing all current requests.
860 */
861 if (!mPendingDummyReq && !RTReqIsBusy(mReqQueue))
862 rc = VINF_SUCCESS;
863 else
864 {
865 if (!mPendingDummyReq)
866 rc = RTReqCallEx(mReqQueue, &mPendingDummyReq, 0 /*cMillies*/, RTREQFLAGS_VOID, (PFNRT)reqVoid, 0);
867 else
868 rc = VERR_TIMEOUT;
869 if (rc == VERR_TIMEOUT)
870 rc = RTReqWait(mPendingDummyReq, cMsTimeout);
871 if (RT_SUCCESS(rc))
872 {
873 RTReqFree(mPendingDummyReq);
874 mPendingDummyReq = NULL;
875 }
876 }
877#else
878 NOREF(cMsTimeout);
879 rc = VINF_SUCCESS;
880#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
881
882 return rc;
883}
884
885/** Helper query used by getOldNotification */
886int Service::getOldNotificationInternal(const char *pszPatterns,
887 uint64_t u64Timestamp,
888 Property *pProp)
889{
890 int rc = VINF_SUCCESS;
891 bool warn = false;
892
893 /* We count backwards, as the guest should normally be querying the
894 * most recent events. */
895 PropertyList::reverse_iterator it = mGuestNotifications.rbegin();
896 for (; it->mTimestamp != u64Timestamp && it != mGuestNotifications.rend();
897 ++it) {}
898 /* Warn if the timestamp was not found. */
899 if (it->mTimestamp != u64Timestamp)
900 warn = true;
901 /* Now look for an event matching the patterns supplied. The base()
902 * member conveniently points to the following element. */
903 PropertyList::iterator base = it.base();
904 for (; !base->Matches(pszPatterns) && base != mGuestNotifications.end();
905 ++base) {}
906 if (RT_SUCCESS(rc) && base != mGuestNotifications.end())
907 *pProp = *base;
908 else if (RT_SUCCESS(rc))
909 *pProp = Property();
910 if (warn)
911 rc = VWRN_NOT_FOUND;
912 return rc;
913}
914
915/** Helper query used by getNotification */
916int Service::getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop)
917{
918 int rc = VINF_SUCCESS;
919 /* Format the data to write to the buffer. */
920 std::string buffer;
921 uint64_t u64Timestamp;
922 char *pchBuf;
923 uint32_t cchBuf;
924 rc = paParms[2].getBuffer((void **) &pchBuf, &cchBuf);
925 if (RT_SUCCESS(rc))
926 {
927 char szFlags[MAX_FLAGS_LEN];
928 rc = writeFlags(prop.mFlags, szFlags);
929 if (RT_SUCCESS(rc))
930 {
931 buffer += prop.mName;
932 buffer += '\0';
933 buffer += prop.mValue;
934 buffer += '\0';
935 buffer += szFlags;
936 buffer += '\0';
937 u64Timestamp = prop.mTimestamp;
938 }
939 }
940 /* Write out the data. */
941 if (RT_SUCCESS(rc))
942 {
943 paParms[1].setUInt64(u64Timestamp);
944 paParms[3].setUInt32((uint32_t)buffer.size());
945 if (buffer.size() <= cchBuf)
946 buffer.copy(pchBuf, cchBuf);
947 else
948 rc = VERR_BUFFER_OVERFLOW;
949 }
950 return rc;
951}
952
953/**
954 * Get the next guest notification.
955 *
956 * @returns iprt status value
957 * @param cParms the number of HGCM parameters supplied
958 * @param paParms the array of HGCM parameters
959 * @thread HGCM
960 * @throws can throw std::bad_alloc
961 */
962int Service::getNotification(VBOXHGCMCALLHANDLE callHandle, uint32_t cParms,
963 VBOXHGCMSVCPARM paParms[])
964{
965 int rc = VINF_SUCCESS;
966 char *pszPatterns = NULL; /* shut up gcc */
967 char *pchBuf;
968 uint32_t cchPatterns = 0;
969 uint32_t cchBuf = 0;
970 uint64_t u64Timestamp;
971
972 /*
973 * Get the HGCM function arguments and perform basic verification.
974 */
975 LogFlowThisFunc(("\n"));
976 if ( (cParms != 4) /* Hardcoded value as the next lines depend on it. */
977 || RT_FAILURE(paParms[0].getString(&pszPatterns, &cchPatterns)) /* patterns */
978 || RT_FAILURE(paParms[1].getUInt64(&u64Timestamp)) /* timestamp */
979 || RT_FAILURE(paParms[2].getBuffer((void **) &pchBuf, &cchBuf)) /* return buffer */
980 )
981 rc = VERR_INVALID_PARAMETER;
982 if (RT_SUCCESS(rc))
983 LogFlow((" pszPatterns=%s, u64Timestamp=%llu\n", pszPatterns,
984 u64Timestamp));
985
986 /*
987 * If no timestamp was supplied or no notification was found in the queue
988 * of old notifications, enqueue the request in the waiting queue.
989 */
990 Property prop;
991 if (RT_SUCCESS(rc) && u64Timestamp != 0)
992 rc = getOldNotification(pszPatterns, u64Timestamp, &prop);
993 if (RT_SUCCESS(rc) && prop.isNull())
994 {
995 mGuestWaiters.push_back(GuestCall(callHandle, GET_NOTIFICATION,
996 paParms, rc));
997 rc = VINF_HGCM_ASYNC_EXECUTE;
998 }
999 /*
1000 * Otherwise reply at once with the enqueued notification we found.
1001 */
1002 else
1003 {
1004 int rc2 = getNotificationWriteOut(paParms, prop);
1005 if (RT_FAILURE(rc2))
1006 rc = rc2;
1007 }
1008 return rc;
1009}
1010
1011/**
1012 * Notify the service owner and the guest that a property has been
1013 * added/deleted/changed
1014 * @param pszProperty the name of the property which has changed
1015 * @param u64Timestamp the time at which the change took place
1016 * @note this call allocates memory which the reqNotify request is expected to
1017 * free again, using RTStrFree().
1018 *
1019 * @thread HGCM service
1020 */
1021void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp)
1022{
1023 int rc = VINF_SUCCESS;
1024
1025 AssertPtrReturnVoid(pszProperty);
1026 LogFlowThisFunc (("pszProperty=%s, u64Timestamp=%llu\n", pszProperty, u64Timestamp));
1027 /* Ensure that our timestamp is different to the last one. */
1028 if ( !mGuestNotifications.empty()
1029 && u64Timestamp == mGuestNotifications.back().mTimestamp)
1030 ++u64Timestamp;
1031
1032 /*
1033 * Try to find the property. Create a change event if we find it and a
1034 * delete event if we do not.
1035 */
1036 Property prop;
1037 prop.mName = pszProperty;
1038 prop.mTimestamp = u64Timestamp;
1039 /* prop is currently a delete event for pszProperty */
1040 bool found = false;
1041 if (RT_SUCCESS(rc))
1042 for (PropertyList::const_iterator it = mProperties.begin();
1043 !found && it != mProperties.end(); ++it)
1044 if (it->mName.compare(pszProperty) == 0)
1045 {
1046 found = true;
1047 /* Make prop into a change event. */
1048 prop.mValue = it->mValue;
1049 prop.mFlags = it->mFlags;
1050 }
1051
1052
1053 /* Release waiters if applicable and add the event to the queue for
1054 * guest notifications */
1055 if (RT_SUCCESS(rc))
1056 {
1057 try
1058 {
1059 CallList::iterator it = mGuestWaiters.begin();
1060 while (it != mGuestWaiters.end())
1061 {
1062 const char *pszPatterns;
1063 uint32_t cchPatterns;
1064 it->mParms[0].getString(&pszPatterns, &cchPatterns);
1065 if (prop.Matches(pszPatterns))
1066 {
1067 GuestCall curCall = *it;
1068 int rc2 = getNotificationWriteOut(curCall.mParms, prop);
1069 if (RT_SUCCESS(rc2))
1070 rc2 = curCall.mRc;
1071 mpHelpers->pfnCallComplete(curCall.mHandle, rc2);
1072 it = mGuestWaiters.erase(it);
1073 }
1074 else
1075 ++it;
1076 }
1077 mGuestNotifications.push_back(prop);
1078 }
1079 catch (std::bad_alloc)
1080 {
1081 rc = VERR_NO_MEMORY;
1082 }
1083 }
1084 if (mGuestNotifications.size() > MAX_GUEST_NOTIFICATIONS)
1085 mGuestNotifications.pop_front();
1086
1087#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
1088 /*
1089 * Host notifications - first case: if the property exists then send its
1090 * current value
1091 */
1092 char *pszName = NULL, *pszValue = NULL, *pszFlags = NULL;
1093
1094 if (found && mpfnHostCallback != NULL)
1095 {
1096 char szFlags[MAX_FLAGS_LEN];
1097 /* Send out a host notification */
1098 rc = writeFlags(prop.mFlags, szFlags);
1099 if (RT_SUCCESS(rc))
1100 rc = RTStrDupEx(&pszName, pszProperty);
1101 if (RT_SUCCESS(rc))
1102 rc = RTStrDupEx(&pszValue, prop.mValue.c_str());
1103 if (RT_SUCCESS(rc))
1104 rc = RTStrDupEx(&pszFlags, szFlags);
1105 if (RT_SUCCESS(rc))
1106 {
1107 LogFlowThisFunc (("pszName=%p (%s)\n", pszName, pszName));
1108 LogFlowThisFunc (("pszValue=%p (%s)\n", pszValue, pszValue));
1109 LogFlowThisFunc (("pszFlags=%p (%s)\n", pszFlags, pszFlags));
1110 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT,
1111 (PFNRT)Service::reqNotify, 7, mpfnHostCallback,
1112 mpvHostData, pszName, pszValue,
1113 (uint32_t) RT_HIDWORD(u64Timestamp),
1114 (uint32_t) RT_LODWORD(u64Timestamp), pszFlags);
1115 }
1116 }
1117
1118 /*
1119 * Host notifications - second case: if the property does not exist then
1120 * send the host an empty value
1121 */
1122 if (!found && mpfnHostCallback != NULL)
1123 {
1124 /* Send out a host notification */
1125 rc = RTStrDupEx(&pszName, pszProperty);
1126 if (RT_SUCCESS(rc))
1127 {
1128 LogFlowThisFunc (("pszName=%p (%s)\n", pszName, pszName));
1129 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT,
1130 (PFNRT)Service::reqNotify, 7, mpfnHostCallback,
1131 mpvHostData, pszName, (uintptr_t) NULL,
1132 (uint32_t) RT_HIDWORD(u64Timestamp),
1133 (uint32_t) RT_LODWORD(u64Timestamp),
1134 (uintptr_t) NULL);
1135 }
1136 }
1137 if (RT_FAILURE(rc)) /* clean up if we failed somewhere */
1138 {
1139 LogThisFunc (("Failed, freeing allocated strings.\n"));
1140 RTStrFree(pszName);
1141 RTStrFree(pszValue);
1142 RTStrFree(pszFlags);
1143 }
1144 LogFlowThisFunc (("returning\n"));
1145#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
1146}
1147
1148/**
1149 * Notify the service owner that a property has been added/deleted/changed.
1150 * asynchronous part.
1151 * @param pszProperty the name of the property which has changed
1152 * @note this call allocates memory which the reqNotify request is expected to
1153 * free again, using RTStrFree().
1154 *
1155 * @thread request thread
1156 */
1157/* static */
1158DECLCALLBACK(int) Service::reqNotify(PFNHGCMSVCEXT pfnCallback, void *pvData,
1159 char *pszName, char *pszValue, uint32_t u32TimeHigh,
1160 uint32_t u32TimeLow, char *pszFlags)
1161{
1162 LogFlowFunc (("pfnCallback=%p, pvData=%p, pszName=%p, pszValue=%p, u32TimeHigh=%u, u32TimeLow=%u, pszFlags=%p\n", pfnCallback, pvData, pszName, pszValue, u32TimeHigh, u32TimeLow, pszFlags));
1163 LogFlowFunc (("pszName=%s\n", pszName));
1164 LogFlowFunc (("pszValue=%s\n", pszValue));
1165 LogFlowFunc (("pszFlags=%s\n", pszFlags));
1166 /* LogFlowFunc (("pfnCallback=%p, pvData=%p, pszName=%s, pszValue=%s, u32TimeHigh=%u, u32TimeLow=%u, pszFlags=%s\n", pfnCallback, pvData, pszName, pszValue, u32TimeHigh, u32TimeLow, pszFlags)); */
1167 HOSTCALLBACKDATA HostCallbackData;
1168 HostCallbackData.u32Magic = HOSTCALLBACKMAGIC;
1169 HostCallbackData.pcszName = pszName;
1170 HostCallbackData.pcszValue = pszValue;
1171 HostCallbackData.u64Timestamp = RT_MAKE_U64(u32TimeLow, u32TimeHigh);
1172 HostCallbackData.pcszFlags = pszFlags;
1173 int rc = pfnCallback(pvData, 0 /*u32Function*/, (void *)(&HostCallbackData),
1174 sizeof(HostCallbackData));
1175 AssertRC(rc);
1176 LogFlowFunc (("Freeing strings\n"));
1177 RTStrFree(pszName);
1178 RTStrFree(pszValue);
1179 RTStrFree(pszFlags);
1180 LogFlowFunc (("returning success\n"));
1181 return VINF_SUCCESS;
1182}
1183
1184
1185/**
1186 * Handle an HGCM service call.
1187 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
1188 * @note All functions which do not involve an unreasonable delay will be
1189 * handled synchronously. If needed, we will add a request handler
1190 * thread in future for those which do.
1191 *
1192 * @thread HGCM
1193 */
1194void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
1195 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
1196 VBOXHGCMSVCPARM paParms[])
1197{
1198 int rc = VINF_SUCCESS;
1199 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
1200 u32ClientID, eFunction, cParms, paParms));
1201
1202 try
1203 {
1204 switch (eFunction)
1205 {
1206 /* The guest wishes to read a property */
1207 case GET_PROP:
1208 LogFlowFunc(("GET_PROP\n"));
1209 rc = getProperty(cParms, paParms);
1210 break;
1211
1212 /* The guest wishes to set a property */
1213 case SET_PROP:
1214 LogFlowFunc(("SET_PROP\n"));
1215 rc = setProperty(cParms, paParms, true);
1216 break;
1217
1218 /* The guest wishes to set a property value */
1219 case SET_PROP_VALUE:
1220 LogFlowFunc(("SET_PROP_VALUE\n"));
1221 rc = setProperty(cParms, paParms, true);
1222 break;
1223
1224 /* The guest wishes to remove a configuration value */
1225 case DEL_PROP:
1226 LogFlowFunc(("DEL_PROP\n"));
1227 rc = delProperty(cParms, paParms, true);
1228 break;
1229
1230 /* The guest wishes to enumerate all properties */
1231 case ENUM_PROPS:
1232 LogFlowFunc(("ENUM_PROPS\n"));
1233 rc = enumProps(cParms, paParms);
1234 break;
1235
1236 /* The guest wishes to get the next property notification */
1237 case GET_NOTIFICATION:
1238 LogFlowFunc(("GET_NOTIFICATION\n"));
1239 rc = getNotification(callHandle, cParms, paParms);
1240 break;
1241
1242 default:
1243 rc = VERR_NOT_IMPLEMENTED;
1244 }
1245 }
1246 catch (std::bad_alloc)
1247 {
1248 rc = VERR_NO_MEMORY;
1249 }
1250 LogFlowFunc(("rc = %Rrc\n", rc));
1251 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1252 {
1253 mpHelpers->pfnCallComplete (callHandle, rc);
1254 }
1255}
1256
1257
1258/**
1259 * Service call handler for the host.
1260 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
1261 * @thread hgcm
1262 */
1263int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1264{
1265 int rc = VINF_SUCCESS;
1266
1267 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
1268 eFunction, cParms, paParms));
1269
1270 try
1271 {
1272 switch (eFunction)
1273 {
1274 /* The host wishes to set a block of properties */
1275 case SET_PROPS_HOST:
1276 LogFlowFunc(("SET_PROPS_HOST\n"));
1277 rc = setPropertyBlock(cParms, paParms);
1278 break;
1279
1280 /* The host wishes to read a configuration value */
1281 case GET_PROP_HOST:
1282 LogFlowFunc(("GET_PROP_HOST\n"));
1283 rc = getProperty(cParms, paParms);
1284 break;
1285
1286 /* The host wishes to set a configuration value */
1287 case SET_PROP_HOST:
1288 LogFlowFunc(("SET_PROP_HOST\n"));
1289 rc = setProperty(cParms, paParms, false);
1290 break;
1291
1292 /* The host wishes to set a configuration value */
1293 case SET_PROP_VALUE_HOST:
1294 LogFlowFunc(("SET_PROP_VALUE_HOST\n"));
1295 rc = setProperty(cParms, paParms, false);
1296 break;
1297
1298 /* The host wishes to remove a configuration value */
1299 case DEL_PROP_HOST:
1300 LogFlowFunc(("DEL_PROP_HOST\n"));
1301 rc = delProperty(cParms, paParms, false);
1302 break;
1303
1304 /* The host wishes to enumerate all properties */
1305 case ENUM_PROPS_HOST:
1306 LogFlowFunc(("ENUM_PROPS\n"));
1307 rc = enumProps(cParms, paParms);
1308 break;
1309
1310 /* The host wishes to set global flags for the service */
1311 case SET_GLOBAL_FLAGS_HOST:
1312 LogFlowFunc(("SET_GLOBAL_FLAGS_HOST\n"));
1313 if (cParms == 1)
1314 {
1315 uint32_t eFlags;
1316 rc = paParms[0].getUInt32(&eFlags);
1317 if (RT_SUCCESS(rc))
1318 meGlobalFlags = (ePropFlags)eFlags;
1319 }
1320 else
1321 rc = VERR_INVALID_PARAMETER;
1322 break;
1323
1324 /* The host wishes to flush all pending notification */
1325 case FLUSH_NOTIFICATIONS_HOST:
1326 LogFlowFunc(("FLUSH_NOTIFICATIONS_HOST\n"));
1327 if (cParms == 1)
1328 {
1329 uint32_t cMsTimeout;
1330 rc = paParms[0].getUInt32(&cMsTimeout);
1331 if (RT_SUCCESS(rc))
1332 rc = flushNotifications(cMsTimeout);
1333 }
1334 else
1335 rc = VERR_INVALID_PARAMETER;
1336 break;
1337
1338 default:
1339 rc = VERR_NOT_SUPPORTED;
1340 break;
1341 }
1342 }
1343 catch (std::bad_alloc)
1344 {
1345 rc = VERR_NO_MEMORY;
1346 }
1347
1348 LogFlowFunc(("rc = %Rrc\n", rc));
1349 return rc;
1350}
1351
1352int Service::uninit()
1353{
1354 int rc = VINF_SUCCESS;
1355
1356 ASMAtomicWriteBool(&mfExitThread, true);
1357
1358#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
1359 /*
1360 * Send a dummy request to the thread so it is forced out of the loop and
1361 * notice that the exit flag is set. Give up waiting after 5 mins.
1362 * We call flushNotifications first to try clean up any pending request.
1363 */
1364 flushNotifications(120*1000);
1365
1366 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT, (PFNRT)reqVoid, 0);
1367 if (RT_SUCCESS(rc))
1368 {
1369 unsigned count = 0;
1370 do
1371 {
1372 rc = RTThreadWait(mReqThread, 1000, NULL);
1373 ++count;
1374 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
1375 } while ((VERR_TIMEOUT == rc) && (count < 300));
1376 }
1377#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
1378 if (RT_SUCCESS(rc))
1379 RTReqDestroyQueue(mReqQueue);
1380 return rc;
1381}
1382
1383} /* namespace guestProp */
1384
1385using guestProp::Service;
1386
1387/**
1388 * @copydoc VBOXHGCMSVCLOAD
1389 */
1390extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1391{
1392 int rc = VINF_SUCCESS;
1393
1394 LogFlowFunc(("ptable = %p\n", ptable));
1395
1396 if (!VALID_PTR(ptable))
1397 {
1398 rc = VERR_INVALID_PARAMETER;
1399 }
1400 else
1401 {
1402 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1403
1404 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1405 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1406 {
1407 rc = VERR_VERSION_MISMATCH;
1408 }
1409 else
1410 {
1411 std::auto_ptr<Service> apService;
1412 /* No exceptions may propogate outside. */
1413 try {
1414 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1415 } catch (int rcThrown) {
1416 rc = rcThrown;
1417 } catch (...) {
1418 rc = VERR_UNRESOLVED_ERROR;
1419 }
1420
1421 if (RT_SUCCESS(rc))
1422 {
1423 /* We do not maintain connections, so no client data is needed. */
1424 ptable->cbClient = 0;
1425
1426 ptable->pfnUnload = Service::svcUnload;
1427 ptable->pfnConnect = Service::svcConnectDisconnect;
1428 ptable->pfnDisconnect = Service::svcConnectDisconnect;
1429 ptable->pfnCall = Service::svcCall;
1430 ptable->pfnHostCall = Service::svcHostCall;
1431 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1432 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1433 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1434
1435 /* Service specific initialization. */
1436 ptable->pvService = apService.release();
1437 }
1438 }
1439 }
1440
1441 LogFlowFunc(("returning %Rrc\n", rc));
1442 return rc;
1443}
1444
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