VirtualBox

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

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

HostServices/GuestProperties: add global "RDONLYGUEST" flag and clean up the test case

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette