VirtualBox

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

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

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

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