VirtualBox

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

Last change on this file since 33064 was 33064, checked in by vboxsync, 14 years ago

Guest Execution/Copy: Added support for input handling, added more code for data copying.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.8 KB
Line 
1/* $Id: service.cpp 33064 2010-10-12 13:20:50Z 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: A (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 happend, 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 * mainting 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 contructor. */
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 contructor. */
116 ClientWaiter() : mClientID(0), mHandle(0), mParms(NULL), mNumParms(0) {}
117 /** The normal contructor. */
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 /** Dynamic structure for holding the HGCM parms */
136 VBOXGUESTCTRPARAMBUFFER mParmBuf;
137
138 /** The standard contructor. */
139 HostCmd() : mContextID(0) {}
140};
141/** The host cmd list + iterator type */
142typedef std::list< HostCmd > HostCmdList;
143typedef std::list< HostCmd >::iterator HostCmdListIter;
144typedef std::list< HostCmd >::const_iterator HostCmdListIterConst;
145
146/**
147 * Class containing the shared information service functionality.
148 */
149class Service : public iprt::non_copyable
150{
151private:
152 /** Type definition for use in callback functions. */
153 typedef Service SELF;
154 /** HGCM helper functions. */
155 PVBOXHGCMSVCHELPERS mpHelpers;
156 /*
157 * Callback function supplied by the host for notification of updates
158 * to properties.
159 */
160 PFNHGCMSVCEXT mpfnHostCallback;
161 /** User data pointer to be supplied to the host callback function. */
162 void *mpvHostData;
163 /** The deferred calls list. */
164 ClientWaiterList mClientWaiterList;
165 /** The host command list. */
166 HostCmdList mHostCmds;
167 /** Client contexts list. */
168 ClientContextsList mClientContextsList;
169
170public:
171 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
172 : mpHelpers(pHelpers)
173 , mpfnHostCallback(NULL)
174 , mpvHostData(NULL)
175 {
176 }
177
178 /**
179 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
180 * Simply deletes the service object
181 */
182 static DECLCALLBACK(int) svcUnload (void *pvService)
183 {
184 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
185 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
186 int rc = pSelf->uninit();
187 AssertRC(rc);
188 if (RT_SUCCESS(rc))
189 delete pSelf;
190 return rc;
191 }
192
193 /**
194 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
195 * Stub implementation of pfnConnect and pfnDisconnect.
196 */
197 static DECLCALLBACK(int) svcConnect (void *pvService,
198 uint32_t u32ClientID,
199 void *pvClient)
200 {
201 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
202 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
203 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
204 int rc = pSelf->clientConnect(u32ClientID, pvClient);
205 LogFlowFunc (("rc=%Rrc\n", rc));
206 return rc;
207 }
208
209 /**
210 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
211 * Stub implementation of pfnConnect and pfnDisconnect.
212 */
213 static DECLCALLBACK(int) svcDisconnect (void *pvService,
214 uint32_t u32ClientID,
215 void *pvClient)
216 {
217 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
218 LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
219 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
220 int rc = pSelf->clientDisconnect(u32ClientID, pvClient);
221 LogFlowFunc (("rc=%Rrc\n", rc));
222 return rc;
223 }
224
225 /**
226 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
227 * Wraps to the call member function
228 */
229 static DECLCALLBACK(void) svcCall (void * pvService,
230 VBOXHGCMCALLHANDLE callHandle,
231 uint32_t u32ClientID,
232 void *pvClient,
233 uint32_t u32Function,
234 uint32_t cParms,
235 VBOXHGCMSVCPARM paParms[])
236 {
237 AssertLogRelReturnVoid(VALID_PTR(pvService));
238 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
239 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
240 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
241 LogFlowFunc (("returning\n"));
242 }
243
244 /**
245 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
246 * Wraps to the hostCall member function
247 */
248 static DECLCALLBACK(int) svcHostCall (void *pvService,
249 uint32_t u32Function,
250 uint32_t cParms,
251 VBOXHGCMSVCPARM paParms[])
252 {
253 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
254 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
255 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
256 int rc = pSelf->hostCall(u32Function, cParms, paParms);
257 LogFlowFunc (("rc=%Rrc\n", rc));
258 return rc;
259 }
260
261 /**
262 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
263 * Installs a host callback for notifications of property changes.
264 */
265 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
266 PFNHGCMSVCEXT pfnExtension,
267 void *pvExtension)
268 {
269 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
270 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
271 pSelf->mpfnHostCallback = pfnExtension;
272 pSelf->mpvHostData = pvExtension;
273 return VINF_SUCCESS;
274 }
275private:
276 int paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
277 void paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf);
278 int paramBufferAssign(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
279 int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
280 int clientConnect(uint32_t u32ClientID, void *pvClient);
281 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
282 int sendHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
283 int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
284 int cancelPendingWaits(uint32_t u32ClientID);
285 int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
286 int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
287 void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
288 void *pvClient, uint32_t eFunction, uint32_t cParms,
289 VBOXHGCMSVCPARM paParms[]);
290 int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
291 int uninit();
292};
293
294
295/** @todo Write some nice doc headers! */
296/* Stores a HGCM request in an internal buffer (pEx). Needs to be freed later using execBufferFree(). */
297int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
298{
299 AssertPtr(pBuf);
300 int rc = VINF_SUCCESS;
301
302 /* Paranoia. */
303 if (cParms > 256)
304 cParms = 256;
305
306 /*
307 * Don't verify anything here (yet), because this function only buffers
308 * the HGCM data into an internal structure and reaches it back to the guest (client)
309 * in an unmodified state.
310 */
311 if (RT_SUCCESS(rc))
312 {
313 pBuf->uMsg = uMsg;
314 pBuf->uParmCount = cParms;
315 pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
316 if (NULL == pBuf->pParms)
317 {
318 rc = VERR_NO_MEMORY;
319 }
320 else
321 {
322 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
323 {
324 pBuf->pParms[i].type = paParms[i].type;
325 switch (paParms[i].type)
326 {
327 case VBOX_HGCM_SVC_PARM_32BIT:
328 pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
329 break;
330
331 case VBOX_HGCM_SVC_PARM_64BIT:
332 /* Not supported yet. */
333 break;
334
335 case VBOX_HGCM_SVC_PARM_PTR:
336 pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
337 if (pBuf->pParms[i].u.pointer.size > 0)
338 {
339 pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
340 if (NULL == pBuf->pParms[i].u.pointer.addr)
341 {
342 rc = VERR_NO_MEMORY;
343 break;
344 }
345 else
346 memcpy(pBuf->pParms[i].u.pointer.addr,
347 paParms[i].u.pointer.addr,
348 pBuf->pParms[i].u.pointer.size);
349 }
350 break;
351
352 default:
353 break;
354 }
355 if (RT_FAILURE(rc))
356 break;
357 }
358 }
359 }
360 return rc;
361}
362
363/* Frees a buffered HGCM request. */
364void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
365{
366 AssertPtr(pBuf);
367 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
368 {
369 switch (pBuf->pParms[i].type)
370 {
371 case VBOX_HGCM_SVC_PARM_PTR:
372 if (pBuf->pParms[i].u.pointer.size > 0)
373 RTMemFree(pBuf->pParms[i].u.pointer.addr);
374 break;
375 }
376 }
377 if (pBuf->uParmCount)
378 {
379 RTMemFree(pBuf->pParms);
380 pBuf->uParmCount = 0;
381 }
382}
383
384/* Assigns data from a buffered HGCM request to the current HGCM request. */
385int Service::paramBufferAssign(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
386{
387 AssertPtr(pBuf);
388 int rc = VINF_SUCCESS;
389 if (cParms != pBuf->uParmCount)
390 {
391 LogFlowFunc(("Parameter count does not match (%u (buffer), %u (guest))\n",
392 pBuf->uParmCount, cParms));
393 rc = VERR_INVALID_PARAMETER;
394 }
395 else
396 {
397 /** @todo Add check to verify if the HGCM request is the same *type* as the buffered one! */
398 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
399 {
400 paParms[i].type = pBuf->pParms[i].type;
401 switch (paParms[i].type)
402 {
403 case VBOX_HGCM_SVC_PARM_32BIT:
404 paParms[i].u.uint32 = pBuf->pParms[i].u.uint32;
405 break;
406
407 case VBOX_HGCM_SVC_PARM_64BIT:
408 /* Not supported yet. */
409 break;
410
411 case VBOX_HGCM_SVC_PARM_PTR:
412 memcpy(paParms[i].u.pointer.addr,
413 pBuf->pParms[i].u.pointer.addr,
414 pBuf->pParms[i].u.pointer.size);
415 break;
416
417 default:
418 break;
419 }
420 }
421 }
422 return rc;
423}
424
425int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
426{
427 LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
428 return VINF_SUCCESS;
429}
430
431int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
432{
433 LogFlowFunc(("Client (%ld) disconnected\n", u32ClientID));
434
435 /*
436 * Throw out all stale clients.
437 */
438 int rc = VINF_SUCCESS;
439
440 CallListIter itCall = mClientWaiterList.begin();
441 while (itCall != mClientWaiterList.end())
442 {
443 if (itCall->mClientID == u32ClientID)
444 {
445 itCall = mClientWaiterList.erase(itCall);
446 }
447 else
448 itCall++;
449 }
450
451 ClientContextsListIter it = mClientContextsList.begin();
452 while ( it != mClientContextsList.end()
453 && RT_SUCCESS(rc))
454 {
455 if (it->mClientID == u32ClientID)
456 {
457 std::list< uint32_t >::iterator itContext = it->mContextList.begin();
458 while ( itContext != it->mContextList.end()
459 && RT_SUCCESS(rc))
460 {
461 LogFlowFunc(("Notifying host context %u of disconnect ...\n", (*itContext)));
462
463 /*
464 * Notify the host that clients with u32ClientID are no longer
465 * around and need to be cleaned up (canceling waits etc).
466 */
467 if (mpfnHostCallback)
468 {
469 CALLBACKDATACLIENTDISCONNECTED data;
470 data.hdr.u32Magic = CALLBACKDATAMAGICCLIENTDISCONNECTED;
471 data.hdr.u32ContextID = (*itContext);
472 rc = mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data));
473 if (RT_FAILURE(rc))
474 LogFlowFunc(("Notification of host context %u failed with %Rrc\n", rc));
475 }
476 itContext++;
477 }
478 it = mClientContextsList.erase(it);
479 }
480 else
481 it++;
482 }
483 return rc;
484}
485
486int Service::sendHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
487{
488 AssertPtr(pCmd);
489 int rc;
490
491 /* Sufficient parameter space? */
492 if (pCmd->mParmBuf.uParmCount > cParms)
493 {
494 paParms[0].setUInt32(pCmd->mParmBuf.uMsg); /* Message ID */
495 paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */
496
497 /*
498 * So this call apparently failed because the guest wanted to peek
499 * how much parameters it has to supply in order to successfully retrieve
500 * this command. Let's tell him so!
501 */
502 rc = VERR_TOO_MUCH_DATA;
503 }
504 else
505 {
506 rc = paramBufferAssign(&pCmd->mParmBuf, cParms, paParms);
507 }
508 return rc;
509}
510
511/*
512 * Either fills in parameters from a pending host command into our guest context or
513 * defer the guest call until we have something from the host.
514 */
515int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
516 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
517{
518 int rc = VINF_SUCCESS;
519
520 /*
521 * Lookup client in our list so that we can assign the context ID of
522 * a command to that client.
523 */
524 std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin();
525 while (it != mClientContextsList.rend())
526 {
527 if (it->mClientID == u32ClientID)
528 break;
529 it++;
530 }
531
532 /* Not found? Add client to list. */
533 if (it == mClientContextsList.rend())
534 {
535 mClientContextsList.push_back(ClientContexts(u32ClientID));
536 it = mClientContextsList.rbegin();
537 }
538 Assert(it != mClientContextsList.rend());
539
540 /*
541 * If host command list is empty (nothing to do right now) just
542 * defer the call until we got something to do (makes the client
543 * wait, depending on the flags set).
544 */
545 if (mHostCmds.empty()) /* If command list is empty, defer ... */
546 {
547 mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms));
548 rc = VINF_HGCM_ASYNC_EXECUTE;
549 }
550 else
551 {
552 /*
553 * Get the next unassigned host command in the list.
554 */
555 HostCmd curCmd = mHostCmds.front();
556 rc = sendHostCmdToGuest(&curCmd, callHandle, cParms, paParms);
557 if (RT_SUCCESS(rc))
558 {
559 /* Remember which client processes which context (for
560 * later reference & cleanup). */
561 Assert(curCmd.mContextID > 0);
562 /// @todo r=bird: check if already in the list.
563 it->mContextList.push_back(curCmd.mContextID);
564
565 /* Only if the guest really got and understood the message remove it from the list. */
566 paramBufferFree(&curCmd.mParmBuf);
567 mHostCmds.pop_front();
568 }
569 }
570 return rc;
571}
572
573/*
574 * Client asks itself (in another thread) to cancel all pending waits which are blocking the client
575 * from shutting down / doing something else.
576 */
577int Service::cancelPendingWaits(uint32_t u32ClientID)
578{
579 int rc = VINF_SUCCESS;
580 CallListIter it = mClientWaiterList.begin();
581 while (it != mClientWaiterList.end())
582 {
583 if (it->mClientID == u32ClientID)
584 {
585 if (it->mNumParms >= 2)
586 {
587 it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
588 it->mParms[1].setUInt32(0); /* Required parameters for message. */
589 }
590 if (mpHelpers)
591 mpHelpers->pfnCallComplete(it->mHandle, rc);
592 it = mClientWaiterList.erase(it);
593 }
594 else
595 it++;
596 }
597 return rc;
598}
599
600int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
601{
602 LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
603 eFunction, cParms, paParms));
604 int rc = VINF_SUCCESS;
605 if ( eFunction == GUEST_EXEC_SEND_STATUS
606 && cParms == 5)
607 {
608 CALLBACKDATAEXECSTATUS data;
609 data.hdr.u32Magic = CALLBACKDATAMAGICEXECSTATUS;
610 paParms[0].getUInt32(&data.hdr.u32ContextID);
611
612 paParms[1].getUInt32(&data.u32PID);
613 paParms[2].getUInt32(&data.u32Status);
614 paParms[3].getUInt32(&data.u32Flags);
615 paParms[4].getPointer(&data.pvData, &data.cbData);
616
617 if (mpfnHostCallback)
618 rc = mpfnHostCallback(mpvHostData, eFunction,
619 (void *)(&data), sizeof(data));
620 }
621 else if ( eFunction == GUEST_EXEC_SEND_OUTPUT
622 && cParms == 5)
623 {
624 CALLBACKDATAEXECOUT data;
625 data.hdr.u32Magic = CALLBACKDATAMAGICEXECOUT;
626 paParms[0].getUInt32(&data.hdr.u32ContextID);
627
628 paParms[1].getUInt32(&data.u32PID);
629 paParms[2].getUInt32(&data.u32HandleId);
630 paParms[3].getUInt32(&data.u32Flags);
631 paParms[4].getPointer(&data.pvData, &data.cbData);
632
633 if (mpfnHostCallback)
634 rc = mpfnHostCallback(mpvHostData, eFunction,
635 (void *)(&data), sizeof(data));
636 }
637 else if ( eFunction == GUEST_EXEC_SEND_INPUT_STATUS
638 && cParms == 4)
639 {
640 CALLBACKDATAEXECINSTATUS data;
641 data.hdr.u32Magic = CALLBACKDATAMAGICEXECINSTATUS;
642 paParms[0].getUInt32(&data.hdr.u32ContextID);
643
644 paParms[1].getUInt32(&data.u32PID);
645 paParms[2].getUInt32(&data.u32Flags);
646 paParms[3].getUInt32(&data.cbProcessed);
647
648 if (mpfnHostCallback)
649 rc = mpfnHostCallback(mpvHostData, eFunction,
650 (void *)(&data), sizeof(data));
651 }
652 else
653 rc = VERR_NOT_SUPPORTED;
654 LogFlowFunc(("returning %Rrc\n", rc));
655 return rc;
656}
657
658int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
659{
660 int rc = VINF_SUCCESS;
661
662 HostCmd newCmd;
663 rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms);
664 if ( RT_SUCCESS(rc)
665 && newCmd.mParmBuf.uParmCount > 0)
666 {
667 /*
668 * Assume that the context ID *always* is the first parameter,
669 * assign the context ID to the command.
670 */
671 newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID);
672 Assert(newCmd.mContextID > 0);
673 }
674
675 bool fProcessed = false;
676 if (RT_SUCCESS(rc))
677 {
678 /* Can we wake up a waiting client on guest? */
679 if (!mClientWaiterList.empty())
680 {
681 ClientWaiter guest = mClientWaiterList.front();
682 rc = sendHostCmdToGuest(&newCmd,
683 guest.mHandle, guest.mNumParms, guest.mParms);
684
685 /* In any case the client did something, so wake up and remove from list. */
686 AssertPtr(mpHelpers);
687 mpHelpers->pfnCallComplete(guest.mHandle, rc);
688 mClientWaiterList.pop_front();
689
690 /* If we got VERR_TOO_MUCH_DATA we buffer the host command in the next block
691 * and return success to the host. */
692 if (rc == VERR_TOO_MUCH_DATA)
693 {
694 rc = VINF_SUCCESS;
695 }
696 else /* If command was understood by the client, free and remove from host commands list. */
697 {
698 paramBufferFree(&newCmd.mParmBuf);
699 fProcessed = true;
700 }
701 }
702
703 /* If not processed, buffer it ... */
704 if (!fProcessed)
705 {
706 mHostCmds.push_back(newCmd);
707#if 0
708 /* Limit list size by deleting oldest element. */
709 if (mHostCmds.size() > 256) /** @todo Use a define! */
710 mHostCmds.pop_front();
711#endif
712 }
713 }
714 return rc;
715}
716
717/**
718 * Handle an HGCM service call.
719 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
720 * @note All functions which do not involve an unreasonable delay will be
721 * handled synchronously. If needed, we will add a request handler
722 * thread in future for those which do.
723 *
724 * @thread HGCM
725 */
726void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
727 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
728 VBOXHGCMSVCPARM paParms[])
729{
730 int rc = VINF_SUCCESS;
731 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
732 u32ClientID, eFunction, cParms, paParms));
733 try
734 {
735 switch (eFunction)
736 {
737 /*
738 * The guest asks the host for the next messsage to process.
739 */
740 case GUEST_GET_HOST_MSG:
741 LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
742 rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms);
743 break;
744
745 /*
746 * The guest wants to shut down and asks us (this service) to cancel
747 * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
748 * guest can gracefully shut down.
749 */
750 case GUEST_CANCEL_PENDING_WAITS:
751 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
752 rc = cancelPendingWaits(u32ClientID);
753 break;
754
755 /*
756 * The guest notifies the host that some output at stdout/stderr is available.
757 */
758 case GUEST_EXEC_SEND_OUTPUT:
759 LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
760 rc = notifyHost(eFunction, cParms, paParms);
761 break;
762
763 /*
764 * The guest notifies the host of the executed process status.
765 */
766 case GUEST_EXEC_SEND_STATUS:
767 LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
768 rc = notifyHost(eFunction, cParms, paParms);
769 break;
770
771 case GUEST_EXEC_SEND_INPUT_STATUS:
772 LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
773 rc = notifyHost(eFunction, cParms, paParms);
774 break;
775
776 default:
777 rc = VERR_NOT_SUPPORTED;
778 break;
779 }
780 if (rc != VINF_HGCM_ASYNC_EXECUTE)
781 {
782 /* Tell the client that the call is complete (unblocks waiting). */
783 AssertPtr(mpHelpers);
784 mpHelpers->pfnCallComplete(callHandle, rc);
785 }
786 }
787 catch (std::bad_alloc)
788 {
789 rc = VERR_NO_MEMORY;
790 }
791 LogFlowFunc(("rc = %Rrc\n", rc));
792}
793
794/**
795 * Service call handler for the host.
796 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
797 * @thread hgcm
798 */
799int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
800{
801 int rc = VINF_SUCCESS;
802 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
803 eFunction, cParms, paParms));
804 try
805 {
806 switch (eFunction)
807 {
808 /* The host wants to execute something. */
809 case HOST_EXEC_CMD:
810 LogFlowFunc(("HOST_EXEC_CMD\n"));
811 rc = processHostCmd(eFunction, cParms, paParms);
812 break;
813
814 /* The host wants to send something to the
815 * started process' stdin pipe. */
816 case HOST_EXEC_SET_INPUT:
817 LogFlowFunc(("HOST_EXEC_SET_INPUT\n"));
818 rc = processHostCmd(eFunction, cParms, paParms);
819 break;
820
821 case HOST_EXEC_GET_OUTPUT:
822 LogFlowFunc(("HOST_EXEC_GET_OUTPUT\n"));
823 rc = processHostCmd(eFunction, cParms, paParms);
824 break;
825
826 default:
827 rc = VERR_NOT_SUPPORTED;
828 break;
829 }
830 }
831 catch (std::bad_alloc)
832 {
833 rc = VERR_NO_MEMORY;
834 }
835
836 LogFlowFunc(("rc = %Rrc\n", rc));
837 return rc;
838}
839
840int Service::uninit()
841{
842 /* Free allocated buffered host commands. */
843 HostCmdListIter it;
844 for (it = mHostCmds.begin(); it != mHostCmds.end(); it++)
845 paramBufferFree(&it->mParmBuf);
846 mHostCmds.clear();
847
848 return VINF_SUCCESS;
849}
850
851} /* namespace guestControl */
852
853using guestControl::Service;
854
855/**
856 * @copydoc VBOXHGCMSVCLOAD
857 */
858extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
859{
860 int rc = VINF_SUCCESS;
861
862 LogFlowFunc(("ptable = %p\n", ptable));
863
864 if (!VALID_PTR(ptable))
865 {
866 rc = VERR_INVALID_PARAMETER;
867 }
868 else
869 {
870 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
871
872 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
873 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
874 {
875 rc = VERR_VERSION_MISMATCH;
876 }
877 else
878 {
879 std::auto_ptr<Service> apService;
880 /* No exceptions may propogate outside. */
881 try {
882 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
883 } catch (int rcThrown) {
884 rc = rcThrown;
885 } catch (...) {
886 rc = VERR_UNRESOLVED_ERROR;
887 }
888
889 if (RT_SUCCESS(rc))
890 {
891 /*
892 * We don't need an additional client data area on the host,
893 * because we're a class which can have members for that :-).
894 */
895 ptable->cbClient = 0;
896
897 /* Register functions. */
898 ptable->pfnUnload = Service::svcUnload;
899 ptable->pfnConnect = Service::svcConnect;
900 ptable->pfnDisconnect = Service::svcDisconnect;
901 ptable->pfnCall = Service::svcCall;
902 ptable->pfnHostCall = Service::svcHostCall;
903 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
904 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
905 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
906
907 /* Service specific initialization. */
908 ptable->pvService = apService.release();
909 }
910 }
911 }
912
913 LogFlowFunc(("returning %Rrc\n", rc));
914 return rc;
915}
916
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