VirtualBox

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

Last change on this file since 57358 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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