VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestControl/service.cpp@ 44863

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

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.6 KB
Line 
1/* $Id: service.cpp 44863 2013-02-28 12:18:17Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2011-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to this service. A client is represented by its unique HGCM client ID.
30 * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
31 * to not only distinguish clients but individual requests. Because
32 * the host does not know anything about connected clients it needs
33 * an indicator which it can refer to later. This context ID gets
34 * internally bound by the service to a client which actually processes
35 * the command in order to have a relationship between client<->context ID(s).
36 *
37 * The host can trigger commands which get buffered by the service (with full HGCM
38 * parameter info). As soon as a client connects (or is ready to do some new work)
39 * it gets a buffered host command to process it. This command then will be immediately
40 * removed from the command list. If there are ready clients but no new commands to be
41 * processed, these clients will be set into a deferred state (that is being blocked
42 * to return until a new command is available).
43 *
44 * If a client needs to inform the host that something happened, it can send a
45 * message to a low level HGCM callback registered in Main. This callback contains
46 * the actual data as well as the context ID to let the host do the next necessary
47 * steps for this context. This context ID makes it possible to wait for an event
48 * inside the host's Main API function (like starting a process on the guest and
49 * wait for getting its PID returned by the client) as well as cancelling blocking
50 * host calls in order the client terminated/crashed (HGCM detects disconnected
51 * clients and reports it to this service's callback).
52 *
53 * Starting at VBox 4.2 the context ID itself consists of a session ID, an object
54 * ID (for example a process or file ID) and a count. This is necessary to not break
55 * compatibility between older hosts and to manage guest session on the host.
56 */
57
58/*******************************************************************************
59* Header Files *
60*******************************************************************************/
61#define LOG_GROUP LOG_GROUP_HGCM
62#include <VBox/HostServices/GuestControlSvc.h>
63
64#include <VBox/log.h>
65#include <iprt/assert.h>
66#include <iprt/cpp/autores.h>
67#include <iprt/cpp/utils.h>
68#include <iprt/err.h>
69#include <iprt/mem.h>
70#include <iprt/list.h>
71#include <iprt/req.h>
72#include <iprt/string.h>
73#include <iprt/thread.h>
74#include <iprt/time.h>
75
76#include <map>
77#include <memory> /* for auto_ptr */
78#include <string>
79#include <list>
80
81#include "gctrl.h"
82
83namespace guestControl {
84
85/** Flag for indicating that the client only is interested in
86 * messages for specific contexts. */
87#define CLIENTSTATE_FLAG_CONTEXTFILTER RT_BIT(0)
88
89/**
90 * Structure for maintaining a pending (that is, a deferred and not yet completed)
91 * client command.
92 */
93typedef struct ClientConnection
94{
95 /** The call handle */
96 VBOXHGCMCALLHANDLE mHandle;
97 /** Number of parameters */
98 uint32_t mNumParms;
99 /** The call parameters */
100 VBOXHGCMSVCPARM *mParms;
101 /** The standard constructor. */
102 ClientConnection(void) : mHandle(0), mNumParms(0), mParms(NULL) {}
103} ClientConnection;
104
105/**
106 * Structure for holding a buffered host command which has
107 * not been processed yet.
108 */
109typedef struct HostCommand
110{
111 RTLISTNODE Node;
112
113 uint32_t AddRef(void)
114 {
115 return ++mRefCount;
116 }
117
118 uint32_t Release(void)
119 {
120 LogFlowFunc(("Releasing CID=%RU32, refCount=%RU32\n",
121 mContextID, mRefCount));
122
123 /* Release reference for current command. */
124 Assert(mRefCount);
125 if (--mRefCount == 0)
126 Free();
127
128 return mRefCount;
129 }
130
131 /**
132 * Allocates the command with an HGCM request. Needs to be free'd using Free().
133 *
134 * @return IPRT status code.
135 * @param uMsg Message type.
136 * @param cParms Number of parameters of HGCM request.
137 * @param paParms Array of parameters of HGCM request.
138 */
139 int Allocate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
140 {
141 LogFlowFunc(("Allocating uMsg=%RU32, cParms=%RU32, paParms=%p\n",
142 uMsg, cParms, paParms));
143
144 if (!cParms) /* At least one parameter (context ID) must be present. */
145 return VERR_INVALID_PARAMETER;
146
147 AssertPtrReturn(paParms, VERR_INVALID_POINTER);
148
149 /* Paranoia. */
150 if (cParms > 256)
151 cParms = 256;
152
153 int rc = VINF_SUCCESS;
154
155 /*
156 * Don't verify anything here (yet), because this function only buffers
157 * the HGCM data into an internal structure and reaches it back to the guest (client)
158 * in an unmodified state.
159 */
160 mMsgType = uMsg;
161 mParmCount = cParms;
162 if (mParmCount)
163 {
164 mpParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount);
165 if (NULL == mpParms)
166 rc = VERR_NO_MEMORY;
167 }
168
169 if (RT_SUCCESS(rc))
170 {
171 for (uint32_t i = 0; i < mParmCount; i++)
172 {
173 mpParms[i].type = paParms[i].type;
174 switch (paParms[i].type)
175 {
176 case VBOX_HGCM_SVC_PARM_32BIT:
177 mpParms[i].u.uint32 = paParms[i].u.uint32;
178 break;
179
180 case VBOX_HGCM_SVC_PARM_64BIT:
181 /* Not supported yet. */
182 break;
183
184 case VBOX_HGCM_SVC_PARM_PTR:
185 mpParms[i].u.pointer.size = paParms[i].u.pointer.size;
186 if (mpParms[i].u.pointer.size > 0)
187 {
188 mpParms[i].u.pointer.addr = RTMemAlloc(mpParms[i].u.pointer.size);
189 if (NULL == mpParms[i].u.pointer.addr)
190 {
191 rc = VERR_NO_MEMORY;
192 break;
193 }
194 else
195 memcpy(mpParms[i].u.pointer.addr,
196 paParms[i].u.pointer.addr,
197 mpParms[i].u.pointer.size);
198 }
199 else
200 {
201 /* Size is 0 -- make sure we don't have any pointer. */
202 mpParms[i].u.pointer.addr = NULL;
203 }
204 break;
205
206 default:
207 break;
208 }
209 if (RT_FAILURE(rc))
210 break;
211 }
212 }
213
214 if (RT_SUCCESS(rc))
215 {
216 /*
217 * Assume that the context ID *always* is the first parameter,
218 * assign the context ID to the command.
219 */
220 rc = mpParms[0].getUInt32(&mContextID);
221 }
222
223 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
224 return rc;
225 }
226
227 /**
228 * Frees the buffered HGCM request.
229 *
230 * @return IPRT status code.
231 */
232 void Free(void)
233 {
234 AssertMsg(mRefCount == 0, ("Command still being used by a client (%RU32 refs), cannot free yet\n",
235 mRefCount));
236
237 LogFlowFunc(("Freeing host command CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
238 mContextID, mMsgType, mParmCount, mpParms));
239
240 for (uint32_t i = 0; i < mParmCount; i++)
241 {
242 switch (mpParms[i].type)
243 {
244 case VBOX_HGCM_SVC_PARM_PTR:
245 if (mpParms[i].u.pointer.size > 0)
246 RTMemFree(mpParms[i].u.pointer.addr);
247 break;
248 }
249 }
250
251 if (mpParms)
252 RTMemFree(mpParms);
253
254 mParmCount = 0;
255 }
256
257 /**
258 * Copies data from the buffered HGCM request to the current HGCM request.
259 *
260 * @return IPRT status code.
261 * @param paDstParms Array of parameters of HGCM request to fill the data into.
262 * @param cPDstarms Number of parameters the HGCM request can handle.
263 * @param pSrcBuf Parameter buffer to assign.
264 */
265 int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
266 {
267 int rc = VINF_SUCCESS;
268 if (cDstParms != mParmCount)
269 {
270 LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
271 cDstParms, mParmCount));
272 rc = VERR_INVALID_PARAMETER;
273 }
274 else
275 {
276 for (uint32_t i = 0; i < mParmCount; i++)
277 {
278 if (paDstParms[i].type != mpParms[i].type)
279 {
280 LogFlowFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n",
281 i, paDstParms[i].type, mpParms[i].type));
282 rc = VERR_INVALID_PARAMETER;
283 }
284 else
285 {
286 switch (mpParms[i].type)
287 {
288 case VBOX_HGCM_SVC_PARM_32BIT:
289 paDstParms[i].u.uint32 = mpParms[i].u.uint32;
290 break;
291
292 case VBOX_HGCM_SVC_PARM_PTR:
293 {
294 if (!mpParms[i].u.pointer.size)
295 continue; /* Only copy buffer if there actually is something to copy. */
296
297 if (!paDstParms[i].u.pointer.addr)
298 rc = VERR_INVALID_PARAMETER;
299
300 if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
301 rc = VERR_BUFFER_OVERFLOW;
302
303 if (RT_SUCCESS(rc))
304 {
305 memcpy(paDstParms[i].u.pointer.addr,
306 mpParms[i].u.pointer.addr,
307 mpParms[i].u.pointer.size);
308 }
309
310 break;
311 }
312
313 case VBOX_HGCM_SVC_PARM_64BIT:
314 /* Fall through is intentional. */
315 default:
316 LogFlowFunc(("Parameter %RU32 of type %RU32 is not supported yet\n",
317 i, mpParms[i].type));
318 rc = VERR_NOT_SUPPORTED;
319 break;
320 }
321 }
322
323 if (RT_FAILURE(rc))
324 {
325 LogFlowFunc(("Parameter %RU32 invalid (rc=%Rrc), refusing\n",
326 i, rc));
327 break;
328 }
329 }
330 }
331
332 return rc;
333 }
334
335 int AssignToConnection(const ClientConnection *pConnection)
336 {
337 int rc;
338
339 LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
340 mMsgType, mParmCount, mpParms));
341
342 /* Does the current host command need more parameter space which
343 * the client does not provide yet? */
344 if (mParmCount > pConnection->mNumParms)
345 {
346 pConnection->mParms[0].setUInt32(mMsgType); /* Message ID */
347 pConnection->mParms[1].setUInt32(mParmCount); /* Required parameters for message */
348
349 /*
350 * So this call apparently failed because the guest wanted to peek
351 * how much parameters it has to supply in order to successfully retrieve
352 * this command. Let's tell him so!
353 */
354 rc = VERR_TOO_MUCH_DATA;
355 }
356 else
357 {
358 rc = CopyTo(pConnection->mParms, pConnection->mNumParms);
359
360 /* Has there been enough parameter space but the wrong parameter types
361 * were submitted -- maybe the client was just asking for the next upcoming
362 * host message?
363 *
364 * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
365 * in every case. */
366 if (RT_FAILURE(rc))
367 rc = VERR_TOO_MUCH_DATA;
368 }
369
370 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
371 return rc;
372 }
373
374 /** Reference count for keeping track how many connected
375 * clients still need to process this command until it can
376 * be removed. */
377 uint32_t mRefCount;
378 /** The context ID this command belongs to. Will be extracted
379 * *always* from HGCM parameter [0]. */
380 uint32_t mContextID;
381 /** Dynamic structure for holding the HGCM parms */
382 uint32_t mMsgType;
383 uint32_t mParmCount;
384 PVBOXHGCMSVCPARM mpParms;
385} HostCommand;
386
387/**
388 * Per-client structure used for book keeping/state tracking a
389 * certain host command.
390 */
391typedef struct ClientContext
392{
393 /* Pointer to list node of this command. */
394 HostCommand *mpHostCmd;
395 /** The standard constructor. */
396 ClientContext(void) : mpHostCmd(NULL) {}
397 /** Internal constrcutor. */
398 ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {}
399} ClientContext;
400typedef std::map< uint32_t, ClientContext > ClientContextMap;
401typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter;
402typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst;
403
404/**
405 * Structure for holding all clients with their
406 * generated host contexts. This is necessary for
407 * maintaining the relationship between a client and its context ID(s).
408 */
409typedef struct ClientState
410{
411 ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers)
412 : mSvcHelpers(pSvcHelpers), mpHostCmd(NULL),
413 mFlags(0), mContextFilter(0), mIsPending(false),
414 mHostCmdTries(0), mHostCmdRc(VINF_SUCCESS) {}
415
416 ClientState(void)
417 : mSvcHelpers(NULL), mpHostCmd(NULL),
418 mFlags(0), mContextFilter(0), mIsPending(false),
419 mHostCmdTries(0), mHostCmdRc(VINF_SUCCESS) {}
420
421 bool WantsHostCommand(const HostCommand *pHostCmd) const
422 {
423 AssertPtrReturn(pHostCmd, false);
424
425 /*
426 * If a sesseion filter is set, only obey those sessions we're interested in.
427 */
428 if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER)
429 {
430 if (VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->mContextID) == mContextFilter)
431 return true;
432 }
433 else /* Client is interested in all commands. */
434 return true;
435
436 return false;
437 }
438
439 int SetPending(const ClientConnection *pConnection)
440 {
441 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
442
443 if (mpHostCmd == NULL)
444 {
445 AssertMsg(mIsPending == false,
446 ("Client %p already is pending but tried to receive a new host command\n", this));
447
448 mPending.mHandle = pConnection->mHandle;
449 mPending.mNumParms = pConnection->mNumParms;
450 mPending.mParms = pConnection->mParms;
451
452 mIsPending = true;
453
454 LogFlowFunc(("Client now is in pending mode\n"));
455
456 /*
457 * Signal that we don't and can't return yet.
458 */
459 return VINF_HGCM_ASYNC_EXECUTE;
460 }
461
462 /*
463 * Signal that there already is a connection pending.
464 * Shouldn't happen in daily usage.
465 */
466 AssertMsgFailed(("Client already has a connection pending\n"));
467 return VERR_SIGNAL_PENDING;
468 }
469
470 int Run(const ClientConnection *pConnection,
471 const RTLISTANCHOR *pHostCmdList)
472 {
473 int rc = VINF_SUCCESS;
474
475 LogFlowFunc(("Client pConnection=%p, pHostCmdList=%p\n",
476 pConnection, pHostCmdList));
477 LogFlowFunc(("Client hostCmd=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32\n",
478 mpHostCmd, mHostCmdRc, mHostCmdTries));
479
480 /* Do we have a current command? */
481 if (mpHostCmd)
482 {
483 bool fRemove = false;
484 if (RT_FAILURE(mHostCmdRc))
485 {
486 mHostCmdTries++;
487
488 /*
489 * If the client understood the message but supplied too little buffer space
490 * don't send this message again and drop it after 3 unsuccessful attempts.
491 * The host then should take care of next actions (maybe retry it with a smaller buffer).
492 */
493 if ( mHostCmdRc == VERR_TOO_MUCH_DATA
494 && mHostCmdTries >= 3)
495 {
496 fRemove = true;
497 }
498 /* Client did not understand the message or something else weird happened. Try again one
499 * more time and drop it if it didn't get handled then. */
500 else if (mHostCmdTries > 1)
501 fRemove = true;
502 }
503 else
504 fRemove = true; /* Everything went fine, remove it. */
505
506 LogFlowFunc(("Client tried CID=%RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
507 mpHostCmd->mContextID, mHostCmdTries, mHostCmdRc, fRemove));
508
509 if (fRemove)
510 {
511 LogFlowFunc(("Client removes itself from command CID=%RU32\n",
512 mpHostCmd->mContextID));
513
514 /* Remove command from context map. */
515 mContextMap.erase(mpHostCmd->mContextID);
516
517 /* Release reference for current command. */
518 if (mpHostCmd->Release() == 0)
519 {
520 LogFlowFunc(("Destroying and removing command CID=%RU32 from list\n",
521 mpHostCmd->mContextID));
522
523 /* Last reference removes the command from the list. */
524 RTListNodeRemove(&mpHostCmd->Node);
525
526 RTMemFree(mpHostCmd);
527 mpHostCmd = NULL;
528 }
529
530 /* Reset everything else. */
531 mHostCmdRc = VINF_SUCCESS;
532 mHostCmdTries = 0;
533 }
534 }
535
536 /* ... or don't we have one (anymore?) Try getting a new one to process now. */
537 if (mpHostCmd == NULL)
538 {
539 /* Get the next host command the clienet is interested in. */
540 bool fFoundCmd = false;
541 HostCommand *pCurCmd;
542 RTListForEach(pHostCmdList, pCurCmd, HostCommand, Node)
543 {
544 fFoundCmd = WantsHostCommand(pCurCmd);
545 if (fFoundCmd)
546 {
547 mpHostCmd = pCurCmd;
548 AssertPtr(mpHostCmd);
549 mpHostCmd->AddRef();
550
551 /* Create a command context to keep track of client-specific
552 * information about a certain command. */
553 Assert(mContextMap.find(mpHostCmd->mContextID) == mContextMap.end());
554 mContextMap[mpHostCmd->mContextID] = ClientContext(mpHostCmd);
555 /** @todo Exception handling! */
556
557 LogFlowFunc(("Assigning next host comamnd CID=%RU32, cmdType=%RU32, cmdParms=%RU32, new refCount=%RU32\n",
558 mpHostCmd->mContextID, mpHostCmd->mMsgType, mpHostCmd->mParmCount, mpHostCmd->mRefCount));
559 break;
560 }
561 }
562
563 LogFlowFunc(("Client %s new command\n",
564 fFoundCmd ? "found" : "did not find a"));
565
566 /* If no new command was found, set client into pending state. */
567 if (!fFoundCmd)
568 rc = SetPending(pConnection);
569 }
570
571 if (mpHostCmd)
572 {
573 AssertPtr(mpHostCmd);
574 mHostCmdRc = SendReply(pConnection, mpHostCmd);
575 if (RT_FAILURE(mHostCmdRc))
576 {
577 LogFlowFunc(("Processing command CID=%RU32 ended with rc=%Rrc\n",
578 mpHostCmd->mContextID, mHostCmdRc));
579 }
580
581 if (RT_SUCCESS(rc))
582 rc = mHostCmdRc;
583 }
584
585 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
586 return rc;
587 }
588
589 int RunNow(const ClientConnection *pConnection,
590 const PRTLISTANCHOR pHostCmdList)
591 {
592 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
593 AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
594
595 AssertMsgReturn(!mIsPending, ("Can't use another connection when client still is in pending mode\n"),
596 VERR_INVALID_PARAMETER);
597
598 int rc = Run(pConnection, pHostCmdList);
599
600 LogFlowFunc(("Returned with rc=%Rrc\n"));
601 return rc;
602 }
603
604 int Wakeup(const PRTLISTANCHOR pHostCmdList)
605 {
606 AssertMsgReturn(mIsPending, ("Cannot wake up a client which is not in pending mode\n"),
607 VERR_INVALID_PARAMETER);
608
609 int rc = Run(&mPending, pHostCmdList);
610
611 /* Reset pending state. */
612 mIsPending = false;
613
614 LogFlowFunc(("Returned with rc=%Rrc\n"));
615 return rc;
616 }
617
618 int CancelWaiting(int rcPending)
619 {
620 LogFlowFunc(("Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
621 rcPending, mIsPending, mPending.mNumParms, mFlags));
622
623 if ( mIsPending
624 && mPending.mNumParms >= 2)
625 {
626 mPending.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
627 mPending.mParms[1].setUInt32(0); /* Required parameters for message. */
628
629 AssertPtr(mSvcHelpers);
630 mSvcHelpers->pfnCallComplete(mPending.mHandle, rcPending);
631
632 mIsPending = false;
633 }
634
635 return VINF_SUCCESS;
636 }
637
638 int SendReply(const ClientConnection *pConnection,
639 HostCommand *pHostCmd)
640 {
641 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
642 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
643
644 /* Try assigning the host command to the client and store the
645 * result code for later use. */
646 int rc = pHostCmd->AssignToConnection(pConnection);
647
648 /* In any case the client did something, so wake up and remove from list. */
649 AssertPtr(mSvcHelpers);
650 mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
651
652 LogFlowFunc(("pConnection=%p, pHostCmd=%p, rc=%Rrc\n",
653 pConnection, pHostCmd, rc));
654 return rc;
655 }
656
657 PVBOXHGCMSVCHELPERS mSvcHelpers;
658 /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */
659 uint32_t mFlags;
660 /** The context ID filter, based on the flags set. */
661 uint32_t mContextFilter;
662 /** Map containing all context IDs a client is assigned to. */
663 //std::map< uint32_t, ClientContext > mContextMap;
664 /** Pointer to current host command to process. */
665 HostCommand *mpHostCmd;
666 /** Last (most recent) rc after handling the
667 * host command. */
668 int mHostCmdRc;
669 /** How many times the host service has tried to deliver this
670 * command to the according client. */
671 uint32_t mHostCmdTries;
672 /** Map containing all context IDs a client is assigned to. */
673 ClientContextMap mContextMap;
674 /** Flag indicating whether the client currently is pending. */
675 bool mIsPending;
676 /** The client's pending connection. */
677 ClientConnection mPending;
678} ClientState;
679typedef std::map< uint32_t, ClientState > ClientStateMap;
680typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter;
681typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst;
682
683/**
684 * Class containing the shared information service functionality.
685 */
686class Service : public RTCNonCopyable
687{
688
689private:
690
691 /** Type definition for use in callback functions. */
692 typedef Service SELF;
693 /** HGCM helper functions. */
694 PVBOXHGCMSVCHELPERS mpHelpers;
695 /**
696 * Callback function supplied by the host for notification of updates
697 * to properties.
698 */
699 PFNHGCMSVCEXT mpfnHostCallback;
700 /** User data pointer to be supplied to the host callback function. */
701 void *mpvHostData;
702 /** List containing all buffered host commands. */
703 RTLISTANCHOR mHostCmdList;
704 /** Map containing all connected clients. The primary key contains
705 * the HGCM client ID to identify the client. */
706 ClientStateMap mClientStateMap;
707public:
708 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
709 : mpHelpers(pHelpers)
710 , mpfnHostCallback(NULL)
711 , mpvHostData(NULL)
712 {
713 RTListInit(&mHostCmdList);
714 }
715
716 /**
717 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
718 * Simply deletes the service object
719 */
720 static DECLCALLBACK(int) svcUnload (void *pvService)
721 {
722 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
723 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
724 int rc = pSelf->uninit();
725 AssertRC(rc);
726 if (RT_SUCCESS(rc))
727 delete pSelf;
728 return rc;
729 }
730
731 /**
732 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
733 * Stub implementation of pfnConnect and pfnDisconnect.
734 */
735 static DECLCALLBACK(int) svcConnect (void *pvService,
736 uint32_t u32ClientID,
737 void *pvClient)
738 {
739 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
740 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
741 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
742 return pSelf->clientConnect(u32ClientID, pvClient);
743 }
744
745 /**
746 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
747 * Stub implementation of pfnConnect and pfnDisconnect.
748 */
749 static DECLCALLBACK(int) svcDisconnect (void *pvService,
750 uint32_t u32ClientID,
751 void *pvClient)
752 {
753 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
754 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
755 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
756 return pSelf->clientDisconnect(u32ClientID, pvClient);
757 }
758
759 /**
760 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
761 * Wraps to the call member function
762 */
763 static DECLCALLBACK(void) svcCall (void * pvService,
764 VBOXHGCMCALLHANDLE callHandle,
765 uint32_t u32ClientID,
766 void *pvClient,
767 uint32_t u32Function,
768 uint32_t cParms,
769 VBOXHGCMSVCPARM paParms[])
770 {
771 AssertLogRelReturnVoid(VALID_PTR(pvService));
772 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n",
773 pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
774 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
775 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
776 }
777
778 /**
779 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
780 * Wraps to the hostCall member function
781 */
782 static DECLCALLBACK(int) svcHostCall (void *pvService,
783 uint32_t u32Function,
784 uint32_t cParms,
785 VBOXHGCMSVCPARM paParms[])
786 {
787 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
788 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
789 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
790 return pSelf->hostCall(u32Function, cParms, paParms);
791 }
792
793 /**
794 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
795 * Installs a host callback for notifications of property changes.
796 */
797 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
798 PFNHGCMSVCEXT pfnExtension,
799 void *pvExtension)
800 {
801 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
802 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
803 pSelf->mpfnHostCallback = pfnExtension;
804 pSelf->mpvHostData = pvExtension;
805 return VINF_SUCCESS;
806 }
807
808private:
809
810 int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
811 int clientConnect(uint32_t u32ClientID, void *pvClient);
812 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
813 int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
814 int clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
815 int cancelHostCmd(uint32_t u32ContextID);
816 int cancelPendingWaits(uint32_t u32ClientID, int rcPending);
817 int hostCallback(VBOXHGCMCALLHANDLE callHandle, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
818 int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
819 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
820 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
821 int uninit(void);
822};
823
824/**
825 * Handles a client which just connected.
826 *
827 * @return IPRT status code.
828 * @param u32ClientID
829 * @param pvClient
830 */
831int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
832{
833 LogFlowFunc(("New client with ID=%RU32 connected\n", u32ClientID));
834#ifdef VBOX_STRICT
835 ClientStateMapIterConst it = mClientStateMap.find(u32ClientID);
836 if (it != mClientStateMap.end())
837 {
838 AssertMsgFailed(("Client with ID=%RU32 already connected when it should not\n",
839 u32ClientID));
840 return VERR_ALREADY_EXISTS;
841 }
842#endif
843 ClientState cs(mpHelpers);
844 mClientStateMap[u32ClientID] = cs;
845 /** @todo Exception handling! */
846 return VINF_SUCCESS;
847}
848
849/**
850 * Handles a client which disconnected. This functiond does some
851 * internal cleanup as well as sends notifications to the host so
852 * that the host can do the same (if required).
853 *
854 * @return IPRT status code.
855 * @param u32ClientID The client's ID of which disconnected.
856 * @param pvClient User data, not used at the moment.
857 */
858int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
859{
860 LogFlowFunc(("Client with ID=%RU32 (%zu clients total) disconnected\n",
861 u32ClientID, mClientStateMap.size()));
862
863 /* If this was the last connected (guest) client we need to
864 * unblock all eventually queued up (waiting) host calls. */
865 bool fAllClientsDisconnected = mClientStateMap.size() == 0;
866 if (fAllClientsDisconnected)
867 LogFlowFunc(("No connected clients left, notifying all queued up host callbacks\n"));
868
869 /*
870 * Throw out all stale clients.
871 */
872 int rc = VINF_SUCCESS;
873
874 ClientStateMapIter itClientState = mClientStateMap.begin();
875 while ( itClientState != mClientStateMap.end()
876 && RT_SUCCESS(rc))
877 {
878 /*
879 * Unblock/call back all queued items of the specified client
880 * or for all items in case there is no waiting client around
881 * anymore.
882 */
883 if ( itClientState->first == u32ClientID
884 || fAllClientsDisconnected)
885 {
886 LogFlowFunc(("Cancelling %RU32 context of client ID=%RU32\n",
887 itClientState->second.mContextMap.size(), u32ClientID));
888
889 ClientContextMapIter itContext = itClientState->second.mContextMap.begin();
890 while (itContext != itClientState->second.mContextMap.end())
891 {
892 uint32_t uContextID = itContext->first;
893
894 /*
895 * Notify the host that clients with u32ClientID are no longer
896 * around and need to be cleaned up (canceling waits etc).
897 */
898 LogFlowFunc(("Notifying CID=%RU32 of disconnect ...\n", uContextID));
899 int rc2 = cancelHostCmd(uContextID);
900 if (RT_FAILURE(rc))
901 {
902 LogFlowFunc(("Cancelling assigned client CID=%RU32 failed with rc=%Rrc\n",
903 uContextID, rc2));
904 /* Keep going. */
905 }
906
907 AssertPtr(itContext->second.mpHostCmd);
908 itContext->second.mpHostCmd->Release();
909
910 LogFlowFunc(("Released host command CID=%RU32 returned with rc=%Rrc\n",
911 uContextID, rc2));
912
913 itContext++;
914 }
915 itClientState = mClientStateMap.erase(itClientState);
916 }
917 else
918 itClientState++;
919 }
920
921 if (fAllClientsDisconnected)
922 {
923 /*
924 * If all clients disconnected we also need to make sure that all buffered
925 * host commands need to be notified, because Main is waiting a notification
926 * via a (multi stage) progress object.
927 */
928 HostCommand *pCurCmd = RTListGetFirst(&mHostCmdList, HostCommand, Node);
929 while (pCurCmd)
930 {
931 HostCommand *pNext = RTListNodeGetNext(&pCurCmd->Node, HostCommand, Node);
932 bool fLast = RTListNodeIsLast(&mHostCmdList, &pCurCmd->Node);
933
934 int rc2 = cancelHostCmd(pCurCmd->mContextID);
935 if (RT_FAILURE(rc2))
936 {
937 LogFlowFunc(("Cancelling host command with CID=%u (refCount=%RU32) failed with rc=%Rrc\n",
938 pCurCmd->mContextID, pCurCmd->mRefCount, rc2));
939 /* Keep going. */
940 }
941
942 pCurCmd->Free();
943
944 RTListNodeRemove(&pCurCmd->Node);
945 RTMemFree(pCurCmd);
946
947 if (fLast)
948 break;
949
950 pCurCmd = pNext;
951 }
952
953 Assert(RTListIsEmpty(&mHostCmdList));
954 }
955
956 return rc;
957}
958
959/**
960 * Either fills in parameters from a pending host command into our guest context or
961 * defer the guest call until we have something from the host.
962 *
963 * @return IPRT status code.
964 * @param u32ClientID The client's ID.
965 * @param callHandle The client's call handle.
966 * @param cParms Number of parameters.
967 * @param paParms Array of parameters.
968 */
969int Service::clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
970 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
971{
972 /*
973 * Lookup client in our list so that we can assign the context ID of
974 * a command to that client.
975 */
976 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
977 AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
978 u32ClientID));
979 if (itClientState == mClientStateMap.end())
980 return VERR_NOT_FOUND; /* Should never happen. */
981
982 ClientState &clientState = itClientState->second;
983
984 /* Use the current (inbound) connection. */
985 ClientConnection thisCon;
986 thisCon.mHandle = callHandle;
987 thisCon.mNumParms = cParms;
988 thisCon.mParms = paParms;
989
990 /*
991 * If host command list is empty (nothing to do right now) just
992 * defer the call until we got something to do (makes the client
993 * wait).
994 */
995 int rc;
996 if (RTListIsEmpty(&mHostCmdList))
997 {
998 rc = clientState.SetPending(&thisCon);
999 }
1000 else
1001 {
1002 rc = clientState.RunNow(&thisCon, &mHostCmdList);
1003 }
1004
1005 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1006 return rc;
1007}
1008
1009int Service::clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
1010 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1011{
1012 /*
1013 * Lookup client in our list so that we can assign the context ID of
1014 * a command to that client.
1015 */
1016 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1017 AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
1018 u32ClientID));
1019 if (itClientState == mClientStateMap.end())
1020 return VERR_NOT_FOUND; /* Should never happen. */
1021
1022 if (cParms != 2)
1023 return VERR_INVALID_PARAMETER;
1024
1025 uint32_t uMaskAdd;
1026 int rc = paParms[0].getUInt32(&uMaskAdd);
1027 if (RT_SUCCESS(rc))
1028 {
1029 /* paParms[1] unused yet. */
1030
1031 ClientState &clientState = itClientState->second;
1032
1033 clientState.mFlags |= CLIENTSTATE_FLAG_CONTEXTFILTER;
1034 clientState.mContextFilter = uMaskAdd;
1035
1036 LogFlowFunc(("Client ID=%RU32 now has filter=%x enabled (flags=%x)\n",
1037 u32ClientID, clientState.mContextFilter, clientState.mFlags));
1038 }
1039
1040 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1041 return rc;
1042}
1043
1044/**
1045 * Cancels a buffered host command to unblock waiting on Main side
1046 * via callbacks.
1047 *
1048 * @return IPRT status code.
1049 * @param u32ContextID Context ID of host command to cancel.
1050 */
1051int Service::cancelHostCmd(uint32_t u32ContextID)
1052{
1053 Assert(mpfnHostCallback);
1054
1055 LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID));
1056
1057 CALLBACKDATA_CLIENT_DISCONNECTED data;
1058 data.hdr.uContextID = u32ContextID;
1059
1060 AssertPtr(mpfnHostCallback);
1061 AssertPtr(mpvHostData);
1062
1063 return mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data));
1064}
1065
1066/**
1067 * Client asks itself (in another thread) to cancel all pending waits which are blocking the client
1068 * from shutting down / doing something else.
1069 *
1070 * @return IPRT status code.
1071 * @param u32ClientID The client's ID.
1072 * @param rcPending Result code for completing pending operation.
1073 */
1074int Service::cancelPendingWaits(uint32_t u32ClientID, int rcPending)
1075{
1076 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1077 if (itClientState != mClientStateMap.end())
1078 return itClientState->second.CancelWaiting(rcPending);
1079
1080 return VINF_SUCCESS;
1081}
1082
1083/**
1084 * Notifies the host (using low-level HGCM callbacks) about an event
1085 * which was sent from the client.
1086 *
1087 * @return IPRT status code.
1088 * @param callHandle Call handle.
1089 * @param eFunction Function (event) that occured.
1090 * @param cParms Number of parameters.
1091 * @param paParms Array of parameters.
1092 */
1093int Service::hostCallback(VBOXHGCMCALLHANDLE callHandle,
1094 uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1095{
1096 LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
1097 eFunction, cParms, paParms));
1098
1099 int rc;
1100 if (mpfnHostCallback)
1101 {
1102 VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
1103 rc = mpfnHostCallback(mpvHostData, eFunction,
1104 (void *)(&data), sizeof(data));
1105 }
1106 else
1107 rc = VERR_NOT_SUPPORTED;
1108
1109 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1110 return rc;
1111}
1112
1113/**
1114 * Processes a command receiveed from the host side and re-routes it to
1115 * a connect client on the guest.
1116 *
1117 * @return IPRT status code.
1118 * @param eFunction Function code to process.
1119 * @param cParms Number of parameters.
1120 * @param paParms Array of parameters.
1121 */
1122int Service::hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1123{
1124 /*
1125 * If no client is connected at all we don't buffer any host commands
1126 * and immediately return an error to the host. This avoids the host
1127 * waiting for a response from the guest side in case VBoxService on
1128 * the guest is not running/system is messed up somehow.
1129 */
1130 if (mClientStateMap.size() == 0)
1131 return VERR_NOT_FOUND;
1132
1133 int rc;
1134 HostCommand *pHostCmd = (HostCommand*)RTMemAllocZ(sizeof(HostCommand));
1135 if (pHostCmd)
1136 {
1137 rc = pHostCmd->Allocate(eFunction, cParms, paParms);
1138 if (RT_SUCCESS(rc))
1139 RTListAppend(&mHostCmdList, &pHostCmd->Node);
1140 }
1141 else
1142 rc = VERR_NO_MEMORY;
1143
1144 if (RT_SUCCESS(rc))
1145 {
1146 LogFlowFunc(("Handling host command CID=%RU32, numClients=%zu\n",
1147 pHostCmd->mContextID, mClientStateMap.size()));
1148
1149 /*
1150 * Wake up all pending clients which are interested in this
1151 * host command.
1152 */
1153#ifdef DEBUG
1154 uint32_t uClientsWokenUp = 0;
1155#endif
1156
1157 ClientStateMapIter itClientState = mClientStateMap.begin();
1158 AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n"));
1159 while (itClientState != mClientStateMap.end())
1160 {
1161 if (itClientState->second.mIsPending) /* Only wake up pending clients. */
1162 {
1163 LogFlowFunc(("Waking up client ID=%RU32 (isPending=%RTbool) ...\n",
1164 itClientState->first, itClientState->second.mIsPending));
1165
1166 ClientState &clientState = itClientState->second;
1167 int rc2 = clientState.Wakeup(&mHostCmdList);
1168 LogFlowFunc(("Client ID=%RU32 wakeup ended with rc=%Rrc\n",
1169 itClientState->first, rc2));
1170#ifdef DEBUG
1171 uClientsWokenUp++;
1172#endif
1173 }
1174 else
1175 LogFlowFunc(("Client ID=%RU32 is not in pending state\n",
1176 itClientState->first));
1177
1178 itClientState++;
1179 }
1180
1181#ifdef DEBUG
1182 LogFlowFunc(("%RU32 clients have been succcessfully woken up\n",
1183 uClientsWokenUp));
1184#endif
1185 }
1186
1187 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1188 return rc;
1189}
1190
1191/**
1192 * Handle an HGCM service call.
1193 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
1194 * @note All functions which do not involve an unreasonable delay will be
1195 * handled synchronously. If needed, we will add a request handler
1196 * thread in future for those which do.
1197 *
1198 * @thread HGCM
1199 */
1200void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
1201 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
1202 VBOXHGCMSVCPARM paParms[])
1203{
1204 int rc = VINF_SUCCESS;
1205 LogFlowFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=0x%p\n",
1206 u32ClientID, eFunction, cParms, paParms));
1207 try
1208 {
1209 /*
1210 * The guest asks the host for the next message to process.
1211 */
1212 if (eFunction == GUEST_MSG_WAIT)
1213 {
1214 LogFlowFunc(("GUEST_MSG_GET\n"));
1215 rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms);
1216 }
1217 else
1218 {
1219 switch (eFunction)
1220 {
1221 /*
1222 * A client wants to shut down and asks us (this service) to cancel
1223 * all blocking/pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
1224 * client can gracefully shut down.
1225 */
1226 case GUEST_CANCEL_PENDING_WAITS:
1227 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
1228 rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */);
1229 break;
1230
1231 /*
1232 * The guest only wants certain messages set by the filter mask(s).
1233 * Since VBox 4.3+.
1234 */
1235 case GUEST_MSG_FILTER:
1236 LogFlowFunc(("GUEST_MSG_FILTER\n"));
1237 rc = clientSetMsgFilter(u32ClientID, callHandle, cParms, paParms);
1238 break;
1239
1240 /*
1241 * For all other regular commands we call our hostCallback
1242 * function. If the current command does not support notifications,
1243 * notifyHost will return VERR_NOT_SUPPORTED.
1244 */
1245 default:
1246 rc = hostCallback(callHandle, eFunction, cParms, paParms);
1247 break;
1248 }
1249
1250 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1251 {
1252 /* Tell the client that the call is complete (unblocks waiting). */
1253 AssertPtr(mpHelpers);
1254 mpHelpers->pfnCallComplete(callHandle, rc);
1255 }
1256 }
1257 }
1258 catch (std::bad_alloc)
1259 {
1260 rc = VERR_NO_MEMORY;
1261 }
1262}
1263
1264/**
1265 * Service call handler for the host.
1266 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
1267 * @thread hgcm
1268 */
1269int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1270{
1271 int rc = VERR_NOT_SUPPORTED;
1272 LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n",
1273 eFunction, cParms, paParms));
1274 try
1275 {
1276 switch (eFunction)
1277 {
1278 /**
1279 * Host
1280 */
1281 case HOST_CANCEL_PENDING_WAITS:
1282 {
1283 LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n"));
1284 ClientStateMapIter itClientState = mClientStateMap.begin();
1285 while (itClientState != mClientStateMap.end())
1286 {
1287 int rc2 = itClientState->second.CancelWaiting(VINF_SUCCESS /* Pending rc. */);
1288 if (RT_FAILURE(rc2))
1289 LogFlowFunc(("Cancelling waiting for client ID=%RU32 failed with rc=%Rrc",
1290 itClientState->first, rc2));
1291 itClientState++;
1292 }
1293 rc = VINF_SUCCESS;
1294 break;
1295 }
1296
1297 default:
1298 rc = hostProcessCommand(eFunction, cParms, paParms);
1299 break;
1300 }
1301 }
1302 catch (std::bad_alloc)
1303 {
1304 rc = VERR_NO_MEMORY;
1305 }
1306
1307 return rc;
1308}
1309
1310int Service::uninit()
1311{
1312 Assert(RTListIsEmpty(&mHostCmdList));
1313
1314 return VINF_SUCCESS;
1315}
1316
1317} /* namespace guestControl */
1318
1319using guestControl::Service;
1320
1321/**
1322 * @copydoc VBOXHGCMSVCLOAD
1323 */
1324extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
1325{
1326 int rc = VINF_SUCCESS;
1327
1328 LogFlowFunc(("ptable = %p\n", ptable));
1329
1330 if (!VALID_PTR(ptable))
1331 {
1332 rc = VERR_INVALID_PARAMETER;
1333 }
1334 else
1335 {
1336 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1337
1338 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1339 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1340 {
1341 rc = VERR_VERSION_MISMATCH;
1342 }
1343 else
1344 {
1345 std::auto_ptr<Service> apService;
1346 /* No exceptions may propagate outside. */
1347 try {
1348 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1349 } catch (int rcThrown) {
1350 rc = rcThrown;
1351 } catch (...) {
1352 rc = VERR_UNRESOLVED_ERROR;
1353 }
1354
1355 if (RT_SUCCESS(rc))
1356 {
1357 /*
1358 * We don't need an additional client data area on the host,
1359 * because we're a class which can have members for that :-).
1360 */
1361 ptable->cbClient = 0;
1362
1363 /* Register functions. */
1364 ptable->pfnUnload = Service::svcUnload;
1365 ptable->pfnConnect = Service::svcConnect;
1366 ptable->pfnDisconnect = Service::svcDisconnect;
1367 ptable->pfnCall = Service::svcCall;
1368 ptable->pfnHostCall = Service::svcHostCall;
1369 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1370 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1371 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1372
1373 /* Service specific initialization. */
1374 ptable->pvService = apService.release();
1375 }
1376 }
1377 }
1378
1379 LogFlowFunc(("returning %Rrc\n", rc));
1380 return rc;
1381}
1382
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