VirtualBox

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

Last change on this file since 50582 was 50561, checked in by vboxsync, 11 years ago

DnD: Update, bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.2 KB
Line 
1/* $Id: service.cpp 50561 2014-02-24 21:07:22Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2014 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#ifdef LOG_GROUP
58 #undef LOG_GROUP
59#endif
60#define LOG_GROUP LOG_GROUP_GUEST_DND
61
62#include "dndmanager.h"
63
64/******************************************************************************
65 * Service class declaration *
66 ******************************************************************************/
67
68/**
69 * Specialized drag & drop service class.
70 */
71class DragAndDropService: public HGCM::AbstractService<DragAndDropService>
72{
73public:
74
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(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser);
91 int modeSet(uint32_t u32Mode);
92 inline uint32_t modeGet() { return m_u32Mode; };
93
94 DnDManager *m_pManager;
95
96 uint32_t m_cClients;
97 RTCList<HGCM::Client*> m_clientQueue;
98 uint32_t m_u32Mode;
99};
100
101/******************************************************************************
102 * Service class implementation *
103 ******************************************************************************/
104
105int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
106{
107 /* Register functions. */
108 pTable->pfnHostCall = svcHostCall;
109 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
110 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
111 pTable->pfnRegisterExtension = svcRegisterExtension;
112
113 /* Drag'n drop mode is disabled by default. */
114 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
115
116 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
117
118 return VINF_SUCCESS;
119}
120
121int DragAndDropService::uninit(void)
122{
123 delete m_pManager;
124
125 return VINF_SUCCESS;
126}
127
128int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
129{
130 LogFlowFunc(("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
136 /*
137 * Clear the message queue as soon as a new clients connect
138 * to ensure that every client has the same state.
139 */
140 if (m_pManager)
141 m_pManager->clear();
142
143 return VINF_SUCCESS;
144}
145
146int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
147{
148 /* Remove all waiters with this u32ClientID. */
149 for (size_t i = 0; i < m_clientQueue.size(); )
150 {
151 HGCM::Client *pClient = m_clientQueue.at(i);
152 if (pClient->clientId() == u32ClientID)
153 {
154 if (m_pHelpers)
155 m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
156
157 m_clientQueue.removeAt(i);
158 delete pClient;
159 }
160 else
161 i++;
162 }
163
164 return VINF_SUCCESS;
165}
166
167int DragAndDropService::modeSet(uint32_t u32Mode)
168{
169 /** @todo Validate mode. */
170 switch (u32Mode)
171 {
172 case VBOX_DRAG_AND_DROP_MODE_OFF:
173 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
174 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
175 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
176 m_u32Mode = u32Mode;
177 break;
178
179 default:
180 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
181 break;
182 }
183
184 return VINF_SUCCESS;
185}
186
187void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
188 void *pvClient, uint32_t u32Function,
189 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
190{
191 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
192 u32ClientID, u32Function, cParms));
193
194 /* Check if we've the right mode set. */
195 bool fIgnoreRequest = true; /* Play safe. */
196 switch (u32Function)
197 {
198 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
199 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
200 {
201 fIgnoreRequest = false;
202 }
203 else
204 LogFlowFunc(("Drag'n drop disabled, ignoring request\n"));
205 break;
206 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
207 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
208 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
209 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
210 {
211 fIgnoreRequest = false;
212 }
213 else
214 LogFlowFunc(("Host -> guest DnD mode disabled, ignoring request\n"));
215 break;
216#ifdef VBOX_WITH_DRAG_AND_DROP_GH
217 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
218 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
219 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
220 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
221 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
222 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
223 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
224 {
225 fIgnoreRequest = false;
226 }
227 else
228 LogFlowFunc(("Guest -> host DnD mode disabled, ignoring request\n"));
229 break;
230#endif
231 default:
232 /* Reach through to DnD manager. */
233 fIgnoreRequest = false;
234 break;
235 }
236
237 int rc;
238 if (!fIgnoreRequest)
239 {
240 switch (u32Function)
241 {
242 /* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
243 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
244 * HOST_DND_GH_REQ_PENDING, which led this service returning
245 * VERR_INVALID_PARAMETER when the guest wanted to actually
246 * handle HOST_DND_GH_REQ_PENDING. */
247 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
248 {
249 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
250 if ( cParms != 3
251 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
252 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
253 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
254 rc = VERR_INVALID_PARAMETER;
255 else
256 {
257 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
258 if ( RT_FAILURE(rc)
259 && paParms[2].u.uint32) /* Blocking? */
260 {
261 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle,
262 u32Function, cParms, paParms));
263 rc = VINF_HGCM_ASYNC_EXECUTE;
264 }
265 }
266 break;
267 }
268 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
269 {
270 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
271 if ( cParms != 1
272 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
273 rc = VERR_INVALID_PARAMETER;
274 else
275 {
276 DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
277 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
278 paParms[0].getUInt32(&data.uAction);
279 if (m_pfnHostCallback)
280 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
281 // m_pHelpers->pfnCallComplete(callHandle, rc);
282 }
283 break;
284 }
285 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
286 {
287 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
288 if ( cParms != 1
289 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
290 rc = VERR_INVALID_PARAMETER;
291 else
292 {
293 DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
294 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
295 uint32_t cTmp;
296 paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
297 if (m_pfnHostCallback)
298 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
299 // m_pHelpers->pfnCallComplete(callHandle, rc);
300 // if (data.pszFormat)
301 // RTMemFree(data.pszFormat);
302 // if (data.pszTmpPath)
303 // RTMemFree(data.pszTmpPath);
304 }
305 break;
306 }
307#ifdef VBOX_WITH_DRAG_AND_DROP_GH
308 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
309 {
310 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
311 if ( cParms != 3
312 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
313 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
314 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
315 rc = VERR_INVALID_PARAMETER;
316 else
317 {
318 DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
319 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
320 paParms[0].getUInt32(&data.uDefAction);
321 paParms[1].getUInt32(&data.uAllActions);
322 uint32_t cTmp;
323 paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
324 if (m_pfnHostCallback)
325 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
326 }
327 break;
328 }
329 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
330 {
331 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
332 if ( cParms != 2
333 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* data */
334 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
335 rc = VERR_INVALID_PARAMETER;
336 else
337 {
338 DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
339 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
340 paParms[0].getPointer((void**)&data.pvData, &data.cbData);
341 paParms[1].getUInt32(&data.cbTotalSize);
342 if (m_pfnHostCallback)
343 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
344 }
345 break;
346 }
347 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
348 {
349 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
350 if ( cParms != 3
351 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
352 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* path length */
353 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
354 rc = VERR_INVALID_PARAMETER;
355 else
356 {
357 DragAndDropSvc::VBOXDNDCBSNDDIRDATA data;
358 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR;
359 uint32_t cTmp;
360 paParms[0].getPointer((void**)&data.pszPath, &cTmp);
361 paParms[1].getUInt32(&data.cbPath);
362 paParms[2].getUInt32(&data.fMode);
363#ifdef DEBUG_andy
364 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n",
365 data.pszPath, data.cbPath, data.fMode));
366#endif
367 if (m_pfnHostCallback)
368 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
369 }
370 break;
371 }
372 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
373 {
374 LogFlowFunc(("GUEST_DND_GH_SND_FILE\n"));
375 if ( cParms != 5
376 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* file path */
377 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
378 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* file data */
379 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */
380 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
381 rc = VERR_INVALID_PARAMETER;
382 else
383 {
384 DragAndDropSvc::VBOXDNDCBSNDFILEDATA data;
385 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE;
386 uint32_t cTmp;
387 paParms[0].getPointer((void**)&data.pszFilePath, &cTmp);
388 paParms[1].getUInt32(&data.cbFilePath);
389 paParms[2].getPointer((void**)&data.pvData, &data.cbData);
390 /* paParms[3] is cbData. */
391 paParms[4].getUInt32(&data.fMode);
392#ifdef DEBUG_andy
393 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
394 data.pszFilePath, data.cbData, data.pvData, data.fMode));
395#endif
396 if (m_pfnHostCallback)
397 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
398 }
399 break;
400 }
401 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
402 {
403 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
404 if ( cParms != 1
405 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
406 rc = VERR_INVALID_PARAMETER;
407 else
408 {
409 DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
410 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
411
412 uint32_t rcOp;
413 paParms[0].getUInt32(&rcOp);
414 data.rc = rcOp;
415
416 if (m_pfnHostCallback)
417 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
418 }
419 break;
420 }
421#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
422 default:
423 {
424 /* All other messages are handled by the DnD manager. */
425 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
426 break;
427 }
428 }
429 }
430 else
431 rc = VERR_ACCESS_DENIED;
432
433 /* If async execute is requested, we didn't notify the guest about
434 * completion. The client is queued into the waiters list and will be
435 * notified as soon as a new event is available. */
436 if ( rc != VINF_HGCM_ASYNC_EXECUTE
437 && m_pHelpers)
438 {
439 m_pHelpers->pfnCallComplete(callHandle, rc);
440 }
441
442 LogFlowFunc(("Returning rc=%Rrc\n", rc));
443}
444
445int DragAndDropService::hostCall(uint32_t u32Function,
446 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
447{
448 LogFlowFunc(("u32Function=%RU32, cParms=%RU32\n", u32Function, cParms));
449
450 int rc;
451 if (u32Function == DragAndDropSvc::HOST_DND_SET_MODE)
452 {
453 if (cParms != 1)
454 rc = VERR_INVALID_PARAMETER;
455 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
456 rc = VERR_INVALID_PARAMETER;
457 else
458 rc = modeSet(paParms[0].u.uint32);
459 }
460 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
461 {
462 if (!m_clientQueue.isEmpty()) /* At least one client on the guest connected? */
463 {
464 rc = m_pManager->addMessage(u32Function, cParms, paParms);
465 if (RT_SUCCESS(rc))
466 {
467 HGCM::Client *pClient = m_clientQueue.first();
468 AssertPtr(pClient);
469
470 /* Check if this was a request for getting the next host
471 * message. If so, return the message id and the parameter
472 * count. The message itself has to be queued. */
473 uint32_t uMsg = pClient->message();
474 if (uMsg == DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG)
475 {
476 LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
477
478 uint32_t uMsg1;
479 uint32_t cParms1;
480 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
481 if (RT_SUCCESS(rc))
482 {
483 pClient->addMessageInfo(uMsg1, cParms1);
484 if (m_pHelpers)
485 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
486
487 m_clientQueue.removeFirst();
488 delete pClient;
489 }
490 else
491 AssertMsgFailed(("m_pManager::nextMessageInfo failed with rc=%Rrc\n", rc));
492 }
493 else
494 AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
495 pClient->clientId(), uMsg));
496 }
497 else
498 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
499 u32Function, rc));
500 }
501 else
502 {
503 /* Tell the host that the guest does not support drag'n drop.
504 * This might happen due to not installed Guest Additions or
505 * not running VBoxTray/VBoxClient. */
506 rc = VERR_NOT_SUPPORTED;
507 }
508 }
509 else
510 {
511 /* Tell the host that a wrong drag'n drop mode is set. */
512 rc = VERR_ACCESS_DENIED;
513 }
514
515 LogFlowFunc(("rc=%Rrc\n", rc));
516 return rc;
517}
518
519DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser)
520{
521 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
522
523 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
524 AssertPtr(pSelf);
525
526 if (pSelf->m_pfnHostCallback)
527 {
528 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uPercentage=%RU32, uState=%RU32, rc=%Rrc\n",
529 uPercentage, uState, rc));
530 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
531 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
532 data.uPercentage = RT_MIN(uPercentage, 100);
533 data.uState = uState;
534 data.rc = rc;
535
536 return pSelf->m_pfnHostCallback(pSelf->m_pvHostData,
537 DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS,
538 &data, sizeof(data));
539 }
540
541 return VINF_SUCCESS;
542}
543
544/**
545 * @copydoc VBOXHGCMSVCLOAD
546 */
547extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
548{
549 return DragAndDropService::svcLoad(pTable);
550}
551
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