VirtualBox

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

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

Build fix.

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