VirtualBox

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

Last change on this file since 13941 was 13922, checked in by vboxsync, 16 years ago

HostServices/GuestProperties: try to fix a burn that works here

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