VirtualBox

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

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

GuestProperties/service.cpp: shut up more gcc warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 49.0 KB
Line 
1/* $Id: service.cpp 25601 2009-12-31 00:40:25Z 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 = 0; /* ditto */
618 uint32_t cchValue = 0; /* ditto */
619 uint32_t cchFlags = 0;
620 uint32_t fFlags = NILFLAG;
621 RTTIMESPEC time;
622 uint64_t u64TimeNano = RTTimeSpecGetNano(RTTimeNow(&time));
623
624 LogFlowThisFunc(("\n"));
625 /*
626 * First of all, make sure that we won't exceed the maximum number of properties.
627 */
628 if (mProperties.size() >= MAX_PROPS)
629 rc = VERR_TOO_MUCH_DATA;
630
631 /*
632 * General parameter correctness checking.
633 */
634 if ( RT_SUCCESS(rc)
635 && ( (cParms < 2) || (cParms > 3) /* Hardcoded value as the next lines depend on it. */
636 || RT_FAILURE(paParms[0].getString(&pcszName, &cchName)) /* name */
637 || RT_FAILURE(paParms[1].getString(&pcszValue, &cchValue)) /* value */
638 || ( (3 == cParms)
639 && RT_FAILURE(paParms[2].getString(&pcszFlags, &cchFlags)) /* flags */
640 )
641 )
642 )
643 rc = VERR_INVALID_PARAMETER;
644
645 /*
646 * Check the values passed in the parameters for correctness.
647 */
648 if (RT_SUCCESS(rc))
649 rc = validateName(pcszName, cchName);
650 if (RT_SUCCESS(rc))
651 rc = validateValue(pcszValue, cchValue);
652 if ((3 == cParms) && RT_SUCCESS(rc))
653 rc = RTStrValidateEncodingEx(pcszFlags, cchFlags,
654 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
655 if ((3 == cParms) && RT_SUCCESS(rc))
656 rc = validateFlags(pcszFlags, &fFlags);
657 if (RT_SUCCESS(rc))
658 {
659 /*
660 * If the property already exists, check its flags to see if we are allowed
661 * to change it.
662 */
663 PropertyList::iterator it;
664 bool found = false;
665 for (it = mProperties.begin(); it != mProperties.end(); ++it)
666 if (it->mName.compare(pcszName) == 0)
667 {
668 found = true;
669 break;
670 }
671
672 rc = checkPermission(found ? (ePropFlags)it->mFlags : NILFLAG,
673 isGuest);
674 if (rc == VINF_SUCCESS)
675 {
676 /*
677 * Set the actual value
678 */
679 if (found)
680 {
681 it->mValue = pcszValue;
682 it->mTimestamp = u64TimeNano;
683 it->mFlags = fFlags;
684 }
685 else /* This can throw. No problem as we have nothing to roll back. */
686 mProperties.push_back(Property(pcszName, pcszValue, u64TimeNano, fFlags));
687
688 /*
689 * Send a notification to the host and return.
690 */
691 // if (isGuest) /* Notify the host even for properties that the host
692 // * changed. Less efficient, but ensures consistency. */
693 doNotifications(pcszName, u64TimeNano);
694 Log2(("Set string %s, rc=%Rrc, value=%s\n", pcszName, rc, pcszValue));
695 }
696 }
697
698 LogFlowThisFunc(("rc = %Rrc\n", rc));
699 return rc;
700}
701
702
703/**
704 * Remove a value in the property registry by name, checking the validity
705 * of the arguments passed.
706 *
707 * @returns iprt status value
708 * @param cParms the number of HGCM parameters supplied
709 * @param paParms the array of HGCM parameters
710 * @param isGuest is this call coming from the guest (or the host)?
711 * @thread HGCM
712 */
713int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
714{
715 int rc = VINF_SUCCESS;
716 const char *pcszName = NULL; /* shut up gcc */
717 uint32_t cbName;
718
719 LogFlowThisFunc(("\n"));
720
721 /*
722 * Check the user-supplied parameters.
723 */
724 if ( (cParms == 1) /* Hardcoded value as the next lines depend on it. */
725 && RT_SUCCESS(paParms[0].getString(&pcszName, &cbName)) /* name */
726 )
727 rc = validateName(pcszName, cbName);
728 else
729 rc = VERR_INVALID_PARAMETER;
730 if (RT_SUCCESS(rc))
731 {
732 /*
733 * If the property exists, check its flags to see if we are allowed
734 * to change it.
735 */
736 PropertyList::iterator it;
737 bool found = false;
738 for (it = mProperties.begin(); it != mProperties.end(); ++it)
739 if (it->mName.compare(pcszName) == 0)
740 {
741 found = true;
742 rc = checkPermission((ePropFlags)it->mFlags, isGuest);
743 break;
744 }
745
746 /*
747 * And delete the property if all is well.
748 */
749 if (rc == VINF_SUCCESS && found)
750 {
751 RTTIMESPEC time;
752 uint64_t u64Timestamp = RTTimeSpecGetNano(RTTimeNow(&time));
753 mProperties.erase(it);
754 // if (isGuest) /* Notify the host even for properties that the host
755 // * changed. Less efficient, but ensures consistency. */
756 doNotifications(pcszName, u64Timestamp);
757 }
758 }
759
760 LogFlowThisFunc(("rc = %Rrc\n", rc));
761 return rc;
762}
763
764/**
765 * Enumerate guest properties by mask, checking the validity
766 * of the arguments passed.
767 *
768 * @returns iprt status value
769 * @param cParms the number of HGCM parameters supplied
770 * @param paParms the array of HGCM parameters
771 * @thread HGCM
772 */
773int Service::enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
774{
775 int rc = VINF_SUCCESS;
776
777 /*
778 * Get the HGCM function arguments.
779 */
780 char *pcchPatterns = NULL, *pchBuf = NULL;
781 uint32_t cchPatterns = 0, cchBuf = 0;
782 LogFlowThisFunc(("\n"));
783 if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
784 || RT_FAILURE(paParms[0].getString(&pcchPatterns, &cchPatterns)) /* patterns */
785 || RT_FAILURE(paParms[1].getBuffer((void **) &pchBuf, &cchBuf)) /* return buffer */
786 )
787 rc = VERR_INVALID_PARAMETER;
788 if (RT_SUCCESS(rc) && cchPatterns > MAX_PATTERN_LEN)
789 rc = VERR_TOO_MUCH_DATA;
790
791 /*
792 * First repack the patterns into the format expected by RTStrSimplePatternMatch()
793 */
794 char pszPatterns[MAX_PATTERN_LEN];
795 for (unsigned i = 0; i < cchPatterns - 1; ++i)
796 if (pcchPatterns[i] != '\0')
797 pszPatterns[i] = pcchPatterns[i];
798 else
799 pszPatterns[i] = '|';
800 pszPatterns[cchPatterns - 1] = '\0';
801
802 /*
803 * Next enumerate into a temporary buffer. This can throw, but this is
804 * not a problem as we have nothing to roll back.
805 */
806 std::string buffer;
807 for (PropertyList::const_iterator it = mProperties.begin();
808 RT_SUCCESS(rc) && (it != mProperties.end()); ++it)
809 {
810 if (it->Matches(pszPatterns))
811 {
812 char szFlags[MAX_FLAGS_LEN];
813 char szTimestamp[256];
814 uint32_t cchTimestamp;
815 buffer += it->mName;
816 buffer += '\0';
817 buffer += it->mValue;
818 buffer += '\0';
819 cchTimestamp = RTStrFormatNumber(szTimestamp, it->mTimestamp,
820 10, 0, 0, 0);
821 buffer.append(szTimestamp, cchTimestamp);
822 buffer += '\0';
823 rc = writeFlags(it->mFlags, szFlags);
824 if (RT_SUCCESS(rc))
825 buffer += szFlags;
826 buffer += '\0';
827 }
828 }
829 buffer.append(4, '\0'); /* The final terminators */
830
831 /*
832 * Finally write out the temporary buffer to the real one if it is not too
833 * small.
834 */
835 if (RT_SUCCESS(rc))
836 {
837 paParms[2].setUInt32 ((uint32_t)buffer.size());
838 /* Copy the memory if it fits into the guest buffer */
839 if (buffer.size() <= cchBuf)
840 buffer.copy(pchBuf, cchBuf);
841 else
842 rc = VERR_BUFFER_OVERFLOW;
843 }
844 return rc;
845}
846
847/**
848 * Flushes the notifications.
849 *
850 * @returns iprt status value
851 * @param cMsTimeout The timeout in milliseconds.
852 * @thread HGCM
853 */
854int Service::flushNotifications(uint32_t cMsTimeout)
855{
856 LogFlowThisFunc(("cMsTimeout=%RU32\n", cMsTimeout));
857 int rc;
858
859#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
860 /*
861 * Wait for the thread to finish processing all current requests.
862 */
863 if (!mPendingDummyReq && !RTReqIsBusy(mReqQueue))
864 rc = VINF_SUCCESS;
865 else
866 {
867 if (!mPendingDummyReq)
868 rc = RTReqCallEx(mReqQueue, &mPendingDummyReq, 0 /*cMillies*/, RTREQFLAGS_VOID, (PFNRT)reqVoid, 0);
869 else
870 rc = VERR_TIMEOUT;
871 if (rc == VERR_TIMEOUT)
872 rc = RTReqWait(mPendingDummyReq, cMsTimeout);
873 if (RT_SUCCESS(rc))
874 {
875 RTReqFree(mPendingDummyReq);
876 mPendingDummyReq = NULL;
877 }
878 }
879#else
880 NOREF(cMsTimeout);
881 rc = VINF_SUCCESS;
882#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
883
884 return rc;
885}
886
887/** Helper query used by getOldNotification */
888int Service::getOldNotificationInternal(const char *pszPatterns,
889 uint64_t u64Timestamp,
890 Property *pProp)
891{
892 int rc = VINF_SUCCESS;
893 bool warn = false;
894
895 /* We count backwards, as the guest should normally be querying the
896 * most recent events. */
897 PropertyList::reverse_iterator it = mGuestNotifications.rbegin();
898 for (; it->mTimestamp != u64Timestamp && it != mGuestNotifications.rend();
899 ++it) {}
900 /* Warn if the timestamp was not found. */
901 if (it->mTimestamp != u64Timestamp)
902 warn = true;
903 /* Now look for an event matching the patterns supplied. The base()
904 * member conveniently points to the following element. */
905 PropertyList::iterator base = it.base();
906 for (; !base->Matches(pszPatterns) && base != mGuestNotifications.end();
907 ++base) {}
908 if (RT_SUCCESS(rc) && base != mGuestNotifications.end())
909 *pProp = *base;
910 else if (RT_SUCCESS(rc))
911 *pProp = Property();
912 if (warn)
913 rc = VWRN_NOT_FOUND;
914 return rc;
915}
916
917/** Helper query used by getNotification */
918int Service::getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop)
919{
920 int rc = VINF_SUCCESS;
921 /* Format the data to write to the buffer. */
922 std::string buffer;
923 uint64_t u64Timestamp;
924 char *pchBuf;
925 uint32_t cchBuf;
926 rc = paParms[2].getBuffer((void **) &pchBuf, &cchBuf);
927 if (RT_SUCCESS(rc))
928 {
929 char szFlags[MAX_FLAGS_LEN];
930 rc = writeFlags(prop.mFlags, szFlags);
931 if (RT_SUCCESS(rc))
932 {
933 buffer += prop.mName;
934 buffer += '\0';
935 buffer += prop.mValue;
936 buffer += '\0';
937 buffer += szFlags;
938 buffer += '\0';
939 u64Timestamp = prop.mTimestamp;
940 }
941 }
942 /* Write out the data. */
943 if (RT_SUCCESS(rc))
944 {
945 paParms[1].setUInt64(u64Timestamp);
946 paParms[3].setUInt32((uint32_t)buffer.size());
947 if (buffer.size() <= cchBuf)
948 buffer.copy(pchBuf, cchBuf);
949 else
950 rc = VERR_BUFFER_OVERFLOW;
951 }
952 return rc;
953}
954
955/**
956 * Get the next guest notification.
957 *
958 * @returns iprt status value
959 * @param cParms the number of HGCM parameters supplied
960 * @param paParms the array of HGCM parameters
961 * @thread HGCM
962 * @throws can throw std::bad_alloc
963 */
964int Service::getNotification(VBOXHGCMCALLHANDLE callHandle, uint32_t cParms,
965 VBOXHGCMSVCPARM paParms[])
966{
967 int rc = VINF_SUCCESS;
968 char *pszPatterns = NULL; /* shut up gcc */
969 char *pchBuf;
970 uint32_t cchPatterns = 0;
971 uint32_t cchBuf = 0;
972 uint64_t u64Timestamp;
973
974 /*
975 * Get the HGCM function arguments and perform basic verification.
976 */
977 LogFlowThisFunc(("\n"));
978 if ( (cParms != 4) /* Hardcoded value as the next lines depend on it. */
979 || RT_FAILURE(paParms[0].getString(&pszPatterns, &cchPatterns)) /* patterns */
980 || RT_FAILURE(paParms[1].getUInt64(&u64Timestamp)) /* timestamp */
981 || RT_FAILURE(paParms[2].getBuffer((void **) &pchBuf, &cchBuf)) /* return buffer */
982 )
983 rc = VERR_INVALID_PARAMETER;
984 if (RT_SUCCESS(rc))
985 LogFlow((" pszPatterns=%s, u64Timestamp=%llu\n", pszPatterns,
986 u64Timestamp));
987
988 /*
989 * If no timestamp was supplied or no notification was found in the queue
990 * of old notifications, enqueue the request in the waiting queue.
991 */
992 Property prop;
993 if (RT_SUCCESS(rc) && u64Timestamp != 0)
994 rc = getOldNotification(pszPatterns, u64Timestamp, &prop);
995 if (RT_SUCCESS(rc) && prop.isNull())
996 {
997 mGuestWaiters.push_back(GuestCall(callHandle, GET_NOTIFICATION,
998 paParms, rc));
999 rc = VINF_HGCM_ASYNC_EXECUTE;
1000 }
1001 /*
1002 * Otherwise reply at once with the enqueued notification we found.
1003 */
1004 else
1005 {
1006 int rc2 = getNotificationWriteOut(paParms, prop);
1007 if (RT_FAILURE(rc2))
1008 rc = rc2;
1009 }
1010 return rc;
1011}
1012
1013/**
1014 * Notify the service owner and the guest that a property has been
1015 * added/deleted/changed
1016 * @param pszProperty the name of the property which has changed
1017 * @param u64Timestamp the time at which the change took place
1018 * @note this call allocates memory which the reqNotify request is expected to
1019 * free again, using RTStrFree().
1020 *
1021 * @thread HGCM service
1022 */
1023void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp)
1024{
1025 int rc = VINF_SUCCESS;
1026
1027 AssertPtrReturnVoid(pszProperty);
1028 LogFlowThisFunc (("pszProperty=%s, u64Timestamp=%llu\n", pszProperty, u64Timestamp));
1029 /* Ensure that our timestamp is different to the last one. */
1030 if ( !mGuestNotifications.empty()
1031 && u64Timestamp == mGuestNotifications.back().mTimestamp)
1032 ++u64Timestamp;
1033
1034 /*
1035 * Try to find the property. Create a change event if we find it and a
1036 * delete event if we do not.
1037 */
1038 Property prop;
1039 prop.mName = pszProperty;
1040 prop.mTimestamp = u64Timestamp;
1041 /* prop is currently a delete event for pszProperty */
1042 bool found = false;
1043 if (RT_SUCCESS(rc))
1044 for (PropertyList::const_iterator it = mProperties.begin();
1045 !found && it != mProperties.end(); ++it)
1046 if (it->mName.compare(pszProperty) == 0)
1047 {
1048 found = true;
1049 /* Make prop into a change event. */
1050 prop.mValue = it->mValue;
1051 prop.mFlags = it->mFlags;
1052 }
1053
1054
1055 /* Release waiters if applicable and add the event to the queue for
1056 * guest notifications */
1057 if (RT_SUCCESS(rc))
1058 {
1059 try
1060 {
1061 CallList::iterator it = mGuestWaiters.begin();
1062 while (it != mGuestWaiters.end())
1063 {
1064 const char *pszPatterns;
1065 uint32_t cchPatterns;
1066 it->mParms[0].getString(&pszPatterns, &cchPatterns);
1067 if (prop.Matches(pszPatterns))
1068 {
1069 GuestCall curCall = *it;
1070 int rc2 = getNotificationWriteOut(curCall.mParms, prop);
1071 if (RT_SUCCESS(rc2))
1072 rc2 = curCall.mRc;
1073 mpHelpers->pfnCallComplete(curCall.mHandle, rc2);
1074 it = mGuestWaiters.erase(it);
1075 }
1076 else
1077 ++it;
1078 }
1079 mGuestNotifications.push_back(prop);
1080 }
1081 catch (std::bad_alloc)
1082 {
1083 rc = VERR_NO_MEMORY;
1084 }
1085 }
1086 if (mGuestNotifications.size() > MAX_GUEST_NOTIFICATIONS)
1087 mGuestNotifications.pop_front();
1088
1089#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
1090 /*
1091 * Host notifications - first case: if the property exists then send its
1092 * current value
1093 */
1094 char *pszName = NULL, *pszValue = NULL, *pszFlags = NULL;
1095
1096 if (found && mpfnHostCallback != NULL)
1097 {
1098 char szFlags[MAX_FLAGS_LEN];
1099 /* Send out a host notification */
1100 rc = writeFlags(prop.mFlags, szFlags);
1101 if (RT_SUCCESS(rc))
1102 rc = RTStrDupEx(&pszName, pszProperty);
1103 if (RT_SUCCESS(rc))
1104 rc = RTStrDupEx(&pszValue, prop.mValue.c_str());
1105 if (RT_SUCCESS(rc))
1106 rc = RTStrDupEx(&pszFlags, szFlags);
1107 if (RT_SUCCESS(rc))
1108 {
1109 LogFlowThisFunc (("pszName=%p (%s)\n", pszName, pszName));
1110 LogFlowThisFunc (("pszValue=%p (%s)\n", pszValue, pszValue));
1111 LogFlowThisFunc (("pszFlags=%p (%s)\n", pszFlags, pszFlags));
1112 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT,
1113 (PFNRT)Service::reqNotify, 7, mpfnHostCallback,
1114 mpvHostData, pszName, pszValue,
1115 (uint32_t) RT_HIDWORD(u64Timestamp),
1116 (uint32_t) RT_LODWORD(u64Timestamp), pszFlags);
1117 }
1118 }
1119
1120 /*
1121 * Host notifications - second case: if the property does not exist then
1122 * send the host an empty value
1123 */
1124 if (!found && mpfnHostCallback != NULL)
1125 {
1126 /* Send out a host notification */
1127 rc = RTStrDupEx(&pszName, pszProperty);
1128 if (RT_SUCCESS(rc))
1129 {
1130 LogFlowThisFunc (("pszName=%p (%s)\n", pszName, pszName));
1131 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT,
1132 (PFNRT)Service::reqNotify, 7, mpfnHostCallback,
1133 mpvHostData, pszName, (uintptr_t) NULL,
1134 (uint32_t) RT_HIDWORD(u64Timestamp),
1135 (uint32_t) RT_LODWORD(u64Timestamp),
1136 (uintptr_t) NULL);
1137 }
1138 }
1139 if (RT_FAILURE(rc)) /* clean up if we failed somewhere */
1140 {
1141 LogThisFunc (("Failed, freeing allocated strings.\n"));
1142 RTStrFree(pszName);
1143 RTStrFree(pszValue);
1144 RTStrFree(pszFlags);
1145 }
1146 LogFlowThisFunc (("returning\n"));
1147#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
1148}
1149
1150/**
1151 * Notify the service owner that a property has been added/deleted/changed.
1152 * asynchronous part.
1153 * @param pszProperty the name of the property which has changed
1154 * @note this call allocates memory which the reqNotify request is expected to
1155 * free again, using RTStrFree().
1156 *
1157 * @thread request thread
1158 */
1159/* static */
1160DECLCALLBACK(int) Service::reqNotify(PFNHGCMSVCEXT pfnCallback, void *pvData,
1161 char *pszName, char *pszValue, uint32_t u32TimeHigh,
1162 uint32_t u32TimeLow, char *pszFlags)
1163{
1164 LogFlowFunc (("pfnCallback=%p, pvData=%p, pszName=%p, pszValue=%p, u32TimeHigh=%u, u32TimeLow=%u, pszFlags=%p\n", pfnCallback, pvData, pszName, pszValue, u32TimeHigh, u32TimeLow, pszFlags));
1165 LogFlowFunc (("pszName=%s\n", pszName));
1166 LogFlowFunc (("pszValue=%s\n", pszValue));
1167 LogFlowFunc (("pszFlags=%s\n", pszFlags));
1168 /* LogFlowFunc (("pfnCallback=%p, pvData=%p, pszName=%s, pszValue=%s, u32TimeHigh=%u, u32TimeLow=%u, pszFlags=%s\n", pfnCallback, pvData, pszName, pszValue, u32TimeHigh, u32TimeLow, pszFlags)); */
1169 HOSTCALLBACKDATA HostCallbackData;
1170 HostCallbackData.u32Magic = HOSTCALLBACKMAGIC;
1171 HostCallbackData.pcszName = pszName;
1172 HostCallbackData.pcszValue = pszValue;
1173 HostCallbackData.u64Timestamp = RT_MAKE_U64(u32TimeLow, u32TimeHigh);
1174 HostCallbackData.pcszFlags = pszFlags;
1175 int rc = pfnCallback(pvData, 0 /*u32Function*/, (void *)(&HostCallbackData),
1176 sizeof(HostCallbackData));
1177 AssertRC(rc);
1178 LogFlowFunc (("Freeing strings\n"));
1179 RTStrFree(pszName);
1180 RTStrFree(pszValue);
1181 RTStrFree(pszFlags);
1182 LogFlowFunc (("returning success\n"));
1183 return VINF_SUCCESS;
1184}
1185
1186
1187/**
1188 * Handle an HGCM service call.
1189 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
1190 * @note All functions which do not involve an unreasonable delay will be
1191 * handled synchronously. If needed, we will add a request handler
1192 * thread in future for those which do.
1193 *
1194 * @thread HGCM
1195 */
1196void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
1197 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
1198 VBOXHGCMSVCPARM paParms[])
1199{
1200 int rc = VINF_SUCCESS;
1201 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
1202 u32ClientID, eFunction, cParms, paParms));
1203
1204 try
1205 {
1206 switch (eFunction)
1207 {
1208 /* The guest wishes to read a property */
1209 case GET_PROP:
1210 LogFlowFunc(("GET_PROP\n"));
1211 rc = getProperty(cParms, paParms);
1212 break;
1213
1214 /* The guest wishes to set a property */
1215 case SET_PROP:
1216 LogFlowFunc(("SET_PROP\n"));
1217 rc = setProperty(cParms, paParms, true);
1218 break;
1219
1220 /* The guest wishes to set a property value */
1221 case SET_PROP_VALUE:
1222 LogFlowFunc(("SET_PROP_VALUE\n"));
1223 rc = setProperty(cParms, paParms, true);
1224 break;
1225
1226 /* The guest wishes to remove a configuration value */
1227 case DEL_PROP:
1228 LogFlowFunc(("DEL_PROP\n"));
1229 rc = delProperty(cParms, paParms, true);
1230 break;
1231
1232 /* The guest wishes to enumerate all properties */
1233 case ENUM_PROPS:
1234 LogFlowFunc(("ENUM_PROPS\n"));
1235 rc = enumProps(cParms, paParms);
1236 break;
1237
1238 /* The guest wishes to get the next property notification */
1239 case GET_NOTIFICATION:
1240 LogFlowFunc(("GET_NOTIFICATION\n"));
1241 rc = getNotification(callHandle, cParms, paParms);
1242 break;
1243
1244 default:
1245 rc = VERR_NOT_IMPLEMENTED;
1246 }
1247 }
1248 catch (std::bad_alloc)
1249 {
1250 rc = VERR_NO_MEMORY;
1251 }
1252 LogFlowFunc(("rc = %Rrc\n", rc));
1253 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1254 {
1255 mpHelpers->pfnCallComplete (callHandle, rc);
1256 }
1257}
1258
1259
1260/**
1261 * Service call handler for the host.
1262 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
1263 * @thread hgcm
1264 */
1265int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1266{
1267 int rc = VINF_SUCCESS;
1268
1269 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
1270 eFunction, cParms, paParms));
1271
1272 try
1273 {
1274 switch (eFunction)
1275 {
1276 /* The host wishes to set a block of properties */
1277 case SET_PROPS_HOST:
1278 LogFlowFunc(("SET_PROPS_HOST\n"));
1279 rc = setPropertyBlock(cParms, paParms);
1280 break;
1281
1282 /* The host wishes to read a configuration value */
1283 case GET_PROP_HOST:
1284 LogFlowFunc(("GET_PROP_HOST\n"));
1285 rc = getProperty(cParms, paParms);
1286 break;
1287
1288 /* The host wishes to set a configuration value */
1289 case SET_PROP_HOST:
1290 LogFlowFunc(("SET_PROP_HOST\n"));
1291 rc = setProperty(cParms, paParms, false);
1292 break;
1293
1294 /* The host wishes to set a configuration value */
1295 case SET_PROP_VALUE_HOST:
1296 LogFlowFunc(("SET_PROP_VALUE_HOST\n"));
1297 rc = setProperty(cParms, paParms, false);
1298 break;
1299
1300 /* The host wishes to remove a configuration value */
1301 case DEL_PROP_HOST:
1302 LogFlowFunc(("DEL_PROP_HOST\n"));
1303 rc = delProperty(cParms, paParms, false);
1304 break;
1305
1306 /* The host wishes to enumerate all properties */
1307 case ENUM_PROPS_HOST:
1308 LogFlowFunc(("ENUM_PROPS\n"));
1309 rc = enumProps(cParms, paParms);
1310 break;
1311
1312 /* The host wishes to set global flags for the service */
1313 case SET_GLOBAL_FLAGS_HOST:
1314 LogFlowFunc(("SET_GLOBAL_FLAGS_HOST\n"));
1315 if (cParms == 1)
1316 {
1317 uint32_t eFlags;
1318 rc = paParms[0].getUInt32(&eFlags);
1319 if (RT_SUCCESS(rc))
1320 meGlobalFlags = (ePropFlags)eFlags;
1321 }
1322 else
1323 rc = VERR_INVALID_PARAMETER;
1324 break;
1325
1326 /* The host wishes to flush all pending notification */
1327 case FLUSH_NOTIFICATIONS_HOST:
1328 LogFlowFunc(("FLUSH_NOTIFICATIONS_HOST\n"));
1329 if (cParms == 1)
1330 {
1331 uint32_t cMsTimeout;
1332 rc = paParms[0].getUInt32(&cMsTimeout);
1333 if (RT_SUCCESS(rc))
1334 rc = flushNotifications(cMsTimeout);
1335 }
1336 else
1337 rc = VERR_INVALID_PARAMETER;
1338 break;
1339
1340 default:
1341 rc = VERR_NOT_SUPPORTED;
1342 break;
1343 }
1344 }
1345 catch (std::bad_alloc)
1346 {
1347 rc = VERR_NO_MEMORY;
1348 }
1349
1350 LogFlowFunc(("rc = %Rrc\n", rc));
1351 return rc;
1352}
1353
1354int Service::uninit()
1355{
1356 int rc = VINF_SUCCESS;
1357
1358 ASMAtomicWriteBool(&mfExitThread, true);
1359
1360#ifndef VBOX_GUEST_PROP_TEST_NOTHREAD
1361 /*
1362 * Send a dummy request to the thread so it is forced out of the loop and
1363 * notice that the exit flag is set. Give up waiting after 5 mins.
1364 * We call flushNotifications first to try clean up any pending request.
1365 */
1366 flushNotifications(120*1000);
1367
1368 rc = RTReqCallEx(mReqQueue, NULL, 0, RTREQFLAGS_NO_WAIT, (PFNRT)reqVoid, 0);
1369 if (RT_SUCCESS(rc))
1370 {
1371 unsigned count = 0;
1372 do
1373 {
1374 rc = RTThreadWait(mReqThread, 1000, NULL);
1375 ++count;
1376 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
1377 } while ((VERR_TIMEOUT == rc) && (count < 300));
1378 }
1379#endif /* VBOX_GUEST_PROP_TEST_NOTHREAD not defined */
1380 if (RT_SUCCESS(rc))
1381 RTReqDestroyQueue(mReqQueue);
1382 return rc;
1383}
1384
1385} /* namespace guestProp */
1386
1387using guestProp::Service;
1388
1389/**
1390 * @copydoc VBOXHGCMSVCLOAD
1391 */
1392extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1393{
1394 int rc = VINF_SUCCESS;
1395
1396 LogFlowFunc(("ptable = %p\n", ptable));
1397
1398 if (!VALID_PTR(ptable))
1399 {
1400 rc = VERR_INVALID_PARAMETER;
1401 }
1402 else
1403 {
1404 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1405
1406 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1407 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1408 {
1409 rc = VERR_VERSION_MISMATCH;
1410 }
1411 else
1412 {
1413 std::auto_ptr<Service> apService;
1414 /* No exceptions may propogate outside. */
1415 try {
1416 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1417 } catch (int rcThrown) {
1418 rc = rcThrown;
1419 } catch (...) {
1420 rc = VERR_UNRESOLVED_ERROR;
1421 }
1422
1423 if (RT_SUCCESS(rc))
1424 {
1425 /* We do not maintain connections, so no client data is needed. */
1426 ptable->cbClient = 0;
1427
1428 ptable->pfnUnload = Service::svcUnload;
1429 ptable->pfnConnect = Service::svcConnectDisconnect;
1430 ptable->pfnDisconnect = Service::svcConnectDisconnect;
1431 ptable->pfnCall = Service::svcCall;
1432 ptable->pfnHostCall = Service::svcHostCall;
1433 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1434 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1435 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1436
1437 /* Service specific initialization. */
1438 ptable->pvService = apService.release();
1439 }
1440 }
1441 }
1442
1443 LogFlowFunc(("returning %Rrc\n", rc));
1444 return rc;
1445}
1446
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