VirtualBox

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

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

GuestControl/service.cpp: Support uint64_t HGCM parameters.

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