VirtualBox

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

Last change on this file since 44499 was 44249, checked in by vboxsync, 12 years ago

GuestControl/service: const, HostCmd -> VBOXGUESTCTRLHOSTCMD.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.8 KB
Line 
1/* $Id: service.cpp 44249 2013-01-08 11:08:44Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2011-2012 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
54/*******************************************************************************
55* Header Files *
56*******************************************************************************/
57#define LOG_GROUP LOG_GROUP_HGCM
58#include <VBox/HostServices/GuestControlSvc.h>
59
60#include <VBox/log.h>
61#include <iprt/asm.h> /* For ASMBreakpoint(). */
62#include <iprt/assert.h>
63#include <iprt/cpp/autores.h>
64#include <iprt/cpp/utils.h>
65#include <iprt/err.h>
66#include <iprt/mem.h>
67#include <iprt/req.h>
68#include <iprt/string.h>
69#include <iprt/thread.h>
70#include <iprt/time.h>
71
72#include <memory> /* for auto_ptr */
73#include <string>
74#include <list>
75
76#include "gctrl.h"
77
78namespace guestControl {
79
80/**
81 * Structure for holding all clients with their
82 * generated host contexts. This is necessary for
83 * maintaining the relationship between a client and its context IDs.
84 */
85struct ClientContexts
86{
87 /** This client ID. */
88 uint32_t mClientID;
89 /** The list of contexts a client is assigned to. */
90 std::list< uint32_t > mContextList;
91
92 /** The normal constructor. */
93 ClientContexts(uint32_t aClientID)
94 : mClientID(aClientID) {}
95};
96/** The client list + iterator type */
97typedef std::list< ClientContexts > ClientContextsList;
98typedef std::list< ClientContexts >::iterator ClientContextsListIter;
99typedef std::list< ClientContexts >::const_iterator ClientContextsListIterConst;
100
101/**
102 * Structure for holding an uncompleted guest call.
103 */
104struct ClientWaiter
105{
106 /** Client ID; a client can have multiple handles! */
107 uint32_t mClientID;
108 /** The call handle */
109 VBOXHGCMCALLHANDLE mHandle;
110 /** The call parameters */
111 VBOXHGCMSVCPARM *mParms;
112 /** Number of parameters */
113 uint32_t mNumParms;
114
115 /** The standard constructor. */
116 ClientWaiter() : mClientID(0), mHandle(0), mParms(NULL), mNumParms(0) {}
117 /** The normal constructor. */
118 ClientWaiter(uint32_t aClientID, VBOXHGCMCALLHANDLE aHandle,
119 VBOXHGCMSVCPARM aParms[], uint32_t cParms)
120 : mClientID(aClientID), mHandle(aHandle), mParms(aParms), mNumParms(cParms) {}
121};
122/** The guest call list type */
123typedef std::list< ClientWaiter > ClientWaiterList;
124typedef std::list< ClientWaiter >::iterator CallListIter;
125typedef std::list< ClientWaiter >::const_iterator CallListIterConst;
126
127/**
128 * Structure for holding a buffered host command.
129 */
130typedef struct VBOXGUESTCTRLHOSTCMD
131{
132 /** The context ID this command belongs to. Will be extracted
133 * from the HGCM parameters. */
134 uint32_t mContextID;
135 /** How many times the host service has tried to deliver this
136 * command to the guest. */
137 uint32_t mTries;
138 /** Dynamic structure for holding the HGCM parms */
139 VBOXGUESTCTRPARAMBUFFER mParmBuf;
140
141 /** The standard constructor. */
142 VBOXGUESTCTRLHOSTCMD() : mContextID(0), mTries(0) {}
143};
144/** The host cmd list + iterator type */
145typedef std::list< VBOXGUESTCTRLHOSTCMD > HostCmdList;
146typedef std::list< VBOXGUESTCTRLHOSTCMD >::iterator HostCmdListIter;
147typedef std::list< VBOXGUESTCTRLHOSTCMD >::const_iterator HostCmdListIterConst;
148
149/**
150 * Class containing the shared information service functionality.
151 */
152class Service : public RTCNonCopyable
153{
154private:
155 /** Type definition for use in callback functions. */
156 typedef Service SELF;
157 /** HGCM helper functions. */
158 PVBOXHGCMSVCHELPERS mpHelpers;
159 /*
160 * Callback function supplied by the host for notification of updates
161 * to properties.
162 */
163 PFNHGCMSVCEXT mpfnHostCallback;
164 /** User data pointer to be supplied to the host callback function. */
165 void *mpvHostData;
166 /** The deferred calls list. */
167 ClientWaiterList mClientWaiterList;
168 /** The host command list. */
169 HostCmdList mHostCmds;
170 /** Client contexts list. */
171 ClientContextsList mClientContextsList;
172 /** Number of connected clients. */
173 uint32_t mNumClients;
174public:
175 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
176 : mpHelpers(pHelpers)
177 , mpfnHostCallback(NULL)
178 , mpvHostData(NULL)
179 , mNumClients(0)
180 {
181 }
182
183 /**
184 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
185 * Simply deletes the service object
186 */
187 static DECLCALLBACK(int) svcUnload (void *pvService)
188 {
189 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
190 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
191 int rc = pSelf->uninit();
192 AssertRC(rc);
193 if (RT_SUCCESS(rc))
194 delete pSelf;
195 return rc;
196 }
197
198 /**
199 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
200 * Stub implementation of pfnConnect and pfnDisconnect.
201 */
202 static DECLCALLBACK(int) svcConnect (void *pvService,
203 uint32_t u32ClientID,
204 void *pvClient)
205 {
206 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
207 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
208 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
209 int rc = pSelf->clientConnect(u32ClientID, pvClient);
210 LogFlowFunc (("rc=%Rrc\n", rc));
211 return rc;
212 }
213
214 /**
215 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
216 * Stub implementation of pfnConnect and pfnDisconnect.
217 */
218 static DECLCALLBACK(int) svcDisconnect (void *pvService,
219 uint32_t u32ClientID,
220 void *pvClient)
221 {
222 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
223 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
224 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
225 int rc = pSelf->clientDisconnect(u32ClientID, pvClient);
226 LogFlowFunc (("rc=%Rrc\n", rc));
227 return rc;
228 }
229
230 /**
231 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
232 * Wraps to the call member function
233 */
234 static DECLCALLBACK(void) svcCall (void * pvService,
235 VBOXHGCMCALLHANDLE callHandle,
236 uint32_t u32ClientID,
237 void *pvClient,
238 uint32_t u32Function,
239 uint32_t cParms,
240 VBOXHGCMSVCPARM paParms[])
241 {
242 AssertLogRelReturnVoid(VALID_PTR(pvService));
243 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
244 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
245 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
246 LogFlowFunc (("returning\n"));
247 }
248
249 /**
250 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
251 * Wraps to the hostCall member function
252 */
253 static DECLCALLBACK(int) svcHostCall (void *pvService,
254 uint32_t u32Function,
255 uint32_t cParms,
256 VBOXHGCMSVCPARM paParms[])
257 {
258 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
259 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
260 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
261 int rc = pSelf->hostCall(u32Function, cParms, paParms);
262 LogFlowFunc (("rc=%Rrc\n", rc));
263 return rc;
264 }
265
266 /**
267 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
268 * Installs a host callback for notifications of property changes.
269 */
270 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
271 PFNHGCMSVCEXT pfnExtension,
272 void *pvExtension)
273 {
274 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
275 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
276 pSelf->mpfnHostCallback = pfnExtension;
277 pSelf->mpvHostData = pvExtension;
278 return VINF_SUCCESS;
279 }
280private:
281 int paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
282 void paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf);
283 int paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, const VBOXGUESTCTRPARAMBUFFER *pSrcBuf);
284 int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
285 int clientConnect(uint32_t u32ClientID, void *pvClient);
286 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
287 int assignHostCmdToGuest(const VBOXGUESTCTRLHOSTCMD *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
288 int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
289 int cancelHostCmd(uint32_t u32ContextID);
290 int cancelPendingWaits(uint32_t u32ClientID);
291 int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
292 int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
293 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
294 void *pvClient, uint32_t eFunction, uint32_t cParms,
295 VBOXHGCMSVCPARM paParms[]);
296 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
297 int uninit();
298};
299
300
301/**
302 * Stores a HGCM request in an internal buffer. Needs to be free'd using paramBufferFree().
303 *
304 * @return IPRT status code.
305 * @param pBuf Buffer to store the HGCM request into.
306 * @param uMsg Message type.
307 * @param cParms Number of parameters of HGCM request.
308 * @param paParms Array of parameters of HGCM request.
309 */
310int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
311{
312 AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
313 if (cParms)
314 AssertPtrReturn(paParms, VERR_INVALID_POINTER);
315
316 /* Paranoia. */
317 if (cParms > 256)
318 cParms = 256;
319
320 int rc = VINF_SUCCESS;
321
322 /*
323 * Don't verify anything here (yet), because this function only buffers
324 * the HGCM data into an internal structure and reaches it back to the guest (client)
325 * in an unmodified state.
326 */
327 pBuf->uMsg = uMsg;
328 pBuf->uParmCount = cParms;
329 if (pBuf->uParmCount)
330 {
331 pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
332 if (NULL == pBuf->pParms)
333 rc = VERR_NO_MEMORY;
334 }
335
336 if (RT_SUCCESS(rc))
337 {
338 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
339 {
340 pBuf->pParms[i].type = paParms[i].type;
341 switch (paParms[i].type)
342 {
343 case VBOX_HGCM_SVC_PARM_32BIT:
344 pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
345 break;
346
347 case VBOX_HGCM_SVC_PARM_64BIT:
348 /* Not supported yet. */
349 break;
350
351 case VBOX_HGCM_SVC_PARM_PTR:
352 pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
353 if (pBuf->pParms[i].u.pointer.size > 0)
354 {
355 pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
356 if (NULL == pBuf->pParms[i].u.pointer.addr)
357 {
358 rc = VERR_NO_MEMORY;
359 break;
360 }
361 else
362 memcpy(pBuf->pParms[i].u.pointer.addr,
363 paParms[i].u.pointer.addr,
364 pBuf->pParms[i].u.pointer.size);
365 }
366 else
367 {
368 /* Size is 0 -- make sure we don't have any pointer. */
369 pBuf->pParms[i].u.pointer.addr = NULL;
370 }
371 break;
372
373 default:
374 break;
375 }
376 if (RT_FAILURE(rc))
377 break;
378 }
379 }
380 return rc;
381}
382
383/**
384 * Frees a buffered HGCM request.
385 *
386 * @return IPRT status code.
387 * @param pBuf Parameter buffer to free.
388 */
389void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
390{
391 AssertPtr(pBuf);
392 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
393 {
394 switch (pBuf->pParms[i].type)
395 {
396 case VBOX_HGCM_SVC_PARM_PTR:
397 if (pBuf->pParms[i].u.pointer.size > 0)
398 RTMemFree(pBuf->pParms[i].u.pointer.addr);
399 break;
400 }
401 }
402 if (pBuf->uParmCount)
403 {
404 RTMemFree(pBuf->pParms);
405 pBuf->uParmCount = 0;
406 }
407}
408
409/**
410 * Copies data from a buffered HGCM request to the current HGCM request.
411 *
412 * @return IPRT status code.
413 * @param paDstParms Array of parameters of HGCM request to fill the data into.
414 * @param cPDstarms Number of parameters the HGCM request can handle.
415 * @param pSrcBuf Parameter buffer to assign.
416 */
417int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, const VBOXGUESTCTRPARAMBUFFER *pSrcBuf)
418{
419 AssertPtr(pSrcBuf);
420 int rc = VINF_SUCCESS;
421 if (cDstParms != pSrcBuf->uParmCount)
422 {
423 LogFlowFunc(("Parameter count does not match (got %u, expected %u)\n",
424 cDstParms, pSrcBuf->uParmCount));
425 rc = VERR_INVALID_PARAMETER;
426 }
427 else
428 {
429 for (uint32_t i = 0; i < pSrcBuf->uParmCount; i++)
430 {
431 if (paDstParms[i].type != pSrcBuf->pParms[i].type)
432 {
433 LogFlowFunc(("Parameter %u type mismatch (got %u, expected %u)\n",
434 i, paDstParms[i].type, pSrcBuf->pParms[i].type));
435 rc = VERR_INVALID_PARAMETER;
436 }
437 else
438 {
439 switch (pSrcBuf->pParms[i].type)
440 {
441 case VBOX_HGCM_SVC_PARM_32BIT:
442 paDstParms[i].u.uint32 = pSrcBuf->pParms[i].u.uint32;
443 break;
444
445 case VBOX_HGCM_SVC_PARM_PTR:
446 {
447 if (!pSrcBuf->pParms[i].u.pointer.size)
448 continue; /* Only copy buffer if there actually is something to copy. */
449
450 if (!paDstParms[i].u.pointer.addr)
451 rc = VERR_INVALID_PARAMETER;
452
453 if (paDstParms[i].u.pointer.size < pSrcBuf->pParms[i].u.pointer.size)
454 rc = VERR_BUFFER_OVERFLOW;
455
456 if (RT_SUCCESS(rc))
457 {
458 memcpy(paDstParms[i].u.pointer.addr,
459 pSrcBuf->pParms[i].u.pointer.addr,
460 pSrcBuf->pParms[i].u.pointer.size);
461 }
462
463 break;
464 }
465
466 case VBOX_HGCM_SVC_PARM_64BIT:
467 /* Fall through is intentional. */
468 default:
469 LogFlowFunc(("Parameter %u of type %u is not supported yet\n",
470 i, pSrcBuf->pParms[i].type));
471 rc = VERR_NOT_SUPPORTED;
472 break;
473 }
474 }
475
476 if (RT_FAILURE(rc))
477 {
478 LogFlowFunc(("Parameter %u invalid (rc=%Rrc), refusing\n",
479 i, rc));
480 break;
481 }
482 }
483 }
484 return rc;
485}
486
487/**
488 * Handles a client which just connected.
489 *
490 * @return IPRT status code.
491 * @param u32ClientID
492 * @param pvClient
493 */
494int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
495{
496 LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
497 if (mNumClients < UINT32_MAX)
498 mNumClients++;
499 else
500 AssertMsgFailed(("Max. number of clients reached\n"));
501 return VINF_SUCCESS;
502}
503
504/**
505 * Handles a client which disconnected. This functiond does some
506 * internal cleanup as well as sends notifications to the host so
507 * that the host can do the same (if required).
508 *
509 * @return IPRT status code.
510 * @param u32ClientID The client's ID of which disconnected.
511 * @param pvClient User data, not used at the moment.
512 */
513int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
514{
515 LogFlowFunc(("Client (ID=%u, %u clients total) disconnected\n",
516 u32ClientID, mNumClients));
517 Assert(mNumClients > 0);
518 mNumClients--;
519
520 /* If this was the last connected (guest) client we need to
521 * unblock all eventually queued up (waiting) host calls. */
522 bool fAllClientsDisconnected = mNumClients == 0;
523 if (fAllClientsDisconnected)
524 LogFlowFunc(("No connected clients left, notifying all queued up callbacks\n"));
525
526 /*
527 * Throw out all stale clients.
528 */
529 int rc = VINF_SUCCESS;
530
531 CallListIter itCall = mClientWaiterList.begin();
532 while (itCall != mClientWaiterList.end())
533 {
534 if (itCall->mClientID == u32ClientID)
535 {
536 itCall = mClientWaiterList.erase(itCall);
537 }
538 else
539 itCall++;
540 }
541
542 ClientContextsListIter itContextList = mClientContextsList.begin();
543 while ( itContextList != mClientContextsList.end()
544 && RT_SUCCESS(rc))
545 {
546 /*
547 * Unblock/call back all queued items of the specified client
548 * or for all items in case there is no waiting client around
549 * anymore.
550 */
551 if ( itContextList->mClientID == u32ClientID
552 || fAllClientsDisconnected)
553 {
554 std::list< uint32_t >::iterator itContext = itContextList->mContextList.begin();
555 while (itContext != itContextList->mContextList.end())
556 {
557 uint32_t uContextID = (*itContext);
558
559 /*
560 * Notify the host that clients with u32ClientID are no longer
561 * around and need to be cleaned up (canceling waits etc).
562 */
563 LogFlowFunc(("Notifying CID=%u of disconnect ...\n", uContextID));
564 rc = cancelHostCmd(uContextID);
565 if (RT_FAILURE(rc))
566 {
567 LogFlowFunc(("Cancelling of CID=%u failed with rc=%Rrc\n",
568 uContextID, rc));
569 /* Keep going. */
570 }
571
572 itContext++;
573 }
574 itContextList = mClientContextsList.erase(itContextList);
575 }
576 else
577 itContextList++;
578 }
579
580 if (fAllClientsDisconnected)
581 {
582 /*
583 * If all clients disconnected we also need to make sure that all buffered
584 * host commands need to be notified, because Main is waiting a notification
585 * via a (multi stage) progress object.
586 */
587 HostCmdListIter itHostCmd;
588 for (itHostCmd = mHostCmds.begin(); itHostCmd != mHostCmds.end(); itHostCmd++)
589 {
590 rc = cancelHostCmd(itHostCmd->mContextID);
591 if (RT_FAILURE(rc))
592 {
593 LogFlowFunc(("Cancelling of buffered CID=%u failed with rc=%Rrc\n",
594 itHostCmd->mContextID, rc));
595 /* Keep going. */
596 }
597
598 paramBufferFree(&itHostCmd->mParmBuf);
599 }
600
601 mHostCmds.clear();
602 }
603
604 return rc;
605}
606
607/**
608 * Assigns a specified host command to a client.
609 *
610 * @return IPRT status code.
611 * @param pCmd Host command to send.
612 * @param callHandle Call handle of the client to send the command to.
613 * @param cParms Number of parameters.
614 * @param paParms Array of parameters.
615 */
616int Service::assignHostCmdToGuest(const VBOXGUESTCTRLHOSTCMD *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
617{
618 AssertPtrReturn(pCmd, VERR_INVALID_POINTER);
619 int rc;
620
621 /* Does the current host command need more parameter space which
622 * the client does not provide yet? */
623 if (pCmd->mParmBuf.uParmCount > cParms)
624 {
625 paParms[0].setUInt32(pCmd->mParmBuf.uMsg); /* Message ID */
626 paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */
627
628 /*
629 * So this call apparently failed because the guest wanted to peek
630 * how much parameters it has to supply in order to successfully retrieve
631 * this command. Let's tell him so!
632 */
633 rc = VERR_TOO_MUCH_DATA;
634 }
635 else
636 {
637 rc = paramBufferAssign(paParms, cParms, &pCmd->mParmBuf);
638
639 /* Has there been enough parameter space but the wrong parameter types
640 * were submitted -- maybe the client was just asking for the next upcoming
641 * host message?
642 *
643 * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
644 * in every case. */
645 if (RT_FAILURE(rc))
646 rc = VERR_TOO_MUCH_DATA;
647 }
648
649 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
650 return rc;
651}
652
653/**
654 * Either fills in parameters from a pending host command into our guest context or
655 * defer the guest call until we have something from the host.
656 *
657 * @return IPRT status code.
658 * @param u32ClientID The client's ID.
659 * @param callHandle The client's call handle.
660 * @param cParms Number of parameters.
661 * @param paParms Array of parameters.
662 */
663int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
664 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
665{
666 int rc = VINF_SUCCESS;
667
668 /*
669 * Lookup client in our list so that we can assign the context ID of
670 * a command to that client.
671 */
672 std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin();
673 while (it != mClientContextsList.rend())
674 {
675 if (it->mClientID == u32ClientID)
676 break;
677 it++;
678 }
679
680 /* Not found? Add client to list. */
681 if (it == mClientContextsList.rend())
682 {
683 mClientContextsList.push_back(ClientContexts(u32ClientID));
684 it = mClientContextsList.rbegin();
685 }
686 Assert(it != mClientContextsList.rend());
687
688 /*
689 * If host command list is empty (nothing to do right now) just
690 * defer the call until we got something to do (makes the client
691 * wait, depending on the flags set).
692 */
693 if (mHostCmds.empty()) /* If command list is empty, defer ... */
694 {
695 mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms));
696 rc = VINF_HGCM_ASYNC_EXECUTE;
697 }
698 else
699 {
700 /*
701 * Get the next unassigned host command in the list.
702 */
703 VBOXGUESTCTRLHOSTCMD &curCmd = mHostCmds.front();
704 rc = assignHostCmdToGuest(&curCmd, callHandle, cParms, paParms);
705 if (RT_SUCCESS(rc))
706 {
707 /* Remember which client processes which context (for
708 * later reference & cleanup). */
709 /// @todo r=bird: check if already in the list.
710 /// @todo Use a map instead of a list?
711 it->mContextList.push_back(curCmd.mContextID);
712
713 /* Only if the guest really got and understood the message remove it from the list. */
714 paramBufferFree(&curCmd.mParmBuf);
715 mHostCmds.pop_front();
716 }
717 else
718 {
719 bool fRemoveCmd = false;
720 uint32_t uTries = curCmd.mTries++;
721
722 /* If the client understood the message but supplied too little buffer space
723 * don't send this message again and drop it after 3 unsuccessful attempts.
724 * The host then should take care of next actions (maybe retry it with a smaller buffer). */
725 if ( rc == VERR_BUFFER_OVERFLOW
726 && uTries >= 3)
727 {
728 fRemoveCmd = true;
729 }
730 /* Client did not understand the message or something else weird happened. Try again one
731 * more time and drop it if it didn't get handled then. */
732 else if (uTries > 1)
733 fRemoveCmd = true;
734
735 if (fRemoveCmd)
736 {
737 paramBufferFree(&curCmd.mParmBuf);
738 mHostCmds.pop_front();
739 }
740 }
741 }
742 return rc;
743}
744
745/**
746 * Cancels a buffered host command to unblock waits on Main side
747 * (via (multi stage) progress objects.
748 *
749 * @return IPRT status code.
750 * @param u32ContextID Context ID of host command to cancel.
751 */
752int Service::cancelHostCmd(uint32_t u32ContextID)
753{
754 Assert(mpfnHostCallback);
755
756 LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID));
757
758 CALLBACKDATACLIENTDISCONNECTED data;
759 data.hdr.u32Magic = CALLBACKDATAMAGIC_CLIENT_DISCONNECTED;
760 data.hdr.u32ContextID = u32ContextID;
761
762 AssertPtr(mpfnHostCallback);
763 AssertPtr(mpvHostData);
764
765 return mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data));
766}
767
768/**
769 * Client asks itself (in another thread) to cancel all pending waits which are blocking the client
770 * from shutting down / doing something else.
771 *
772 * @return IPRT status code.
773 * @param u32ClientID The client's ID.
774 */
775int Service::cancelPendingWaits(uint32_t u32ClientID)
776{
777 int rc = VINF_SUCCESS;
778 CallListIter it = mClientWaiterList.begin();
779 while (it != mClientWaiterList.end())
780 {
781 if (it->mClientID == u32ClientID)
782 {
783 if (it->mNumParms >= 2)
784 {
785 it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
786 it->mParms[1].setUInt32(0); /* Required parameters for message. */
787 }
788 if (mpHelpers)
789 mpHelpers->pfnCallComplete(it->mHandle, rc);
790 it = mClientWaiterList.erase(it);
791 }
792 else
793 it++;
794 }
795 return rc;
796}
797
798/**
799 * Notifies the host (using low-level HGCM callbacks) about an event
800 * which was sent from the client.
801 *
802 * @return IPRT status code.
803 * @param eFunction Function (event) that occured.
804 * @param cParms Number of parameters.
805 * @param paParms Array of parameters.
806 */
807int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
808{
809 LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
810 eFunction, cParms, paParms));
811 int rc = VINF_SUCCESS;
812 if ( eFunction == GUEST_EXEC_SEND_STATUS
813 && cParms == 5)
814 {
815 CALLBACKDATAEXECSTATUS data;
816 data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_STATUS;
817 paParms[0].getUInt32(&data.hdr.u32ContextID);
818
819 paParms[1].getUInt32(&data.u32PID);
820 paParms[2].getUInt32(&data.u32Status);
821 paParms[3].getUInt32(&data.u32Flags);
822 paParms[4].getPointer(&data.pvData, &data.cbData);
823
824 if (mpfnHostCallback)
825 rc = mpfnHostCallback(mpvHostData, eFunction,
826 (void *)(&data), sizeof(data));
827 }
828 else if ( eFunction == GUEST_EXEC_SEND_OUTPUT
829 && cParms == 5)
830 {
831 CALLBACKDATAEXECOUT data;
832 data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_OUT;
833 paParms[0].getUInt32(&data.hdr.u32ContextID);
834
835 paParms[1].getUInt32(&data.u32PID);
836 paParms[2].getUInt32(&data.u32HandleId);
837 paParms[3].getUInt32(&data.u32Flags);
838 paParms[4].getPointer(&data.pvData, &data.cbData);
839
840 if (mpfnHostCallback)
841 rc = mpfnHostCallback(mpvHostData, eFunction,
842 (void *)(&data), sizeof(data));
843 }
844 else if ( eFunction == GUEST_EXEC_SEND_INPUT_STATUS
845 && cParms == 5)
846 {
847 CALLBACKDATAEXECINSTATUS data;
848 data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_IN_STATUS;
849 paParms[0].getUInt32(&data.hdr.u32ContextID);
850
851 paParms[1].getUInt32(&data.u32PID);
852 paParms[2].getUInt32(&data.u32Status);
853 paParms[3].getUInt32(&data.u32Flags);
854 paParms[4].getUInt32(&data.cbProcessed);
855
856 if (mpfnHostCallback)
857 rc = mpfnHostCallback(mpvHostData, eFunction,
858 (void *)(&data), sizeof(data));
859 }
860 else
861 rc = VERR_NOT_SUPPORTED;
862 LogFlowFunc(("returning %Rrc\n", rc));
863 return rc;
864}
865
866/**
867 * Processes a command receiveed from the host side and re-routes it to
868 * a connect client on the guest.
869 *
870 * @return IPRT status code.
871 * @param eFunction Function code to process.
872 * @param cParms Number of parameters.
873 * @param paParms Array of parameters.
874 */
875int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
876{
877 /*
878 * If no client is connected at all we don't buffer any host commands
879 * and immediately return an error to the host. This avoids the host
880 * waiting for a response from the guest side in case VBoxService on
881 * the guest is not running/system is messed up somehow.
882 */
883 if (mNumClients == 0)
884 return VERR_NOT_FOUND;
885 VBOXGUESTCTRLHOSTCMD newCmd;
886 int rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms);
887 if ( RT_SUCCESS(rc)
888 && cParms) /* Make sure we at least get one parameter (that is, the context ID). */
889 {
890 /*
891 * Assume that the context ID *always* is the first parameter,
892 * assign the context ID to the command.
893 */
894 newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID);
895 }
896 else if (!cParms)
897 rc = VERR_INVALID_PARAMETER;
898
899 if (RT_SUCCESS(rc))
900 {
901 LogFlowFunc(("Handling host command CID = %u\n",
902 newCmd.mContextID));
903
904 bool fProcessed = false;
905
906 /* Can we wake up a waiting client on guest? */
907 if (!mClientWaiterList.empty())
908 {
909 ClientWaiter guest = mClientWaiterList.front();
910 rc = assignHostCmdToGuest(&newCmd,
911 guest.mHandle, guest.mNumParms, guest.mParms);
912
913 /* In any case the client did something, so wake up and remove from list. */
914 AssertPtr(mpHelpers);
915 mpHelpers->pfnCallComplete(guest.mHandle, rc);
916 mClientWaiterList.pop_front();
917
918 /*
919 * If we got back an error (like VERR_TOO_MUCH_DATA or VERR_BUFFER_OVERFLOW)
920 * we buffer the host command in the next block and return success to the host.
921 */
922 if (RT_FAILURE(rc))
923 {
924 rc = VINF_SUCCESS;
925 }
926 else /* If command was understood by the client, free and remove from host commands list. */
927 {
928 LogFlowFunc(("Host command CID = %u processed with rc=%Rrc\n",
929 newCmd.mContextID, rc));
930
931 paramBufferFree(&newCmd.mParmBuf);
932 }
933 }
934
935 if (!fProcessed)
936 {
937 LogFlowFunc(("Buffering host command CID = %u (rc=%Rrc)\n",
938 newCmd.mContextID, rc));
939
940 mHostCmds.push_back(newCmd);
941 }
942 }
943
944 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
945 return rc;
946}
947
948/**
949 * Handle an HGCM service call.
950 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
951 * @note All functions which do not involve an unreasonable delay will be
952 * handled synchronously. If needed, we will add a request handler
953 * thread in future for those which do.
954 *
955 * @thread HGCM
956 */
957void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
958 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
959 VBOXHGCMSVCPARM paParms[])
960{
961 int rc = VINF_SUCCESS;
962 LogFlowFunc(("u32ClientID = %u, fn = %u, cParms = %u, paParms = 0x%p\n",
963 u32ClientID, eFunction, cParms, paParms));
964 try
965 {
966 switch (eFunction)
967 {
968 /*
969 * The guest asks the host for the next message to process.
970 */
971 case GUEST_GET_HOST_MSG:
972 LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
973 rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms);
974 break;
975
976 /*
977 * The guest wants to shut down and asks us (this service) to cancel
978 * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
979 * guest can gracefully shut down.
980 */
981 case GUEST_CANCEL_PENDING_WAITS:
982 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
983 rc = cancelPendingWaits(u32ClientID);
984 break;
985
986 /*
987 * For all other regular commands we call our notifyHost
988 * function. If the current command does not support notifications
989 * notifyHost will return VERR_NOT_SUPPORTED.
990 */
991 default:
992 rc = notifyHost(eFunction, cParms, paParms);
993 break;
994 }
995 if (rc != VINF_HGCM_ASYNC_EXECUTE)
996 {
997 /* Tell the client that the call is complete (unblocks waiting). */
998 AssertPtr(mpHelpers);
999 mpHelpers->pfnCallComplete(callHandle, rc);
1000 }
1001 }
1002 catch (std::bad_alloc)
1003 {
1004 rc = VERR_NO_MEMORY;
1005 }
1006 LogFlowFunc(("rc = %Rrc\n", rc));
1007}
1008
1009/**
1010 * Service call handler for the host.
1011 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
1012 * @thread hgcm
1013 */
1014int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1015{
1016 int rc = VERR_NOT_SUPPORTED;
1017 LogFlowFunc(("fn = %u, cParms = %u, paParms = 0x%p\n",
1018 eFunction, cParms, paParms));
1019 try
1020 {
1021 rc = processHostCmd(eFunction, cParms, paParms);
1022 }
1023 catch (std::bad_alloc)
1024 {
1025 rc = VERR_NO_MEMORY;
1026 }
1027
1028 LogFlowFunc(("rc = %Rrc\n", rc));
1029 return rc;
1030}
1031
1032int Service::uninit()
1033{
1034 Assert(mHostCmds.empty());
1035
1036 return VINF_SUCCESS;
1037}
1038
1039} /* namespace guestControl */
1040
1041using guestControl::Service;
1042
1043/**
1044 * @copydoc VBOXHGCMSVCLOAD
1045 */
1046extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
1047{
1048 int rc = VINF_SUCCESS;
1049
1050 LogFlowFunc(("ptable = %p\n", ptable));
1051
1052 if (!VALID_PTR(ptable))
1053 {
1054 rc = VERR_INVALID_PARAMETER;
1055 }
1056 else
1057 {
1058 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1059
1060 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1061 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1062 {
1063 rc = VERR_VERSION_MISMATCH;
1064 }
1065 else
1066 {
1067 std::auto_ptr<Service> apService;
1068 /* No exceptions may propagate outside. */
1069 try {
1070 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1071 } catch (int rcThrown) {
1072 rc = rcThrown;
1073 } catch (...) {
1074 rc = VERR_UNRESOLVED_ERROR;
1075 }
1076
1077 if (RT_SUCCESS(rc))
1078 {
1079 /*
1080 * We don't need an additional client data area on the host,
1081 * because we're a class which can have members for that :-).
1082 */
1083 ptable->cbClient = 0;
1084
1085 /* Register functions. */
1086 ptable->pfnUnload = Service::svcUnload;
1087 ptable->pfnConnect = Service::svcConnect;
1088 ptable->pfnDisconnect = Service::svcDisconnect;
1089 ptable->pfnCall = Service::svcCall;
1090 ptable->pfnHostCall = Service::svcHostCall;
1091 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1092 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1093 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1094
1095 /* Service specific initialization. */
1096 ptable->pvService = apService.release();
1097 }
1098 }
1099 }
1100
1101 LogFlowFunc(("returning %Rrc\n", rc));
1102 return rc;
1103}
1104
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