VirtualBox

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

Last change on this file since 37224 was 36872, checked in by vboxsync, 13 years ago

HostServices/GuestCtrl: Fixed several bugs revealed by upcoming testcase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.7 KB
Line 
1/* $Id: service.cpp 36872 2011-04-28 08:53:57Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2010 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 a service. A client is represented by its 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 */
130struct HostCmd
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 HostCmd() : mContextID(0), mTries(0) {}
143};
144/** The host cmd list + iterator type */
145typedef std::list< HostCmd > HostCmdList;
146typedef std::list< HostCmd >::iterator HostCmdListIter;
147typedef std::list< HostCmd >::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(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
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 sendHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
288 int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
289 int cancelPendingWaits(uint32_t u32ClientID);
290 int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
291 int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
292 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
293 void *pvClient, uint32_t eFunction, uint32_t cParms,
294 VBOXHGCMSVCPARM paParms[]);
295 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
296 int uninit();
297};
298
299
300/**
301 * Stores a HGCM request in an internal buffer. Needs to be free'd using paramBufferFree().
302 *
303 * @return IPRT status code.
304 * @param pBuf Buffer to store the HGCM request into.
305 * @param uMsg Message type.
306 * @param cParms Number of parameters of HGCM request.
307 * @param paParms Array of parameters of HGCM request.
308 */
309int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
310{
311 AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
312 if (cParms)
313 AssertPtrReturn(paParms, VERR_INVALID_POINTER);
314
315 /* Paranoia. */
316 if (cParms > 256)
317 cParms = 256;
318
319 int rc = VINF_SUCCESS;
320
321 /*
322 * Don't verify anything here (yet), because this function only buffers
323 * the HGCM data into an internal structure and reaches it back to the guest (client)
324 * in an unmodified state.
325 */
326 pBuf->uMsg = uMsg;
327 pBuf->uParmCount = cParms;
328 if (pBuf->uParmCount)
329 {
330 pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
331 if (NULL == pBuf->pParms)
332 rc = VERR_NO_MEMORY;
333 }
334
335 if (RT_SUCCESS(rc))
336 {
337 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
338 {
339 pBuf->pParms[i].type = paParms[i].type;
340 switch (paParms[i].type)
341 {
342 case VBOX_HGCM_SVC_PARM_32BIT:
343 pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
344 break;
345
346 case VBOX_HGCM_SVC_PARM_64BIT:
347 /* Not supported yet. */
348 break;
349
350 case VBOX_HGCM_SVC_PARM_PTR:
351 pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
352 if (pBuf->pParms[i].u.pointer.size > 0)
353 {
354 pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
355 if (NULL == pBuf->pParms[i].u.pointer.addr)
356 {
357 rc = VERR_NO_MEMORY;
358 break;
359 }
360 else
361 memcpy(pBuf->pParms[i].u.pointer.addr,
362 paParms[i].u.pointer.addr,
363 pBuf->pParms[i].u.pointer.size);
364 }
365 else
366 {
367 /* Size is 0 -- make sure we don't have any pointer. */
368 pBuf->pParms[i].u.pointer.addr = NULL;
369 }
370 break;
371
372 default:
373 break;
374 }
375 if (RT_FAILURE(rc))
376 break;
377 }
378 }
379 return rc;
380}
381
382/**
383 * Frees a buffered HGCM request.
384 *
385 * @return IPRT status code.
386 * @param pBuf Parameter buffer to free.
387 */
388void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
389{
390 AssertPtr(pBuf);
391 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
392 {
393 switch (pBuf->pParms[i].type)
394 {
395 case VBOX_HGCM_SVC_PARM_PTR:
396 if (pBuf->pParms[i].u.pointer.size > 0)
397 RTMemFree(pBuf->pParms[i].u.pointer.addr);
398 break;
399 }
400 }
401 if (pBuf->uParmCount)
402 {
403 RTMemFree(pBuf->pParms);
404 pBuf->uParmCount = 0;
405 }
406}
407
408/**
409 * Assigns data from a buffered HGCM request to the current HGCM request.
410 *
411 * @return IPRT status code.
412 * @param pBuf Parameter buffer to assign.
413 * @param cParms Number of parameters the HGCM request can handle.
414 * @param paParms Array of parameters of HGCM request to fill the data into.
415 */
416int Service::paramBufferAssign(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
417{
418 AssertPtr(pBuf);
419 int rc = VINF_SUCCESS;
420 if (cParms != pBuf->uParmCount)
421 {
422 LogFlowFunc(("Parameter count does not match: %u (host) vs. %u (guest)\n",
423 pBuf->uParmCount, cParms));
424 rc = VERR_INVALID_PARAMETER;
425 }
426 else
427 {
428 /** @todo Add check to verify if the HGCM request is the same *type* as the buffered one! */
429 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
430 {
431 paParms[i].type = pBuf->pParms[i].type;
432 switch (paParms[i].type)
433 {
434 case VBOX_HGCM_SVC_PARM_32BIT:
435 paParms[i].u.uint32 = pBuf->pParms[i].u.uint32;
436 break;
437
438 case VBOX_HGCM_SVC_PARM_64BIT:
439 /* Not supported yet. */
440 break;
441
442 case VBOX_HGCM_SVC_PARM_PTR:
443 if (paParms[i].u.pointer.size >= pBuf->pParms[i].u.pointer.size)
444 {
445 /* Only copy buffer if there actually is something to copy. */
446 if (pBuf->pParms[i].u.pointer.size)
447 {
448 AssertPtr(pBuf->pParms[i].u.pointer.addr);
449 memcpy(paParms[i].u.pointer.addr,
450 pBuf->pParms[i].u.pointer.addr,
451 pBuf->pParms[i].u.pointer.size);
452 }
453 }
454 else
455 rc = VERR_BUFFER_OVERFLOW;
456 break;
457
458 default:
459 break;
460 }
461 }
462 }
463 return rc;
464}
465
466/**
467 * Handles a client which just connected.
468 *
469 * @return IPRT status code.
470 * @param u32ClientID
471 * @param pvClient
472 */
473int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
474{
475 LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
476 if (mNumClients < UINT32_MAX)
477 mNumClients++;
478 else
479 AssertMsgFailed(("Max. number of clients reached\n"));
480 return VINF_SUCCESS;
481}
482
483/**
484 * Handles a client which disconnected. This functiond does some
485 * internal cleanup as well as sends notifications to the host so
486 * that the host can do the same (if required).
487 *
488 * @return IPRT status code.
489 * @param u32ClientID The client's ID of which disconnected.
490 * @param pvClient User data, not used at the moment.
491 */
492int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
493{
494 LogFlowFunc(("Client (%ld) disconnected\n", u32ClientID));
495 Assert(mNumClients > 0);
496 mNumClients--;
497
498 /*
499 * Throw out all stale clients.
500 */
501 int rc = VINF_SUCCESS;
502
503 CallListIter itCall = mClientWaiterList.begin();
504 while (itCall != mClientWaiterList.end())
505 {
506 if (itCall->mClientID == u32ClientID)
507 {
508 itCall = mClientWaiterList.erase(itCall);
509 }
510 else
511 itCall++;
512 }
513
514 ClientContextsListIter it = mClientContextsList.begin();
515 while ( it != mClientContextsList.end()
516 && RT_SUCCESS(rc))
517 {
518 if (it->mClientID == u32ClientID)
519 {
520 std::list< uint32_t >::iterator itContext = it->mContextList.begin();
521 while ( itContext != it->mContextList.end()
522 && RT_SUCCESS(rc))
523 {
524 LogFlowFunc(("Notifying host context %u of disconnect ...\n", (*itContext)));
525
526 /*
527 * Notify the host that clients with u32ClientID are no longer
528 * around and need to be cleaned up (canceling waits etc).
529 */
530 if (mpfnHostCallback)
531 {
532 CALLBACKDATACLIENTDISCONNECTED data;
533 data.hdr.u32Magic = CALLBACKDATAMAGICCLIENTDISCONNECTED;
534 data.hdr.u32ContextID = (*itContext);
535 rc = mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data));
536 if (RT_FAILURE(rc))
537 LogFlowFunc(("Notification of host context %u failed with %Rrc\n", rc));
538 }
539 itContext++;
540 }
541 it = mClientContextsList.erase(it);
542 }
543 else
544 it++;
545 }
546 return rc;
547}
548
549/**
550 * Sends a specified host command to a client.
551 *
552 * @return IPRT status code.
553 * @param pCmd Host comamnd to send.
554 * @param callHandle Call handle of the client to send the command to.
555 * @param cParms Number of parameters.
556 * @param paParms Array of parameters.
557 */
558int Service::sendHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
559{
560 AssertPtr(pCmd);
561 int rc;
562
563 /* Sufficient parameter space? */
564 if (pCmd->mParmBuf.uParmCount > cParms)
565 {
566 paParms[0].setUInt32(pCmd->mParmBuf.uMsg); /* Message ID */
567 paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */
568
569 /*
570 * So this call apparently failed because the guest wanted to peek
571 * how much parameters it has to supply in order to successfully retrieve
572 * this command. Let's tell him so!
573 */
574 rc = VERR_TOO_MUCH_DATA;
575 }
576 else
577 {
578 rc = paramBufferAssign(&pCmd->mParmBuf, cParms, paParms);
579 }
580 return rc;
581}
582
583/**
584 * Either fills in parameters from a pending host command into our guest context or
585 * defer the guest call until we have something from the host.
586 *
587 * @return IPRT status code.
588 * @param u32ClientID The client's ID.
589 * @param callHandle The client's call handle.
590 * @param cParms Number of parameters.
591 * @param paParms Array of parameters.
592 */
593int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
594 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
595{
596 int rc = VINF_SUCCESS;
597
598 /*
599 * Lookup client in our list so that we can assign the context ID of
600 * a command to that client.
601 */
602 std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin();
603 while (it != mClientContextsList.rend())
604 {
605 if (it->mClientID == u32ClientID)
606 break;
607 it++;
608 }
609
610 /* Not found? Add client to list. */
611 if (it == mClientContextsList.rend())
612 {
613 mClientContextsList.push_back(ClientContexts(u32ClientID));
614 it = mClientContextsList.rbegin();
615 }
616 Assert(it != mClientContextsList.rend());
617
618 /*
619 * If host command list is empty (nothing to do right now) just
620 * defer the call until we got something to do (makes the client
621 * wait, depending on the flags set).
622 */
623 if (mHostCmds.empty()) /* If command list is empty, defer ... */
624 {
625 mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms));
626 rc = VINF_HGCM_ASYNC_EXECUTE;
627 }
628 else
629 {
630 /*
631 * Get the next unassigned host command in the list.
632 */
633 HostCmd curCmd = mHostCmds.front();
634 rc = sendHostCmdToGuest(&curCmd, callHandle, cParms, paParms);
635 if (RT_SUCCESS(rc))
636 {
637 /* Remember which client processes which context (for
638 * later reference & cleanup). */
639 Assert(curCmd.mContextID > 0);
640 /// @todo r=bird: check if already in the list.
641 it->mContextList.push_back(curCmd.mContextID);
642
643 /* Only if the guest really got and understood the message remove it from the list. */
644 paramBufferFree(&curCmd.mParmBuf);
645 mHostCmds.pop_front();
646 }
647 else if (rc == VERR_BUFFER_OVERFLOW)
648 {
649 /* If the client understood the message but supplied too little buffer space
650 * don't send this message again and drop it after 3 unsuccessful attempts.
651 * The host then should take care of next actions (maybe retry it with a smaller buffer). */
652 if (++curCmd.mTries >= 3)
653 {
654 paramBufferFree(&curCmd.mParmBuf);
655 mHostCmds.pop_front();
656 }
657 }
658 else
659 {
660 /* Client did not understand the message or something else weird happened. Try again one
661 * more time and drop it if it didn't get handled then. */
662 if (++curCmd.mTries > 1)
663 {
664 paramBufferFree(&curCmd.mParmBuf);
665 mHostCmds.pop_front();
666 }
667 }
668 }
669 return rc;
670}
671
672/**
673 * Client asks itself (in another thread) to cancel all pending waits which are blocking the client
674 * from shutting down / doing something else.
675 *
676 * @return IPRT status code.
677 * @param u32ClientID The client's ID.
678 */
679int Service::cancelPendingWaits(uint32_t u32ClientID)
680{
681 int rc = VINF_SUCCESS;
682 CallListIter it = mClientWaiterList.begin();
683 while (it != mClientWaiterList.end())
684 {
685 if (it->mClientID == u32ClientID)
686 {
687 if (it->mNumParms >= 2)
688 {
689 it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
690 it->mParms[1].setUInt32(0); /* Required parameters for message. */
691 }
692 if (mpHelpers)
693 mpHelpers->pfnCallComplete(it->mHandle, rc);
694 it = mClientWaiterList.erase(it);
695 }
696 else
697 it++;
698 }
699 return rc;
700}
701
702/**
703 * Notifies the host (using low-level HGCM callbacks) about an event
704 * which was sent from the client.
705 *
706 * @return IPRT status code.
707 * @param eFunction Function (event) that occured.
708 * @param cParms Number of parameters.
709 * @param paParms Array of parameters.
710 */
711int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
712{
713 LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
714 eFunction, cParms, paParms));
715 int rc = VINF_SUCCESS;
716 if ( eFunction == GUEST_EXEC_SEND_STATUS
717 && cParms == 5)
718 {
719 CALLBACKDATAEXECSTATUS data;
720 data.hdr.u32Magic = CALLBACKDATAMAGICEXECSTATUS;
721 paParms[0].getUInt32(&data.hdr.u32ContextID);
722
723 paParms[1].getUInt32(&data.u32PID);
724 paParms[2].getUInt32(&data.u32Status);
725 paParms[3].getUInt32(&data.u32Flags);
726 paParms[4].getPointer(&data.pvData, &data.cbData);
727
728 if (mpfnHostCallback)
729 rc = mpfnHostCallback(mpvHostData, eFunction,
730 (void *)(&data), sizeof(data));
731 }
732 else if ( eFunction == GUEST_EXEC_SEND_OUTPUT
733 && cParms == 5)
734 {
735 CALLBACKDATAEXECOUT data;
736 data.hdr.u32Magic = CALLBACKDATAMAGICEXECOUT;
737 paParms[0].getUInt32(&data.hdr.u32ContextID);
738
739 paParms[1].getUInt32(&data.u32PID);
740 paParms[2].getUInt32(&data.u32HandleId);
741 paParms[3].getUInt32(&data.u32Flags);
742 paParms[4].getPointer(&data.pvData, &data.cbData);
743
744 if (mpfnHostCallback)
745 rc = mpfnHostCallback(mpvHostData, eFunction,
746 (void *)(&data), sizeof(data));
747 }
748 else if ( eFunction == GUEST_EXEC_SEND_INPUT_STATUS
749 && cParms == 5)
750 {
751 CALLBACKDATAEXECINSTATUS data;
752 data.hdr.u32Magic = CALLBACKDATAMAGICEXECINSTATUS;
753 paParms[0].getUInt32(&data.hdr.u32ContextID);
754
755 paParms[1].getUInt32(&data.u32PID);
756 paParms[2].getUInt32(&data.u32Status);
757 paParms[3].getUInt32(&data.u32Flags);
758 paParms[4].getUInt32(&data.cbProcessed);
759
760 if (mpfnHostCallback)
761 rc = mpfnHostCallback(mpvHostData, eFunction,
762 (void *)(&data), sizeof(data));
763 }
764 else
765 rc = VERR_NOT_SUPPORTED;
766 LogFlowFunc(("returning %Rrc\n", rc));
767 return rc;
768}
769
770/**
771 * Processes a command receiveed from the host side and re-routes it to
772 * a connect client on the guest.
773 *
774 * @return IPRT status code.
775 * @param eFunction Function code to process.
776 * @param cParms Number of parameters.
777 * @param paParms Array of parameters.
778 */
779int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
780{
781 /*
782 * If no client is connected at all we don't buffer any host commands
783 * and immediately return an error to the host. This avoids the host
784 * waiting for a response from the guest side in case VBoxService on
785 * the guest is not running/system is messed up somehow.
786 */
787 if (mNumClients == 0)
788 return VERR_NOT_FOUND;
789 HostCmd newCmd;
790 int rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms);
791 if (RT_SUCCESS(rc) && cParms)
792 {
793 /*
794 * Assume that the context ID *always* is the first parameter,
795 * assign the context ID to the command.
796 */
797 newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID);
798 Assert(newCmd.mContextID > 0);
799 }
800
801 if (RT_SUCCESS(rc))
802 {
803 bool fProcessed = false;
804
805 /* Can we wake up a waiting client on guest? */
806 if (!mClientWaiterList.empty())
807 {
808 ClientWaiter guest = mClientWaiterList.front();
809 rc = sendHostCmdToGuest(&newCmd,
810 guest.mHandle, guest.mNumParms, guest.mParms);
811
812 /* In any case the client did something, so wake up and remove from list. */
813 AssertPtr(mpHelpers);
814 mpHelpers->pfnCallComplete(guest.mHandle, rc);
815 mClientWaiterList.pop_front();
816
817 /*
818 * If we got back an error (like VERR_TOO_MUCH_DATA or VERR_BUFFER_OVERFLOW)
819 * we buffer the host command in the next block and return success to the host.
820 */
821 if (RT_FAILURE(rc))
822 {
823 rc = VINF_SUCCESS;
824 }
825 else /* If command was understood by the client, free and remove from host commands list. */
826 {
827 paramBufferFree(&newCmd.mParmBuf);
828 fProcessed = true;
829 }
830 }
831
832 /* If not processed, buffer it ... */
833 if (!fProcessed)
834 {
835 mHostCmds.push_back(newCmd);
836#if 0
837 /* Limit list size by deleting oldest element. */
838 if (mHostCmds.size() > 256) /** @todo Use a define! */
839 mHostCmds.pop_front();
840#endif
841 }
842 }
843 return rc;
844}
845
846/**
847 * Handle an HGCM service call.
848 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
849 * @note All functions which do not involve an unreasonable delay will be
850 * handled synchronously. If needed, we will add a request handler
851 * thread in future for those which do.
852 *
853 * @thread HGCM
854 */
855void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
856 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
857 VBOXHGCMSVCPARM paParms[])
858{
859 int rc = VINF_SUCCESS;
860 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
861 u32ClientID, eFunction, cParms, paParms));
862 try
863 {
864 switch (eFunction)
865 {
866 /*
867 * The guest asks the host for the next message to process.
868 */
869 case GUEST_GET_HOST_MSG:
870 LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
871 rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms);
872 break;
873
874 /*
875 * The guest wants to shut down and asks us (this service) to cancel
876 * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
877 * guest can gracefully shut down.
878 */
879 case GUEST_CANCEL_PENDING_WAITS:
880 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
881 rc = cancelPendingWaits(u32ClientID);
882 break;
883
884 /*
885 * The guest notifies the host that some output at stdout/stderr is available.
886 */
887 case GUEST_EXEC_SEND_OUTPUT:
888 LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
889 rc = notifyHost(eFunction, cParms, paParms);
890 break;
891
892 /*
893 * The guest notifies the host of the executed process status.
894 */
895 case GUEST_EXEC_SEND_STATUS:
896 LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
897 rc = notifyHost(eFunction, cParms, paParms);
898 break;
899
900 case GUEST_EXEC_SEND_INPUT_STATUS:
901 LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
902 rc = notifyHost(eFunction, cParms, paParms);
903 break;
904
905 default:
906 rc = VERR_NOT_SUPPORTED;
907 break;
908 }
909 if (rc != VINF_HGCM_ASYNC_EXECUTE)
910 {
911 /* Tell the client that the call is complete (unblocks waiting). */
912 AssertPtr(mpHelpers);
913 mpHelpers->pfnCallComplete(callHandle, rc);
914 }
915 }
916 catch (std::bad_alloc)
917 {
918 rc = VERR_NO_MEMORY;
919 }
920 LogFlowFunc(("rc = %Rrc\n", rc));
921}
922
923/**
924 * Service call handler for the host.
925 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
926 * @thread hgcm
927 */
928int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
929{
930 int rc = VINF_SUCCESS;
931 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
932 eFunction, cParms, paParms));
933 try
934 {
935 switch (eFunction)
936 {
937 case HOST_CANCEL_PENDING_WAITS:
938 LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n"));
939 rc = processHostCmd(eFunction, cParms, paParms);
940 break;
941
942 /* The host wants to execute something. */
943 case HOST_EXEC_CMD:
944 LogFlowFunc(("HOST_EXEC_CMD\n"));
945 rc = processHostCmd(eFunction, cParms, paParms);
946 break;
947
948 /* The host wants to send something to the
949 * started process' stdin pipe. */
950 case HOST_EXEC_SET_INPUT:
951 LogFlowFunc(("HOST_EXEC_SET_INPUT\n"));
952 rc = processHostCmd(eFunction, cParms, paParms);
953 break;
954
955 case HOST_EXEC_GET_OUTPUT:
956 LogFlowFunc(("HOST_EXEC_GET_OUTPUT\n"));
957 rc = processHostCmd(eFunction, cParms, paParms);
958 break;
959
960 default:
961 rc = VERR_NOT_SUPPORTED;
962 break;
963 }
964 }
965 catch (std::bad_alloc)
966 {
967 rc = VERR_NO_MEMORY;
968 }
969
970 LogFlowFunc(("rc = %Rrc\n", rc));
971 return rc;
972}
973
974int Service::uninit()
975{
976 /* Free allocated buffered host commands. */
977 HostCmdListIter it;
978 for (it = mHostCmds.begin(); it != mHostCmds.end(); it++)
979 paramBufferFree(&it->mParmBuf);
980 mHostCmds.clear();
981
982 return VINF_SUCCESS;
983}
984
985} /* namespace guestControl */
986
987using guestControl::Service;
988
989/**
990 * @copydoc VBOXHGCMSVCLOAD
991 */
992extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
993{
994 int rc = VINF_SUCCESS;
995
996 LogFlowFunc(("ptable = %p\n", ptable));
997
998 if (!VALID_PTR(ptable))
999 {
1000 rc = VERR_INVALID_PARAMETER;
1001 }
1002 else
1003 {
1004 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1005
1006 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1007 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1008 {
1009 rc = VERR_VERSION_MISMATCH;
1010 }
1011 else
1012 {
1013 std::auto_ptr<Service> apService;
1014 /* No exceptions may propagate outside. */
1015 try {
1016 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
1017 } catch (int rcThrown) {
1018 rc = rcThrown;
1019 } catch (...) {
1020 rc = VERR_UNRESOLVED_ERROR;
1021 }
1022
1023 if (RT_SUCCESS(rc))
1024 {
1025 /*
1026 * We don't need an additional client data area on the host,
1027 * because we're a class which can have members for that :-).
1028 */
1029 ptable->cbClient = 0;
1030
1031 /* Register functions. */
1032 ptable->pfnUnload = Service::svcUnload;
1033 ptable->pfnConnect = Service::svcConnect;
1034 ptable->pfnDisconnect = Service::svcDisconnect;
1035 ptable->pfnCall = Service::svcCall;
1036 ptable->pfnHostCall = Service::svcHostCall;
1037 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
1038 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
1039 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
1040
1041 /* Service specific initialization. */
1042 ptable->pvService = apService.release();
1043 }
1044 }
1045 }
1046
1047 LogFlowFunc(("returning %Rrc\n", rc));
1048 return rc;
1049}
1050
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