VirtualBox

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

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

GuestCtrl: More code for guest session infrastructure handling (untested, work in progress). Added IGuestSession.status for (later) asynchronous handling.

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