VirtualBox

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

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

GuestProperties: complete a notification request if the client submits exactly the same request.

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

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