VirtualBox

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

Last change on this file since 39228 was 39228, checked in by vboxsync, 13 years ago

GuestProperties: Added code for using RTStrSpace instead of std::list as property container - it's faster.

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