VirtualBox

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

Last change on this file since 53654 was 44529, checked in by vboxsync, 12 years ago

header (C) fixes

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