VirtualBox

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

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

Newline.

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