VirtualBox

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

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

GuestControl/service: Check for HGCM buffer size.

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