VirtualBox

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

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

GuestCtrl: More code for guest session infrastructure handling (untested, work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.5 KB
Line 
1/* $Id: service.cpp 45010 2013-03-12 17:47:56Z 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 CID=%RU32 still being used by a client (%RU32 refs), cannot free yet\n",
235 mContextID, 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 default:
250 break;
251 }
252 }
253
254 if (mpParms)
255 {
256 RTMemFree(mpParms);
257 mpParms = NULL;
258 }
259
260 mParmCount = 0;
261
262 /* Removes the command from its list */
263 RTListNodeRemove(&Node);
264 }
265
266 /**
267 * Copies data from the buffered HGCM request to the current HGCM request.
268 *
269 * @return IPRT status code.
270 * @param paDstParms Array of parameters of HGCM request to fill the data into.
271 * @param cPDstarms Number of parameters the HGCM request can handle.
272 * @param pSrcBuf Parameter buffer to assign.
273 */
274 int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
275 {
276 int rc = VINF_SUCCESS;
277 if (cDstParms != mParmCount)
278 {
279 LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
280 cDstParms, mParmCount));
281 rc = VERR_INVALID_PARAMETER;
282 }
283
284 if (RT_SUCCESS(rc))
285 {
286 for (uint32_t i = 0; i < mParmCount; i++)
287 {
288 if (paDstParms[i].type != mpParms[i].type)
289 {
290 LogFlowFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n",
291 i, paDstParms[i].type, mpParms[i].type));
292 rc = VERR_INVALID_PARAMETER;
293 }
294 else
295 {
296#ifdef DEBUG_andy
297 LogFlowFunc(("\tmpParms[%RU32] type = %RU32\n",
298 i, mpParms[i].type));
299#endif
300 switch (mpParms[i].type)
301 {
302 case VBOX_HGCM_SVC_PARM_32BIT:
303#ifdef DEBUG_andy
304 LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n",
305 i, mpParms[i].u.uint32));
306#endif
307 paDstParms[i].u.uint32 = mpParms[i].u.uint32;
308 break;
309
310 case VBOX_HGCM_SVC_PARM_PTR:
311 {
312#ifdef DEBUG_andy
313 LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n",
314 i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size));
315#endif
316 if (!mpParms[i].u.pointer.size)
317 continue; /* Only copy buffer if there actually is something to copy. */
318
319 if (!paDstParms[i].u.pointer.addr)
320 rc = VERR_INVALID_PARAMETER;
321
322 if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
323 rc = VERR_BUFFER_OVERFLOW;
324
325 if (RT_SUCCESS(rc))
326 {
327 memcpy(paDstParms[i].u.pointer.addr,
328 mpParms[i].u.pointer.addr,
329 mpParms[i].u.pointer.size);
330 }
331
332 break;
333 }
334
335 case VBOX_HGCM_SVC_PARM_64BIT:
336 /* Fall through is intentional. */
337 default:
338 LogFlowFunc(("Parameter %RU32 of type %RU32 is not supported yet\n",
339 i, mpParms[i].type));
340 rc = VERR_NOT_SUPPORTED;
341 break;
342 }
343 }
344
345 if (RT_FAILURE(rc))
346 {
347 LogFlowFunc(("Parameter %RU32 invalid (%Rrc), refusing\n",
348 i, rc));
349 break;
350 }
351 }
352 }
353
354 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
355 return rc;
356 }
357
358 int Assign(const ClientConnection *pConnection)
359 {
360 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
361
362 int rc;
363
364 LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
365 mMsgType, mParmCount, mpParms));
366
367 /* Does the current host command need more parameter space which
368 * the client does not provide yet? */
369 if (mParmCount > pConnection->mNumParms)
370 {
371 /*
372 * So this call apparently failed because the guest wanted to peek
373 * how much parameters it has to supply in order to successfully retrieve
374 * this command. Let's tell him so!
375 */
376 rc = VERR_TOO_MUCH_DATA;
377 }
378 else
379 {
380 rc = CopyTo(pConnection->mParms, pConnection->mNumParms);
381
382 /*
383 * Has there been enough parameter space but the wrong parameter types
384 * were submitted -- maybe the client was just asking for the next upcoming
385 * host message?
386 *
387 * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
388 * in every case.
389 */
390 if (RT_FAILURE(rc))
391 rc = VERR_TOO_MUCH_DATA;
392 }
393
394 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
395 return rc;
396 }
397
398 int Peek(const ClientConnection *pConnection)
399 {
400 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
401
402 LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
403 mMsgType, mParmCount, mpParms));
404 LogFlowFunc(("Telling client the next upcoming message type=%RU32, count=%RU32\n",
405 mMsgType, mParmCount));
406
407 if (pConnection->mNumParms >= 2)
408 {
409 pConnection->mParms[0].setUInt32(mMsgType); /* Message ID */
410 pConnection->mParms[1].setUInt32(mParmCount); /* Required parameters for message */
411 }
412 else
413 LogFlowFunc(("Client has not (yet) submitted enough parameters (%RU32) to at least peak for the next message\n",
414 pConnection->mNumParms));
415
416 /*
417 * Always return VERR_TOO_MUCH_DATA data here to
418 * keep it compatible with older clients and to
419 * have correct accounting (mHostRc + mHostCmdTries).
420 */
421 return VERR_TOO_MUCH_DATA;
422 }
423
424 /** Reference count for keeping track how many connected
425 * clients still need to process this command until it can
426 * be removed. */
427 uint32_t mRefCount;
428 /** The context ID this command belongs to. Will be extracted
429 * *always* from HGCM parameter [0]. */
430 uint32_t mContextID;
431 /** Dynamic structure for holding the HGCM parms */
432 uint32_t mMsgType;
433 uint32_t mParmCount;
434 PVBOXHGCMSVCPARM mpParms;
435} HostCommand;
436
437/**
438 * Per-client structure used for book keeping/state tracking a
439 * certain host command.
440 */
441typedef struct ClientContext
442{
443 /* Pointer to list node of this command. */
444 HostCommand *mpHostCmd;
445 /** The standard constructor. */
446 ClientContext(void) : mpHostCmd(NULL) {}
447 /** Internal constrcutor. */
448 ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {}
449} ClientContext;
450typedef std::map< uint32_t, ClientContext > ClientContextMap;
451typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter;
452typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst;
453
454/**
455 * Structure for holding a connected guest client
456 * state.
457 */
458typedef struct ClientState
459{
460 ClientState(void)
461 : mSvcHelpers(NULL),
462 mFlags(0), mContextFilter(0),
463 mpHostCmd(NULL), mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
464 mIsPending(false) {}
465
466 ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers)
467 : mSvcHelpers(pSvcHelpers),
468 mFlags(0), mContextFilter(0),
469 mpHostCmd(NULL), mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
470 mIsPending(false) {}
471
472 bool WantsHostCommand(const HostCommand *pHostCmd) const
473 {
474 AssertPtrReturn(pHostCmd, false);
475
476#ifdef DEBUG_andy
477 LogFlowFunc(("mFlags=%x, mContextID=%RU32, mContextFilter=%x, filterRes=%x\n",
478 mFlags, pHostCmd->mContextID, mContextFilter, pHostCmd->mContextID & mContextFilter));
479#endif
480 /*
481 * If a sesseion filter is set, only obey those sessions we're interested in.
482 */
483 if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER)
484 {
485 if ((pHostCmd->mContextID & mContextFilter) == mContextFilter)
486 return true;
487 }
488 else /* Client is interested in all commands. */
489 return true;
490
491 return false;
492 }
493
494 int SetPending(const ClientConnection *pConnection)
495 {
496 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
497
498 LogFlowFunc(("mIsPending=%RTbool, mpHostCmd=%p, CID=%RU32, type=%RU32\n",
499 mIsPending, mpHostCmd,
500 mpHostCmd ? mpHostCmd->mContextID : 0,
501 mpHostCmd ? mpHostCmd->mMsgType : 0));
502
503 if (mIsPending)
504 {
505 LogFlowFunc(("Client already is in pending mode\n"));
506
507 /*
508 * Signal that we don't and can't return yet.
509 */
510 return VINF_HGCM_ASYNC_EXECUTE;
511 }
512
513 if (mpHostCmd == NULL)
514 {
515 AssertMsg(mIsPending == false,
516 ("Client %p already is pending but tried to receive a new host command\n", this));
517
518 mPending.mHandle = pConnection->mHandle;
519 mPending.mNumParms = pConnection->mNumParms;
520 mPending.mParms = pConnection->mParms;
521
522 mIsPending = true;
523
524 LogFlowFunc(("Client now is in pending mode\n"));
525
526 /*
527 * Signal that we don't and can't return yet.
528 */
529 return VINF_HGCM_ASYNC_EXECUTE;
530 }
531
532 /*
533 * Signal that there already is a connection pending.
534 * Shouldn't happen in daily usage.
535 */
536 AssertMsgFailed(("Client already has a connection pending\n"));
537 return VERR_SIGNAL_PENDING;
538 }
539
540 int SetNextCommand(HostCommand *pHostCmd)
541 {
542 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
543
544 mpHostCmd = pHostCmd;
545 AssertPtr(mpHostCmd);
546 mpHostCmd->AddRef();
547
548 /* Create a command context to keep track of client-specific
549 * information about a certain command. */
550 Assert(mContextMap.find(mpHostCmd->mContextID) == mContextMap.end());
551 mContextMap[mpHostCmd->mContextID] = ClientContext(mpHostCmd);
552 /** @todo Exception handling! */
553
554 LogFlowFunc(("Assigning next host comamnd CID=%RU32, cmdType=%RU32, cmdParms=%RU32, new refCount=%RU32\n",
555 mpHostCmd->mContextID, mpHostCmd->mMsgType, mpHostCmd->mParmCount, mpHostCmd->mRefCount));
556
557 return VINF_SUCCESS;
558 }
559
560 int Run(const ClientConnection *pConnection,
561 const RTLISTANCHOR *pHostCmdList)
562 {
563 int rc = VINF_SUCCESS;
564
565 LogFlowFunc(("Client pConnection=%p, pHostCmdList=%p\n",
566 pConnection, pHostCmdList));
567 LogFlowFunc(("Client hostCmd=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32\n",
568 mpHostCmd, mHostCmdRc, mHostCmdTries));
569
570 /* No current command? Try getting a new one to process now. */
571 if (mpHostCmd == NULL)
572 {
573 /* Get the next host command the clienet is interested in. */
574 bool fFoundCmd = false;
575 HostCommand *pCurCmd;
576 RTListForEach(pHostCmdList, pCurCmd, HostCommand, Node)
577 {
578 fFoundCmd = WantsHostCommand(pCurCmd);
579 if (fFoundCmd)
580 {
581 int rc2 = SetNextCommand(pCurCmd);
582 if (RT_SUCCESS(rc2))
583 break;
584 }
585 }
586
587 LogFlowFunc(("Client %s new command\n",
588 fFoundCmd ? "found" : "did not find a"));
589
590 /* If no new command was found, set client into pending state. */
591 if (!fFoundCmd)
592 rc = SetPending(pConnection);
593 }
594
595 if (mpHostCmd)
596 {
597 AssertPtr(mpHostCmd);
598 mHostCmdRc = SendReply(pConnection, mpHostCmd);
599 LogFlowFunc(("Processing command CID=%RU32 ended with rc=%Rrc\n",
600 mpHostCmd->mContextID, mHostCmdRc));
601
602 bool fRemove = false;
603 if (RT_FAILURE(mHostCmdRc))
604 {
605 mHostCmdTries++;
606
607 /*
608 * If the client understood the message but supplied too little buffer space
609 * don't send this message again and drop it after 3 unsuccessful attempts.
610 * The host then should take care of next actions (maybe retry it with a smaller buffer).
611 */
612 if ( mHostCmdRc == VERR_TOO_MUCH_DATA
613 && mHostCmdTries >= 3)
614 {
615 fRemove = true;
616 }
617 /* Client did not understand the message or something else weird happened. Try again one
618 * more time and drop it if it didn't get handled then. */
619 else if (mHostCmdTries > 1)
620 fRemove = true;
621 }
622 else
623 fRemove = true; /* Everything went fine, remove it. */
624
625 LogFlowFunc(("Client tried CID=%RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
626 mpHostCmd->mContextID, mHostCmdTries, mHostCmdRc, fRemove));
627
628 if (fRemove)
629 {
630 /* Try fetching next command. */
631 HostCommand *pCmdNext = RTListGetNext(pHostCmdList, mpHostCmd, HostCommand, Node);
632
633 LogFlowFunc(("Client removes itself from command CID=%RU32 (next command: %p, CID=%RU32)\n",
634 mpHostCmd->mContextID, pCmdNext, pCmdNext ? pCmdNext->mContextID : 0));
635
636 /* Remove command from context map. */
637 /** @todo Exception handling! */
638 mContextMap.erase(mpHostCmd->mContextID);
639
640 /* Release reference for current command. */
641 if (mpHostCmd->Release() == 0)
642 {
643 LogFlowFunc(("Destroying command CID=%RU32\n",
644 mpHostCmd->mContextID));
645
646 RTMemFree(mpHostCmd);
647 }
648
649 /* Assign next command (if any) to this client. */
650 if (pCmdNext)
651 {
652 rc = SetNextCommand(pCmdNext);
653 }
654 else
655 mpHostCmd = NULL;
656
657 /* Reset everything else. */
658 mHostCmdRc = VINF_SUCCESS;
659 mHostCmdTries = 0;
660 }
661
662 if (RT_SUCCESS(rc))
663 rc = mHostCmdRc;
664 }
665
666 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
667 return rc;
668 }
669
670 int RunNow(const ClientConnection *pConnection,
671 const PRTLISTANCHOR pHostCmdList)
672 {
673 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
674 AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
675
676 AssertMsgReturn(!mIsPending, ("Can't use another connection when client still is in pending mode\n"),
677 VERR_INVALID_PARAMETER);
678
679 int rc = Run(pConnection, pHostCmdList);
680
681 LogFlowFunc(("Returned with rc=%Rrc\n"));
682 return rc;
683 }
684
685 int Wakeup(const PRTLISTANCHOR pHostCmdList)
686 {
687 AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
688 AssertMsgReturn(mIsPending, ("Cannot wake up a client which is not in pending mode\n"),
689 VERR_INVALID_PARAMETER);
690
691 int rc = Run(&mPending, pHostCmdList);
692
693 /* Reset pending state. */
694 mIsPending = false;
695
696 LogFlowFunc(("Returned with rc=%Rrc\n"));
697 return rc;
698 }
699
700 int CancelWaiting(int rcPending)
701 {
702 LogFlowFunc(("Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
703 rcPending, mIsPending, mPending.mNumParms, mFlags));
704
705 if ( mIsPending
706 && mPending.mNumParms >= 2)
707 {
708 mPending.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
709 mPending.mParms[1].setUInt32(0); /* Required parameters for message. */
710
711 AssertPtr(mSvcHelpers);
712 mSvcHelpers->pfnCallComplete(mPending.mHandle, rcPending);
713
714 mIsPending = false;
715 }
716
717 return VINF_SUCCESS;
718 }
719
720 int SendReply(const ClientConnection *pConnection,
721 HostCommand *pHostCmd)
722 {
723 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
724 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
725
726 int rc;
727 if (mIsPending)
728 {
729 /* If the client is in pending mode, always send back
730 * the peek result first. */
731 rc = pHostCmd->Peek(pConnection);
732 }
733 else
734 {
735 /* Try assigning the host command to the client and store the
736 * result code for later use. */
737 rc = pHostCmd->Assign(pConnection);
738 if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */
739 rc = pHostCmd->Peek(pConnection);
740 }
741
742 /* In any case the client did something, so complete
743 * the pending call with the result we just got. */
744 AssertPtr(mSvcHelpers);
745 mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
746
747 LogFlowFunc(("pConnection=%p, pHostCmd=%p, rc=%Rrc\n",
748 pConnection, pHostCmd, rc));
749 return rc;
750 }
751
752 PVBOXHGCMSVCHELPERS mSvcHelpers;
753 /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */
754 uint32_t mFlags;
755 /** The context ID filter, based on the flags set. */
756 uint32_t mContextFilter;
757 /** Pointer to current host command to process. */
758 HostCommand *mpHostCmd;
759 /** Last (most recent) rc after handling the
760 * host command. */
761 int mHostCmdRc;
762 /** How many times the host service has tried to deliver this
763 * command to the according client. */
764 uint32_t mHostCmdTries;
765 /** Map containing all context IDs a client is assigned to. */
766 ClientContextMap mContextMap;
767 /** Flag indicating whether the client currently is pending. */
768 bool mIsPending;
769 /** The client's pending connection. */
770 ClientConnection mPending;
771} ClientState;
772typedef std::map< uint32_t, ClientState > ClientStateMap;
773typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter;
774typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst;
775
776/**
777 * Class containing the shared information service functionality.
778 */
779class Service : public RTCNonCopyable
780{
781
782private:
783
784 /** Type definition for use in callback functions. */
785 typedef Service SELF;
786 /** HGCM helper functions. */
787 PVBOXHGCMSVCHELPERS mpHelpers;
788 /**
789 * Callback function supplied by the host for notification of updates
790 * to properties.
791 */
792 PFNHGCMSVCEXT mpfnHostCallback;
793 /** User data pointer to be supplied to the host callback function. */
794 void *mpvHostData;
795 /** List containing all buffered host commands. */
796 RTLISTANCHOR mHostCmdList;
797 /** Map containing all connected clients. The primary key contains
798 * the HGCM client ID to identify the client. */
799 ClientStateMap mClientStateMap;
800public:
801 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
802 : mpHelpers(pHelpers)
803 , mpfnHostCallback(NULL)
804 , mpvHostData(NULL)
805 {
806 RTListInit(&mHostCmdList);
807 }
808
809 /**
810 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
811 * Simply deletes the service object
812 */
813 static DECLCALLBACK(int) svcUnload (void *pvService)
814 {
815 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
816 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
817 int rc = pSelf->uninit();
818 AssertRC(rc);
819 if (RT_SUCCESS(rc))
820 delete pSelf;
821 return rc;
822 }
823
824 /**
825 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
826 * Stub implementation of pfnConnect and pfnDisconnect.
827 */
828 static DECLCALLBACK(int) svcConnect (void *pvService,
829 uint32_t u32ClientID,
830 void *pvClient)
831 {
832 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
833 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
834 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
835 return pSelf->clientConnect(u32ClientID, pvClient);
836 }
837
838 /**
839 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
840 * Stub implementation of pfnConnect and pfnDisconnect.
841 */
842 static DECLCALLBACK(int) svcDisconnect (void *pvService,
843 uint32_t u32ClientID,
844 void *pvClient)
845 {
846 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
847 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
848 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
849 return pSelf->clientDisconnect(u32ClientID, pvClient);
850 }
851
852 /**
853 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
854 * Wraps to the call member function
855 */
856 static DECLCALLBACK(void) svcCall (void * pvService,
857 VBOXHGCMCALLHANDLE callHandle,
858 uint32_t u32ClientID,
859 void *pvClient,
860 uint32_t u32Function,
861 uint32_t cParms,
862 VBOXHGCMSVCPARM paParms[])
863 {
864 AssertLogRelReturnVoid(VALID_PTR(pvService));
865 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n",
866 pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
867 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
868 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
869 }
870
871 /**
872 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
873 * Wraps to the hostCall member function
874 */
875 static DECLCALLBACK(int) svcHostCall (void *pvService,
876 uint32_t u32Function,
877 uint32_t cParms,
878 VBOXHGCMSVCPARM paParms[])
879 {
880 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
881 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
882 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
883 return pSelf->hostCall(u32Function, cParms, paParms);
884 }
885
886 /**
887 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
888 * Installs a host callback for notifications of property changes.
889 */
890 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
891 PFNHGCMSVCEXT pfnExtension,
892 void *pvExtension)
893 {
894 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
895 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
896 pSelf->mpfnHostCallback = pfnExtension;
897 pSelf->mpvHostData = pvExtension;
898 return VINF_SUCCESS;
899 }
900
901private:
902
903 int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
904 int clientConnect(uint32_t u32ClientID, void *pvClient);
905 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
906 int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
907 int clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
908 int cancelHostCmd(uint32_t u32ContextID);
909 int cancelPendingWaits(uint32_t u32ClientID, int rcPending);
910 int hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
911 int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
912 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
913 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
914 int uninit(void);
915};
916
917/**
918 * Handles a client which just connected.
919 *
920 * @return IPRT status code.
921 * @param u32ClientID
922 * @param pvClient
923 */
924int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
925{
926 LogFlowFunc(("New client with ID=%RU32 connected\n", u32ClientID));
927#ifdef VBOX_STRICT
928 ClientStateMapIterConst it = mClientStateMap.find(u32ClientID);
929 if (it != mClientStateMap.end())
930 {
931 AssertMsgFailed(("Client with ID=%RU32 already connected when it should not\n",
932 u32ClientID));
933 return VERR_ALREADY_EXISTS;
934 }
935#endif
936 ClientState cs(mpHelpers);
937 mClientStateMap[u32ClientID] = cs;
938 /** @todo Exception handling! */
939 return VINF_SUCCESS;
940}
941
942/**
943 * Handles a client which disconnected. This functiond does some
944 * internal cleanup as well as sends notifications to the host so
945 * that the host can do the same (if required).
946 *
947 * @return IPRT status code.
948 * @param u32ClientID The client's ID of which disconnected.
949 * @param pvClient User data, not used at the moment.
950 */
951int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
952{
953 LogFlowFunc(("Client with ID=%RU32 (%zu clients total) disconnected\n",
954 u32ClientID, mClientStateMap.size()));
955
956 /* If this was the last connected (guest) client we need to
957 * unblock all eventually queued up (waiting) host calls. */
958 bool fAllClientsDisconnected = mClientStateMap.size() == 0;
959 if (fAllClientsDisconnected)
960 LogFlowFunc(("No connected clients left, notifying all queued up host callbacks\n"));
961
962 /*
963 * Throw out all stale clients.
964 */
965 int rc = VINF_SUCCESS;
966
967 ClientStateMapIter itClientState = mClientStateMap.begin();
968 while ( itClientState != mClientStateMap.end()
969 && RT_SUCCESS(rc))
970 {
971 /*
972 * Unblock/call back all queued items of the specified client
973 * or for all items in case there is no waiting client around
974 * anymore.
975 */
976 if ( itClientState->first == u32ClientID
977 || fAllClientsDisconnected)
978 {
979 LogFlowFunc(("Cancelling %RU32 context(s) of client ID=%RU32\n",
980 itClientState->second.mContextMap.size(), u32ClientID));
981
982 ClientContextMapIter itContext = itClientState->second.mContextMap.begin();
983 while (itContext != itClientState->second.mContextMap.end())
984 {
985 uint32_t uContextID = itContext->first;
986
987 /*
988 * Notify the host that clients with u32ClientID are no longer
989 * around and need to be cleaned up (canceling waits etc).
990 */
991 LogFlowFunc(("Notifying CID=%RU32 of disconnect ...\n", uContextID));
992 int rc2 = cancelHostCmd(uContextID);
993 if (RT_FAILURE(rc2))
994 {
995 LogFlowFunc(("Cancelling host command with CID=%RU32 failed with rc=%Rrc\n",
996 uContextID, rc2));
997 /* Keep going. */
998 }
999
1000 AssertPtr(itContext->second.mpHostCmd);
1001 itContext->second.mpHostCmd->Release();
1002
1003 itContext++;
1004 }
1005
1006 itClientState->second.mContextMap.clear();
1007
1008 /** @todo Exception handling! */
1009 mClientStateMap.erase(itClientState++);
1010 }
1011 else
1012 itClientState++;
1013 }
1014
1015 if (fAllClientsDisconnected)
1016 {
1017 /*
1018 * If all clients disconnected we also need to make sure that all buffered
1019 * host commands need to be notified, because Main is waiting a notification
1020 * via a (multi stage) progress object.
1021 */
1022 HostCommand *pCurCmd = RTListGetFirst(&mHostCmdList, HostCommand, Node);
1023 while (pCurCmd)
1024 {
1025 HostCommand *pNext = RTListNodeGetNext(&pCurCmd->Node, HostCommand, Node);
1026 bool fLast = RTListNodeIsLast(&mHostCmdList, &pCurCmd->Node);
1027
1028 int rc2 = cancelHostCmd(pCurCmd->mContextID);
1029 if (RT_FAILURE(rc2))
1030 {
1031 LogFlowFunc(("Cancelling host command with CID=%u (refCount=%RU32) failed with rc=%Rrc\n",
1032 pCurCmd->mContextID, pCurCmd->mRefCount, rc2));
1033 /* Keep going. */
1034 }
1035
1036 pCurCmd->Free();
1037
1038 RTListNodeRemove(&pCurCmd->Node);
1039 RTMemFree(pCurCmd);
1040
1041 if (fLast)
1042 break;
1043
1044 pCurCmd = pNext;
1045 }
1046
1047 Assert(RTListIsEmpty(&mHostCmdList));
1048 }
1049
1050 return rc;
1051}
1052
1053/**
1054 * Either fills in parameters from a pending host command into our guest context or
1055 * defer the guest call until we have something from the host.
1056 *
1057 * @return IPRT status code.
1058 * @param u32ClientID The client's ID.
1059 * @param callHandle The client's call handle.
1060 * @param cParms Number of parameters.
1061 * @param paParms Array of parameters.
1062 */
1063int Service::clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
1064 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1065{
1066 /*
1067 * Lookup client in our list so that we can assign the context ID of
1068 * a command to that client.
1069 */
1070 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1071 AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
1072 u32ClientID));
1073 if (itClientState == mClientStateMap.end())
1074 return VERR_NOT_FOUND; /* Should never happen. */
1075
1076 ClientState &clientState = itClientState->second;
1077
1078 /* Use the current (inbound) connection. */
1079 ClientConnection thisCon;
1080 thisCon.mHandle = callHandle;
1081 thisCon.mNumParms = cParms;
1082 thisCon.mParms = paParms;
1083
1084 /*
1085 * If host command list is empty (nothing to do right now) just
1086 * defer the call until we got something to do (makes the client
1087 * wait).
1088 */
1089 int rc;
1090 if (RTListIsEmpty(&mHostCmdList))
1091 {
1092 rc = clientState.SetPending(&thisCon);
1093 }
1094 else
1095 {
1096 rc = clientState.RunNow(&thisCon, &mHostCmdList);
1097 }
1098
1099 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1100 return rc;
1101}
1102
1103int Service::clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
1104 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1105{
1106 /*
1107 * Lookup client in our list so that we can assign the context ID of
1108 * a command to that client.
1109 */
1110 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1111 AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
1112 u32ClientID));
1113 if (itClientState == mClientStateMap.end())
1114 return VERR_NOT_FOUND; /* Should never happen. */
1115
1116 if (cParms != 2)
1117 return VERR_INVALID_PARAMETER;
1118
1119 uint32_t uMaskAdd, uMaskRemove;
1120 int rc = paParms[0].getUInt32(&uMaskAdd);
1121 if (RT_SUCCESS(rc))
1122 rc = paParms[1].getUInt32(&uMaskRemove);
1123 if (RT_SUCCESS(rc))
1124 {
1125 /* paParms[1] unused yet. */
1126
1127 ClientState &clientState = itClientState->second;
1128
1129 clientState.mFlags |= CLIENTSTATE_FLAG_CONTEXTFILTER;
1130 if (uMaskAdd)
1131 clientState.mContextFilter |= uMaskAdd;
1132 if (uMaskRemove)
1133 clientState.mContextFilter &= ~uMaskRemove;
1134
1135 LogFlowFunc(("Client ID=%RU32 now has filter=%x enabled (flags=%x, maskAdd=%x, maskRemove=%x)\n",
1136 u32ClientID, clientState.mContextFilter, clientState.mFlags,
1137 uMaskAdd, uMaskRemove));
1138 }
1139
1140 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1141 return rc;
1142}
1143
1144/**
1145 * Cancels a buffered host command to unblock waiting on Main side
1146 * via callbacks.
1147 *
1148 * @return IPRT status code.
1149 * @param u32ContextID Context ID of host command to cancel.
1150 */
1151int Service::cancelHostCmd(uint32_t u32ContextID)
1152{
1153 Assert(mpfnHostCallback);
1154
1155 LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID));
1156
1157 uint32_t cParms = 0;
1158 VBOXHGCMSVCPARM arParms[2];
1159 arParms[cParms++].setUInt32(u32ContextID);
1160
1161 return hostCallback(GUEST_DISCONNECTED, cParms, arParms);
1162}
1163
1164/**
1165 * Client asks itself (in another thread) to cancel all pending waits which are blocking the client
1166 * from shutting down / doing something else.
1167 *
1168 * @return IPRT status code.
1169 * @param u32ClientID The client's ID.
1170 * @param rcPending Result code for completing pending operation.
1171 */
1172int Service::cancelPendingWaits(uint32_t u32ClientID, int rcPending)
1173{
1174 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1175 if (itClientState != mClientStateMap.end())
1176 return itClientState->second.CancelWaiting(rcPending);
1177
1178 return VINF_SUCCESS;
1179}
1180
1181/**
1182 * Notifies the host (using low-level HGCM callbacks) about an event
1183 * which was sent from the client.
1184 *
1185 * @return IPRT status code.
1186 * @param eFunction Function (event) that occured.
1187 * @param cParms Number of parameters.
1188 * @param paParms Array of parameters.
1189 */
1190int Service::hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1191{
1192 LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
1193 eFunction, cParms, paParms));
1194
1195 int rc;
1196 if (mpfnHostCallback)
1197 {
1198 VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
1199 rc = mpfnHostCallback(mpvHostData, eFunction,
1200 (void *)(&data), sizeof(data));
1201 }
1202 else
1203 rc = VERR_NOT_SUPPORTED;
1204
1205 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1206 return rc;
1207}
1208
1209/**
1210 * Processes a command receiveed from the host side and re-routes it to
1211 * a connect client on the guest.
1212 *
1213 * @return IPRT status code.
1214 * @param eFunction Function code to process.
1215 * @param cParms Number of parameters.
1216 * @param paParms Array of parameters.
1217 */
1218int Service::hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1219{
1220 /*
1221 * If no client is connected at all we don't buffer any host commands
1222 * and immediately return an error to the host. This avoids the host
1223 * waiting for a response from the guest side in case VBoxService on
1224 * the guest is not running/system is messed up somehow.
1225 */
1226 if (mClientStateMap.size() == 0)
1227 return VERR_NOT_FOUND;
1228
1229 int rc;
1230 HostCommand *pHostCmd = (HostCommand*)RTMemAllocZ(sizeof(HostCommand));
1231 if (pHostCmd)
1232 {
1233 rc = pHostCmd->Allocate(eFunction, cParms, paParms);
1234 if (RT_SUCCESS(rc))
1235 RTListAppend(&mHostCmdList, &pHostCmd->Node);
1236 }
1237 else
1238 rc = VERR_NO_MEMORY;
1239
1240 if (RT_SUCCESS(rc))
1241 {
1242 LogFlowFunc(("Handling host command CID=%RU32, numClients=%zu\n",
1243 pHostCmd->mContextID, mClientStateMap.size()));
1244
1245 /*
1246 * Wake up all pending clients which are interested in this
1247 * host command.
1248 */
1249#ifdef DEBUG
1250 uint32_t uClientsWokenUp = 0;
1251#endif
1252
1253 ClientStateMapIter itClientState = mClientStateMap.begin();
1254 AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n"));
1255 while (itClientState != mClientStateMap.end())
1256 {
1257 if (itClientState->second.mIsPending) /* Only wake up pending clients. */
1258 {
1259 LogFlowFunc(("Waking up client ID=%RU32 (isPending=%RTbool) ...\n",
1260 itClientState->first, itClientState->second.mIsPending));
1261
1262 ClientState &clientState = itClientState->second;
1263 int rc2 = clientState.Wakeup(&mHostCmdList);
1264 LogFlowFunc(("Client ID=%RU32 wakeup ended with rc=%Rrc\n",
1265 itClientState->first, rc2));
1266#ifdef DEBUG
1267 uClientsWokenUp++;
1268#endif
1269 }
1270 else
1271 LogFlowFunc(("Client ID=%RU32 is not in pending state\n",
1272 itClientState->first));
1273
1274 itClientState++;
1275 }
1276
1277#ifdef DEBUG
1278 LogFlowFunc(("%RU32 clients have been succcessfully woken up\n",
1279 uClientsWokenUp));
1280#endif
1281 }
1282
1283 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
1284 return rc;
1285}
1286
1287/**
1288 * Handle an HGCM service call.
1289 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
1290 * @note All functions which do not involve an unreasonable delay will be
1291 * handled synchronously. If needed, we will add a request handler
1292 * thread in future for those which do.
1293 *
1294 * @thread HGCM
1295 */
1296void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
1297 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
1298 VBOXHGCMSVCPARM paParms[])
1299{
1300 int rc = VINF_SUCCESS;
1301 LogFlowFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=0x%p\n",
1302 u32ClientID, eFunction, cParms, paParms));
1303 try
1304 {
1305 /*
1306 * The guest asks the host for the next message to process.
1307 */
1308 if (eFunction == GUEST_MSG_WAIT)
1309 {
1310 LogFlowFunc(("GUEST_MSG_GET\n"));
1311 rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms);
1312 }
1313 else
1314 {
1315 switch (eFunction)
1316 {
1317 /*
1318 * A client wants to shut down and asks us (this service) to cancel
1319 * all blocking/pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
1320 * client can gracefully shut down.
1321 */
1322 case GUEST_CANCEL_PENDING_WAITS:
1323 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
1324 rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */);
1325 break;
1326
1327 /*
1328 * The guest only wants certain messages set by the filter mask(s).
1329 * Since VBox 4.3+.
1330 */
1331 case GUEST_MSG_FILTER:
1332 LogFlowFunc(("GUEST_MSG_FILTER\n"));
1333 rc = clientSetMsgFilter(u32ClientID, callHandle, cParms, paParms);
1334 break;
1335
1336 /*
1337 * For all other regular commands we call our hostCallback
1338 * function. If the current command does not support notifications,
1339 * notifyHost will return VERR_NOT_SUPPORTED.
1340 */
1341 default:
1342 rc = hostCallback(eFunction, cParms, paParms);
1343 break;
1344 }
1345
1346 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1347 {
1348 /* Tell the client that the call is complete (unblocks waiting). */
1349 AssertPtr(mpHelpers);
1350 mpHelpers->pfnCallComplete(callHandle, rc);
1351 }
1352 }
1353 }
1354 catch (std::bad_alloc)
1355 {
1356 rc = VERR_NO_MEMORY;
1357 }
1358}
1359
1360/**
1361 * Service call handler for the host.
1362 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
1363 * @thread hgcm
1364 */
1365int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1366{
1367 int rc = VERR_NOT_SUPPORTED;
1368 LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n",
1369 eFunction, cParms, paParms));
1370 try
1371 {
1372 switch (eFunction)
1373 {
1374 /**
1375 * Host
1376 */
1377 case HOST_CANCEL_PENDING_WAITS:
1378 {
1379 LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n"));
1380 ClientStateMapIter itClientState = mClientStateMap.begin();
1381 while (itClientState != mClientStateMap.end())
1382 {
1383 int rc2 = itClientState->second.CancelWaiting(VINF_SUCCESS /* Pending rc. */);
1384 if (RT_FAILURE(rc2))
1385 LogFlowFunc(("Cancelling waiting for client ID=%RU32 failed with rc=%Rrc",
1386 itClientState->first, rc2));
1387 itClientState++;
1388 }
1389 rc = VINF_SUCCESS;
1390 break;
1391 }
1392
1393 default:
1394 rc = hostProcessCommand(eFunction, cParms, paParms);
1395 break;
1396 }
1397 }
1398 catch (std::bad_alloc)
1399 {
1400 rc = VERR_NO_MEMORY;
1401 }
1402
1403 return rc;
1404}
1405
1406int Service::uninit()
1407{
1408 return VINF_SUCCESS;
1409}
1410
1411} /* namespace guestControl */
1412
1413using guestControl::Service;
1414
1415/**
1416 * @copydoc VBOXHGCMSVCLOAD
1417 */
1418extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
1419{
1420 int rc = VINF_SUCCESS;
1421
1422 LogFlowFunc(("ptable = %p\n", ptable));
1423
1424 if (!VALID_PTR(ptable))
1425 {
1426 rc = VERR_INVALID_PARAMETER;
1427 }
1428 else
1429 {
1430 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1431
1432 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1433 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1434 {
1435 rc = VERR_VERSION_MISMATCH;
1436 }
1437 else
1438 {
1439 std::auto_ptr<Service> apService;
1440 /* No exceptions may propagate outside. */
1441 try {
1442 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1443 } catch (int rcThrown) {
1444 rc = rcThrown;
1445 } catch (...) {
1446 rc = VERR_UNRESOLVED_ERROR;
1447 }
1448
1449 if (RT_SUCCESS(rc))
1450 {
1451 /*
1452 * We don't need an additional client data area on the host,
1453 * because we're a class which can have members for that :-).
1454 */
1455 ptable->cbClient = 0;
1456
1457 /* Register functions. */
1458 ptable->pfnUnload = Service::svcUnload;
1459 ptable->pfnConnect = Service::svcConnect;
1460 ptable->pfnDisconnect = Service::svcDisconnect;
1461 ptable->pfnCall = Service::svcCall;
1462 ptable->pfnHostCall = Service::svcHostCall;
1463 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1464 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1465 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1466
1467 /* Service specific initialization. */
1468 ptable->pvService = apService.release();
1469 }
1470 }
1471 }
1472
1473 LogFlowFunc(("returning %Rrc\n", rc));
1474 return rc;
1475}
1476
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