VirtualBox

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

Last change on this file since 47621 was 47621, checked in by vboxsync, 11 years ago

Logging.

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