VirtualBox

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

Last change on this file since 75737 was 75737, checked in by vboxsync, 6 years ago

HGCM: Replace C++-style inline members on VBOXHGCMSVCPARM with simple functions.
bugref:9172: Shared folder performance tuning
Changes in bugref:9172 caused a build regression on Ubuntu 18.10 due to the
use of RT_ZERO on a structure containing an embedded VBOXHGCMSVCPARM, as
VBOXHGCMSVCPARM had member functions and was therefore not a simple plain-
old-data structure. Rather than just doing the sensible thing and zeroing
it in a different way, I converted the inline member getters and setters to
standard inline C functions, including fixing callers. Actually I had planned
this for some time, as the member function use seemed a bit gratuitous in
hindsight.

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