VirtualBox

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

Last change on this file since 71331 was 71331, checked in by vboxsync, 7 years ago

Guest Control/service: Build fix.

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