VirtualBox

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

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