VirtualBox

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

Last change on this file since 27945 was 27945, checked in by vboxsync, 15 years ago

Guest Control: Update (added dummy IProgress, update to host service).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: service.cpp 27945 2010-04-01 15:20:08Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/** @page pg_svc_guest_control Guest Control HGCM Service
23 *
24 * @todo Write up some nice text here.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP LOG_GROUP_HGCM
31#include <VBox/HostServices/GuestControlSvc.h>
32
33#include <VBox/log.h>
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/cpp/autores.h>
37#include <iprt/cpp/utils.h>
38#include <iprt/critsect.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/req.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/time.h>
45
46#include <memory> /* for auto_ptr */
47#include <string>
48#include <list>
49
50#include "gctrl.h"
51
52namespace guestControl {
53
54/**
55 * Class containing the shared information service functionality.
56 */
57class Service : public stdx::non_copyable
58{
59private:
60 /** Type definition for use in callback functions */
61 typedef Service SELF;
62 /** HGCM helper functions. */
63 PVBOXHGCMSVCHELPERS mpHelpers;
64 /** @todo we should have classes for thread and request handler thread */
65 /** Queue of outstanding property change notifications */
66 RTREQQUEUE *mReqQueue;
67 /** Request that we've left pending in a call to flushNotifications. */
68 PRTREQ mPendingDummyReq;
69 /** Thread for processing the request queue */
70 RTTHREAD mReqThread;
71 /** Tell the thread that it should exit */
72 bool volatile mfExitThread;
73 /** Callback function supplied by the host for notification of updates
74 * to properties */
75 PFNHGCMSVCEXT mpfnHostCallback;
76 /** User data pointer to be supplied to the host callback function */
77 void *mpvHostData;
78 /** The execution data to hold (atm only one buffer!) */
79 VBOXGUESTCTRPARAMBUFFER mExec;
80 RTCRITSECT critsect;
81
82public:
83 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
84 : mpHelpers(pHelpers)
85 , mPendingDummyReq(NULL)
86 , mfExitThread(false)
87 , mpfnHostCallback(NULL)
88 , mpvHostData(NULL)
89 {
90 int rc = RTReqCreateQueue(&mReqQueue);
91#ifndef VBOX_GUEST_CTRL_TEST_NOTHREAD
92 if (RT_SUCCESS(rc))
93 rc = RTThreadCreate(&mReqThread, reqThreadFn, this, 0,
94 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
95 "GuestCtrlReq");
96#endif
97 mExec.uParmCount = 0;
98 mExec.pParms = NULL;
99
100 if (RT_SUCCESS(rc))
101 rc = RTCritSectInit(&critsect);
102
103 if (RT_FAILURE(rc))
104 throw rc;
105 }
106
107 /**
108 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
109 * Simply deletes the service object
110 */
111 static DECLCALLBACK(int) svcUnload (void *pvService)
112 {
113 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
114 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
115 int rc = pSelf->uninit();
116 AssertRC(rc);
117 if (RT_SUCCESS(rc))
118 delete pSelf;
119 return rc;
120 }
121
122 /**
123 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
124 * Stub implementation of pfnConnect and pfnDisconnect.
125 */
126 static DECLCALLBACK(int) svcConnectDisconnect (void * /* pvService */,
127 uint32_t /* u32ClientID */,
128 void * /* pvClient */)
129 {
130 return VINF_SUCCESS;
131 }
132
133 /**
134 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
135 * Wraps to the call member function
136 */
137 static DECLCALLBACK(void) svcCall (void * pvService,
138 VBOXHGCMCALLHANDLE callHandle,
139 uint32_t u32ClientID,
140 void *pvClient,
141 uint32_t u32Function,
142 uint32_t cParms,
143 VBOXHGCMSVCPARM paParms[])
144 {
145 AssertLogRelReturnVoid(VALID_PTR(pvService));
146 LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
147 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
148 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
149 LogFlowFunc (("returning\n"));
150 }
151
152 /**
153 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
154 * Wraps to the hostCall member function
155 */
156 static DECLCALLBACK(int) svcHostCall (void *pvService,
157 uint32_t u32Function,
158 uint32_t cParms,
159 VBOXHGCMSVCPARM paParms[])
160 {
161 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
162 LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
163 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
164 int rc = pSelf->hostCall(u32Function, cParms, paParms);
165 LogFlowFunc (("rc=%Rrc\n", rc));
166 return rc;
167 }
168
169 /**
170 * @copydoc VBOXHGCMSVCHELPERS::pfnRegisterExtension
171 * Installs a host callback for notifications of property changes.
172 */
173 static DECLCALLBACK(int) svcRegisterExtension (void *pvService,
174 PFNHGCMSVCEXT pfnExtension,
175 void *pvExtension)
176 {
177 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
178 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
179 pSelf->mpfnHostCallback = pfnExtension;
180 pSelf->mpvHostData = pvExtension;
181 return VINF_SUCCESS;
182 }
183private:
184 int execBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
185 void execBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf);
186 int execBufferAssign(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
187 int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
188 static DECLCALLBACK(int) reqThreadFn(RTTHREAD ThreadSelf, void *pvUser);
189 void call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
190 void *pvClient, uint32_t eFunction, uint32_t cParms,
191 VBOXHGCMSVCPARM paParms[]);
192 int hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
193 int uninit ();
194};
195
196
197/**
198 * Thread function for processing the request queue
199 * @copydoc FNRTTHREAD
200 */
201/* static */
202DECLCALLBACK(int) Service::reqThreadFn(RTTHREAD ThreadSelf, void *pvUser)
203{
204 SELF *pSelf = reinterpret_cast<SELF *>(pvUser);
205 while (!pSelf->mfExitThread)
206 RTReqProcess(pSelf->mReqQueue, RT_INDEFINITE_WAIT);
207 return VINF_SUCCESS;
208}
209
210/** @todo Write some nice doc headers! */
211/* Stores a HGCM request in an internal buffer (pEx). Needs to be freed later using execBufferFree(). */
212int Service::execBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
213{
214 RTCritSectEnter(&critsect);
215
216 AssertPtr(pBuf);
217 int rc = VINF_SUCCESS;
218
219 /*
220 * Don't verify anything here (yet), because this function only buffers
221 * the HGCM data into an internal structure and reaches it back to the guest (client)
222 * in an unmodified state.
223 */
224 if (RT_SUCCESS(rc))
225 {
226 pBuf->uParmCount = cParms;
227 pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
228 if (NULL == pBuf->pParms)
229 {
230 rc = VERR_NO_MEMORY;
231 }
232 else
233 {
234 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
235 {
236 pBuf->pParms[i].type = paParms[i].type;
237 switch (paParms[i].type)
238 {
239 case VBOX_HGCM_SVC_PARM_32BIT:
240 pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
241 break;
242
243 case VBOX_HGCM_SVC_PARM_64BIT:
244 /* Not supported yet. */
245 break;
246
247 case VBOX_HGCM_SVC_PARM_PTR:
248 pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
249 if (pBuf->pParms[i].u.pointer.size > 0)
250 {
251 pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
252 if (NULL == pBuf->pParms[i].u.pointer.addr)
253 {
254 rc = VERR_NO_MEMORY;
255 break;
256 }
257 else
258 memcpy(pBuf->pParms[i].u.pointer.addr,
259 paParms[i].u.pointer.addr,
260 pBuf->pParms[i].u.pointer.size);
261 }
262 break;
263
264 default:
265 break;
266 }
267 if (RT_FAILURE(rc))
268 break;
269 }
270 }
271 }
272 RTCritSectLeave(&critsect);
273 return rc;
274}
275
276/* Frees a buffered HGCM request. */
277void Service::execBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
278{
279 RTCritSectEnter(&critsect);
280 AssertPtr(pBuf);
281 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
282 {
283 switch (pBuf->pParms[i].type)
284 {
285 case VBOX_HGCM_SVC_PARM_PTR:
286 if (pBuf->pParms[i].u.pointer.size > 0)
287 RTMemFree(pBuf->pParms[i].u.pointer.addr);
288 break;
289 }
290 }
291 if (pBuf->uParmCount)
292 {
293 RTMemFree(pBuf->pParms);
294 pBuf->uParmCount = 0;
295 }
296 RTCritSectLeave(&critsect);
297}
298
299/* Assigns data from a buffered HGCM request to the current HGCM request. */
300int Service::execBufferAssign(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
301{
302 execBufferFree(pBuf);
303
304 RTCritSectEnter(&critsect);
305
306 AssertPtr(pBuf);
307 int rc = VINF_SUCCESS;
308 if (cParms != pBuf->uParmCount)
309 {
310 rc = VERR_INVALID_PARAMETER;
311 }
312 else
313 {
314 /** @todo Add check to verify if the HGCM request is the same *type* as the buffered one! */
315 for (uint32_t i = 0; i < pBuf->uParmCount; i++)
316 {
317 paParms[i].type = pBuf->pParms[i].type;
318 switch (paParms[i].type)
319 {
320 case VBOX_HGCM_SVC_PARM_32BIT:
321 paParms[i].u.uint32 = pBuf->pParms[i].u.uint32;
322 break;
323
324 case VBOX_HGCM_SVC_PARM_64BIT:
325 /* Not supported yet. */
326 break;
327
328 case VBOX_HGCM_SVC_PARM_PTR:
329 memcpy(paParms[i].u.pointer.addr,
330 pBuf->pParms[i].u.pointer.addr,
331 pBuf->pParms[i].u.pointer.size);
332 break;
333
334 default:
335 break;
336 }
337 }
338 }
339 RTCritSectLeave(&critsect);
340 return rc;
341}
342
343/**
344 * Handle an HGCM service call.
345 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
346 * @note All functions which do not involve an unreasonable delay will be
347 * handled synchronously. If needed, we will add a request handler
348 * thread in future for those which do.
349 *
350 * @thread HGCM
351 */
352void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
353 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
354 VBOXHGCMSVCPARM paParms[])
355{
356 int rc = VINF_SUCCESS;
357 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
358 u32ClientID, eFunction, cParms, paParms));
359
360 ASMBreakpoint();
361
362 try
363 {
364 switch (eFunction)
365 {
366 /* The guest asks the host for the next messsage to process. */
367 case GUEST_GET_HOST_MSG:
368 LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
369 paParms[0].setUInt32(HOST_EXEC_CMD); /* msg id */
370 paParms[1].setUInt32(12); /* parms count */
371 break;
372
373 case GUEST_GET_HOST_MSG_DATA:
374 LogFlowFunc(("GUEST_GET_HOST_MSG_DATA\n"));
375 rc = execBufferAssign(&mExec, cParms, paParms);
376 break;
377
378 /* The guest notifies the host that some output at stdout is available. */
379 case GUEST_EXEC_SEND_STDOUT:
380 LogFlowFunc(("GUEST_EXEC_SEND_STDOUT\n"));
381 break;
382
383 /* The guest notifies the host that some output at stderr is available. */
384 case GUEST_EXEC_SEND_STDERR:
385 LogFlowFunc(("GUEST_EXEC_SEND_STDERR\n"));
386 break;
387
388 /* The guest notifies the host of the current client status. */
389 case GUEST_EXEC_SEND_STATUS:
390 LogFlowFunc(("SEND_STATUS\n"));
391 break;
392
393 default:
394 rc = VERR_NOT_IMPLEMENTED;
395 }
396 }
397 catch (std::bad_alloc)
398 {
399 rc = VERR_NO_MEMORY;
400 }
401 LogFlowFunc(("rc = %Rrc\n", rc));
402 if (rc != VINF_HGCM_ASYNC_EXECUTE)
403 {
404 mpHelpers->pfnCallComplete (callHandle, rc);
405 }
406}
407
408/**
409 * Service call handler for the host.
410 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
411 * @thread hgcm
412 */
413int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
414{
415 int rc = VINF_SUCCESS;
416
417 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
418 eFunction, cParms, paParms));
419 try
420 {
421 switch (eFunction)
422 {
423 /* The host wants to execute something. */
424 case HOST_EXEC_CMD:
425 LogFlowFunc(("HOST_EXEC_CMD\n"));
426 rc = execBufferAllocate(&mExec, cParms, paParms);
427 break;
428
429 /* The host wants to send something to the guest's stdin pipe. */
430 case HOST_EXEC_SEND_STDIN:
431 LogFlowFunc(("HOST_EXEC_SEND_STDIN\n"));
432 break;
433
434 case HOST_EXEC_GET_STATUS:
435 LogFlowFunc(("HOST_EXEC_GET_STATUS\n"));
436 break;
437
438 default:
439 rc = VERR_NOT_SUPPORTED;
440 break;
441 }
442 }
443 catch (std::bad_alloc)
444 {
445 rc = VERR_NO_MEMORY;
446 }
447
448 LogFlowFunc(("rc = %Rrc\n", rc));
449 return rc;
450}
451
452int Service::uninit()
453{
454 int rc = VINF_SUCCESS;
455 return rc;
456}
457
458} /* namespace guestControl */
459
460using guestControl::Service;
461
462/**
463 * @copydoc VBOXHGCMSVCLOAD
464 */
465extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
466{
467 int rc = VINF_SUCCESS;
468
469 LogFlowFunc(("ptable = %p\n", ptable));
470
471 if (!VALID_PTR(ptable))
472 {
473 rc = VERR_INVALID_PARAMETER;
474 }
475 else
476 {
477 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
478
479 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
480 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
481 {
482 rc = VERR_VERSION_MISMATCH;
483 }
484 else
485 {
486 std::auto_ptr<Service> apService;
487 /* No exceptions may propogate outside. */
488 try {
489 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
490 } catch (int rcThrown) {
491 rc = rcThrown;
492 } catch (...) {
493 rc = VERR_UNRESOLVED_ERROR;
494 }
495
496 if (RT_SUCCESS(rc))
497 {
498 /*
499 * We don't need an additional client data area on the host,
500 * because we're a class which can have members for that :-).
501 */
502 ptable->cbClient = 0;
503
504 /* Register functions. */
505 ptable->pfnUnload = Service::svcUnload;
506 ptable->pfnConnect = Service::svcConnectDisconnect;
507 ptable->pfnDisconnect = Service::svcConnectDisconnect;
508 ptable->pfnCall = Service::svcCall;
509 ptable->pfnHostCall = Service::svcHostCall;
510 ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
511 ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
512 ptable->pfnRegisterExtension = Service::svcRegisterExtension;
513
514 /* Service specific initialization. */
515 ptable->pvService = apService.release();
516 }
517 }
518 }
519
520 LogFlowFunc(("returning %Rrc\n", rc));
521 return rc;
522}
523
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette