VirtualBox

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

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

GuestCtrl: Added abstract IGuestProcessIOEvent event, removed duplicate code, making the testdriver execution tests pass.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette