VirtualBox

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

Last change on this file since 30084 was 29394, checked in by vboxsync, 15 years ago

GuestProperties: Try make sure the timestamp is somewhat unique because the code is making assumptions about this (RTTimeNow granularity is very coarse on windows). Fixed several derefernces of rend in getOldNotificationInternal that VC++'s strictness checks pointed out. Also cleaned up the method, dropping the unncessary and somewhat historical/puzzling RT_SUCCESS(rc) bits.

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