VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 45038

Last change on this file since 45038 was 43996, checked in by vboxsync, 12 years ago

HostServices/DragAndDrop: play safe when removing clients from the queue

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: service.cpp 43996 2012-11-29 13:24:08Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to 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
59#include "dndmanager.h"
60
61//# define DO(s) RTPrintf s
62#define DO(s) do {} while(0)
63//#define DO(s) Log s
64
65/******************************************************************************
66 * Service class declaration *
67 ******************************************************************************/
68
69/**
70 * Specialized drag & drop service class.
71 */
72class DragAndDropService: public HGCM::AbstractService<DragAndDropService>
73{
74public:
75 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
76 : HGCM::AbstractService<DragAndDropService>(pHelpers)
77 , m_pManager(0)
78 , m_cClients(0)
79 {}
80
81protected:
82 /* HGCM service implementation */
83 int init(VBOXHGCMSVCFNTABLE *pTable);
84 int uninit();
85 int clientConnect(uint32_t u32ClientID, void *pvClient);
86 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
87 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
88 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
89
90 static DECLCALLBACK(int) progressCallback(unsigned uPercentage, uint32_t uState, void *pvUser);
91 int hostMessage(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
92 void modeSet(uint32_t u32Mode);
93 inline uint32_t modeGet() { return m_u32Mode; };
94
95 DnDManager *m_pManager;
96
97 uint32_t m_cClients;
98 RTCList<HGCM::Client*> m_clientQueue;
99 uint32_t m_u32Mode;
100};
101
102/******************************************************************************
103 * Service class implementation *
104 ******************************************************************************/
105
106int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
107{
108 /* Register functions. */
109 pTable->pfnHostCall = svcHostCall;
110 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
111 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
112 pTable->pfnRegisterExtension = svcRegisterExtension;
113 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
114
115 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
116
117 return VINF_SUCCESS;
118}
119
120int DragAndDropService::uninit()
121{
122 delete m_pManager;
123
124 return VINF_SUCCESS;
125}
126
127int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
128{
129 LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
130 DO(("New client (%ld) connected\n", u32ClientID));
131 if (m_cClients < UINT32_MAX)
132 m_cClients++;
133 else
134 AssertMsgFailed(("Maximum number of clients reached\n"));
135 return VINF_SUCCESS;
136}
137
138int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
139{
140 /* Remove all waiters with this clientId. */
141 for (size_t i = 0; i < m_clientQueue.size(); )
142 {
143 HGCM::Client *pClient = m_clientQueue.at(i);
144 if (pClient->clientId() == u32ClientID)
145 {
146 m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
147 m_clientQueue.removeAt(i);
148 delete pClient;
149 }
150 else
151 i++;
152 }
153
154 return VINF_SUCCESS;
155}
156
157void DragAndDropService::modeSet(uint32_t u32Mode)
158{
159 switch (u32Mode)
160 {
161 case VBOX_DRAG_AND_DROP_MODE_OFF:
162 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
163 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
164 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
165 m_u32Mode = u32Mode;
166 break;
167
168 default:
169 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
170 }
171}
172
173void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
174{
175 int rc = VINF_SUCCESS;
176 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
177 u32ClientID, u32Function, cParms, paParms));
178// RTPrintf("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
179// u32ClientID, u32Function, cParms, paParms);
180
181 switch (u32Function)
182 {
183 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
184 {
185 DO(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
186 if ( cParms != 3
187 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
188 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
189 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
190 rc = VERR_INVALID_PARAMETER;
191 else
192 {
193 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
194 if ( RT_FAILURE(rc)
195 && paParms[2].u.uint32) /* Blocking? */
196 {
197 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle, u32Function, cParms, paParms));
198 rc = VINF_HGCM_ASYNC_EXECUTE;
199 }
200 }
201 break;
202 }
203 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
204 {
205 DO(("GUEST_DND_HG_ACK_OP\n"));
206 if ( modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
207 && modeGet() != VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
208 {
209 DO(("=> ignoring!\n"));
210 break;
211 }
212
213 if ( cParms != 1
214 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
215 rc = VERR_INVALID_PARAMETER;
216 else
217 {
218 DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
219 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
220 paParms[0].getUInt32(&data.uAction);
221 if (m_pfnHostCallback)
222 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
223// m_pHelpers->pfnCallComplete(callHandle, rc);
224 }
225 break;
226 }
227 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
228 {
229 DO(("GUEST_DND_HG_REQ_DATA\n"));
230 if ( modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
231 && modeGet() != VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
232 {
233 DO(("=> ignoring!\n"));
234 break;
235 }
236
237 if ( cParms != 1
238 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
239 rc = VERR_INVALID_PARAMETER;
240 else
241 {
242 DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
243 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
244 uint32_t cTmp;
245 paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
246 if (m_pfnHostCallback)
247 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
248// m_pHelpers->pfnCallComplete(callHandle, rc);
249// if (data.pszFormat)
250// RTMemFree(data.pszFormat);
251// if (data.pszTmpPath)
252// RTMemFree(data.pszTmpPath);
253 }
254 break;
255 }
256#ifdef VBOX_WITH_DRAG_AND_DROP_GH
257 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
258 {
259 DO(("GUEST_DND_GH_ACK_PENDING\n"));
260 if ( modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
261 && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
262 {
263 DO(("=> ignoring!\n"));
264 break;
265 }
266
267 if ( cParms != 3
268 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
269 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
270 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
271 rc = VERR_INVALID_PARAMETER;
272 else
273 {
274 DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
275 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
276 paParms[0].getUInt32(&data.uDefAction);
277 paParms[1].getUInt32(&data.uAllActions);
278 uint32_t cTmp;
279 paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
280 if (m_pfnHostCallback)
281 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
282 }
283 break;
284 }
285 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
286 {
287 DO(("GUEST_DND_GH_SND_DATA\n"));
288 if ( modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
289 && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
290 {
291 DO(("=> ignoring\n"));
292 break;
293 }
294
295 if ( cParms != 2
296 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* data */
297 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
298 rc = VERR_INVALID_PARAMETER;
299 else
300 {
301 DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
302 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
303 paParms[0].getPointer((void**)&data.pvData, &data.cbData);
304 paParms[1].getUInt32(&data.cbAllSize);
305 if (m_pfnHostCallback)
306 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
307 }
308 break;
309 }
310 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
311 {
312 DO(("GUEST_DND_GH_EVT_ERROR\n"));
313 if ( modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
314 && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
315 {
316 DO(("=> ignoring!\n"));
317 break;
318 }
319
320 if ( cParms != 1
321 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
322 rc = VERR_INVALID_PARAMETER;
323 else
324 {
325 DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
326 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
327 uint32_t rcOp;
328 paParms[0].getUInt32(&rcOp);
329 data.rc = rcOp;
330 if (m_pfnHostCallback)
331 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
332 }
333 break;
334 }
335#endif
336 default:
337 {
338 /* All other messages are handled by the DnD manager. */
339 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
340 /* Check for error. Buffer overflow is allowed. It signals the
341 * guest to ask for more data in the next event. */
342 if ( RT_FAILURE(rc)
343 && rc != VERR_CANCELLED
344 && rc != VERR_BUFFER_OVERFLOW) /* Buffer overflow is allowed. */
345 {
346 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle, u32Function, cParms, paParms));
347 rc = VINF_HGCM_ASYNC_EXECUTE;
348 }
349 break;
350 }
351 }
352 /* If async execute is requested, we didn't notify the guest about
353 * completion. The client is queued into the waiters list and will be
354 * notified as soon as a new event is available. */
355 if (rc != VINF_HGCM_ASYNC_EXECUTE)
356 m_pHelpers->pfnCallComplete(callHandle, rc);
357 DO(("guest call: %Rrc\n", rc));
358}
359
360int DragAndDropService::hostMessage(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
361{
362 int rc = VINF_SUCCESS;
363#if 0
364 HGCM::Message *pMessage = new HGCM::Message(u32Function, cParms, paParms);
365 m_hostQueue.push(pMessage);
366// bool fPush = true;
367 RTPrintf("client queue %u\n", m_clientQueue.size());
368 RTPrintf("host queue %u\n", m_hostQueue.size());
369 if (!m_clientQueue.empty())
370 {
371 pMessage = m_hostQueue.front();
372 HGCM::Client *pClient = m_clientQueue.front();
373 /* Check if this was a request for getting the next host
374 * message. If so, return the message id and the parameter
375 * count. The message itself has to be queued. */
376 if (pClient->message() == DragAndDropSvc::GUEST_GET_NEXT_HOST_MSG)
377 {
378 RTPrintf("client is waiting for next host msg\n");
379// rc = VERR_TOO_MUCH_DATA;
380 pClient->addMessageInfo(pMessage);
381 /* temp */
382// m_pHelpers->pfnCallComplete(pClient->handle(), rc);
383// m_clientQueue.pop();
384// delete pClient;
385 }
386 else
387 {
388 RTPrintf("client is waiting for host msg (%d)\n", u32Function);
389 /* There is a request for a host message pending. Check
390 * if this is the correct message and if so deliver. If
391 * not the message will be queued. */
392 rc = pClient->addMessage(pMessage);
393 m_hostQueue.pop();
394 delete pMessage;
395// if (RT_SUCCESS(rc))
396// fPush = false;
397 }
398 /* In any case mark this client request as done. */
399 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
400 m_clientQueue.pop_front();
401 delete pClient;
402 }
403// if (fPush)
404// {
405// RTPrintf("push message\n");
406// m_hostQueue.push(pMessage);
407// }
408// else
409// delete pMessage;
410#endif
411
412 return rc;
413}
414
415int DragAndDropService::hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
416{
417 int rc = VINF_SUCCESS;
418 if (u32Function == DragAndDropSvc::HOST_DND_SET_MODE)
419 {
420 if (cParms != 1)
421 rc = VERR_INVALID_PARAMETER;
422 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
423 rc = VERR_INVALID_PARAMETER;
424 else
425 modeSet(paParms[0].u.uint32);
426 }
427 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
428 {
429 rc = m_pManager->addMessage(u32Function, cParms, paParms);
430 if ( RT_SUCCESS(rc)
431 && !m_clientQueue.isEmpty())
432 {
433 HGCM::Client *pClient = m_clientQueue.first();
434 /* Check if this was a request for getting the next host
435 * message. If so, return the message id and the parameter
436 * count. The message itself has to be queued. */
437 if (pClient->message() == DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG)
438 {
439 DO(("client is waiting for next host msg\n"));
440// rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
441 uint32_t uMsg1;
442 uint32_t cParms1;
443 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
444 if (RT_SUCCESS(rc))
445 {
446 pClient->addMessageInfo(uMsg1, cParms1);
447 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
448 m_clientQueue.removeFirst();
449 delete pClient;
450 }
451 else
452 AssertMsgFailed(("Should not happen!"));
453 }
454 else
455 AssertMsgFailed(("Should not happen!"));
456 }
457// else
458// AssertMsgFailed(("Should not happen %Rrc!", rc));
459 }
460
461 LogFlowFunc(("rc=%Rrc\n", rc));
462 return rc;
463}
464
465DECLCALLBACK(int) DragAndDropService::progressCallback(unsigned uPercentage, uint32_t uState, void *pvUser)
466{
467 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
468
469 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
470
471 if (pSelf->m_pfnHostCallback)
472 {
473 DO(("GUEST_DND_HG_EVT_PROGRESS %u\n", uPercentage));
474 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
475 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
476 data.uPercentage = uPercentage;
477 data.uState = uState;
478
479 return pSelf->m_pfnHostCallback(pSelf->m_pvHostData, DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS, &data, sizeof(data));
480 }
481
482 return VINF_SUCCESS;
483}
484
485/**
486 * @copydoc VBOXHGCMSVCLOAD
487 */
488extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
489{
490 return DragAndDropService::svcLoad(pTable);
491}
492
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