VirtualBox

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

Last change on this file since 76224 was 75969, checked in by vboxsync, 6 years ago

HGCM,GuestProps: Added HGCM service notifications for VM power-on, VM resume, VM suspend, VM reset and VM power-off. Made use of the first two in guest properties to wake up guest programs waiting on any of the host version properties. Also moved inserting sysprep and host version properties to the services as that's a better home than ConsoleImpl/VMMDevInterface in my opinion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.9 KB
Line 
1/* $Id: service.cpp 75969 2018-12-05 12:08:09Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2018 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 Drag and drop HGCM Service
19 *
20 * TODO
21 */
22
23
24/*********************************************************************************************************************************
25* Header Files *
26*********************************************************************************************************************************/
27#ifdef LOG_GROUP
28 #undef LOG_GROUP
29#endif
30#define LOG_GROUP LOG_GROUP_GUEST_DND
31
32#include <algorithm>
33#include <list>
34#include <map>
35
36#include <VBox/GuestHost/DragAndDrop.h>
37#include <VBox/GuestHost/DragAndDropDefs.h>
38#include <VBox/HostServices/Service.h>
39#include <VBox/HostServices/DragAndDropSvc.h>
40
41#include "dndmanager.h"
42
43using namespace DragAndDropSvc;
44
45
46/*********************************************************************************************************************************
47* Service class declaration *
48*********************************************************************************************************************************/
49
50class DragAndDropClient : public HGCM::Client
51{
52public:
53
54 DragAndDropClient(uint32_t uClientID)
55 : HGCM::Client(uClientID)
56 {
57 RT_ZERO(m_SvcCtx);
58 }
59
60 virtual ~DragAndDropClient(void)
61 {
62 disconnect();
63 }
64
65public:
66
67 void disconnect(void);
68};
69
70/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
71typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
72
73/** Simple queue (list) which holds deferred (waiting) clients. */
74typedef std::list<uint32_t> DnDClientQueue;
75
76/**
77 * Specialized drag & drop service class.
78 */
79class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
80{
81public:
82
83 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
84 : HGCM::AbstractService<DragAndDropService>(pHelpers)
85 , m_pManager(NULL) {}
86
87protected:
88
89 int init(VBOXHGCMSVCFNTABLE *pTable);
90 int uninit(void);
91 int clientConnect(uint32_t u32ClientID, void *pvClient);
92 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
93 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
94 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
95
96 int modeSet(uint32_t u32Mode);
97 inline uint32_t modeGet(void) const { return m_u32Mode; };
98
99protected:
100
101 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
102
103protected:
104
105 /** Pointer to our DnD manager instance. */
106 DnDManager *m_pManager;
107 /** Map of all connected clients.
108 * The primary key is the (unique) client ID, the secondary value
109 * an allocated pointer to the DragAndDropClient class, managed
110 * by this service class. */
111 DnDClientMap m_clientMap;
112 /** List of all clients which are queued up (deferred return) and ready
113 * to process new commands. The key is the (unique) client ID. */
114 DnDClientQueue m_clientQueue;
115 /** Current drag and drop mode. */
116 uint32_t m_u32Mode;
117};
118
119
120/*********************************************************************************************************************************
121* Client implementation *
122*********************************************************************************************************************************/
123
124/**
125 * Called when the HGCM client disconnected on the guest side.
126 * This function takes care of the client's data cleanup and also lets the host
127 * know that the client has been disconnected.
128 *
129 */
130void DragAndDropClient::disconnect(void)
131{
132 LogFlowThisFunc(("uClient=%RU32\n", m_uClientID));
133
134 if (IsDeferred())
135 CompleteDeferred(VERR_INTERRUPTED);
136
137 /*
138 * Let the host know.
139 */
140 VBOXDNDCBDISCONNECTMSGDATA data;
141 RT_ZERO(data);
142 /** @todo Magic needed? */
143 /** @todo Add context ID. */
144
145 if (m_SvcCtx.pfnHostCallback)
146 {
147 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_DISCONNECT, &data, sizeof(data));
148 if (RT_FAILURE(rc2))
149 LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientID, rc2));
150 /* Not fatal. */
151 }
152}
153
154
155/*********************************************************************************************************************************
156* Service class implementation *
157*********************************************************************************************************************************/
158
159int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
160{
161 /* Register functions. */
162 pTable->pfnHostCall = svcHostCall;
163 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
164 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
165 pTable->pfnRegisterExtension = svcRegisterExtension;
166 pTable->pfnNotify = NULL;
167
168 /* Drag'n drop mode is disabled by default. */
169 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
170
171 int rc = VINF_SUCCESS;
172
173 try
174 {
175 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
176 }
177 catch(std::bad_alloc &)
178 {
179 rc = VERR_NO_MEMORY;
180 }
181
182 LogFlowFuncLeaveRC(rc);
183 return rc;
184}
185
186int DragAndDropService::uninit(void)
187{
188 LogFlowFuncEnter();
189
190 if (m_pManager)
191 {
192 delete m_pManager;
193 m_pManager = NULL;
194 }
195
196 DnDClientMap::iterator itClient = m_clientMap.begin();
197 while (itClient != m_clientMap.end())
198 {
199 delete itClient->second;
200 m_clientMap.erase(itClient);
201 itClient = m_clientMap.begin();
202 }
203
204 LogFlowFuncLeave();
205 return VINF_SUCCESS;
206}
207
208int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
209{
210 RT_NOREF1(pvClient);
211 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
212 {
213 AssertMsgFailed(("Maximum number of clients reached\n"));
214 return VERR_MAX_PROCS_REACHED;
215 }
216
217 int rc = VINF_SUCCESS;
218
219 /*
220 * Add client to our client map.
221 */
222 if (m_clientMap.find(u32ClientID) != m_clientMap.end())
223 rc = VERR_ALREADY_EXISTS;
224
225 if (RT_SUCCESS(rc))
226 {
227 try
228 {
229 DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
230 pClient->SetSvcContext(m_SvcCtx);
231 m_clientMap[u32ClientID] = pClient;
232 }
233 catch(std::bad_alloc &)
234 {
235 rc = VERR_NO_MEMORY;
236 }
237
238 if (RT_SUCCESS(rc))
239 {
240 /*
241 * Reset the message queue as soon as a new clients connect
242 * to ensure that every client has the same state.
243 */
244 if (m_pManager)
245 m_pManager->Reset();
246 }
247 }
248
249 LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
250 return rc;
251}
252
253int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
254{
255 RT_NOREF1(pvClient);
256
257 /* Client not found? Bail out early. */
258 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
259 if (itClient == m_clientMap.end())
260 return VERR_NOT_FOUND;
261
262 /*
263 * Remove from waiters queue.
264 */
265 m_clientQueue.remove(u32ClientID);
266
267 /*
268 * Remove from client map and deallocate.
269 */
270 AssertPtr(itClient->second);
271 delete itClient->second;
272
273 m_clientMap.erase(itClient);
274
275 LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
276 return VINF_SUCCESS;
277}
278
279int DragAndDropService::modeSet(uint32_t u32Mode)
280{
281#ifndef VBOX_WITH_DRAG_AND_DROP_GH
282 if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
283 || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
284 {
285 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
286 return VERR_NOT_SUPPORTED;
287 }
288#endif
289
290 switch (u32Mode)
291 {
292 case VBOX_DRAG_AND_DROP_MODE_OFF:
293 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
294 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
295 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
296 m_u32Mode = u32Mode;
297 break;
298
299 default:
300 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
301 break;
302 }
303
304 return VINF_SUCCESS;
305}
306
307void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
308 void *pvClient, uint32_t u32Function,
309 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
310{
311 RT_NOREF1(pvClient);
312 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
313 u32ClientID, u32Function, cParms));
314
315 /* Check if we've the right mode set. */
316 int rc = VERR_ACCESS_DENIED; /* Play safe. */
317 switch (u32Function)
318 {
319 case GUEST_DND_GET_NEXT_HOST_MSG:
320 {
321 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
322 {
323 rc = VINF_SUCCESS;
324 }
325 else
326 {
327 LogFlowFunc(("DnD disabled, deferring request\n"));
328 rc = VINF_HGCM_ASYNC_EXECUTE;
329 }
330 break;
331 }
332
333 /* New since protocol v2. */
334 case GUEST_DND_CONNECT:
335 {
336 /*
337 * Never block the initial connect call, as the clients do this when
338 * initializing and might get stuck if drag and drop is set to "disabled" at
339 * that time.
340 */
341 rc = VINF_SUCCESS;
342 break;
343 }
344 case GUEST_DND_HG_ACK_OP:
345 case GUEST_DND_HG_REQ_DATA:
346 case GUEST_DND_HG_EVT_PROGRESS:
347 {
348 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
349 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
350 {
351 rc = VINF_SUCCESS;
352 }
353 else
354 LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
355 break;
356 }
357
358 case GUEST_DND_GH_ACK_PENDING:
359 case GUEST_DND_GH_SND_DATA_HDR:
360 case GUEST_DND_GH_SND_DATA:
361 case GUEST_DND_GH_SND_DIR:
362 case GUEST_DND_GH_SND_FILE_HDR:
363 case GUEST_DND_GH_SND_FILE_DATA:
364 case GUEST_DND_GH_EVT_ERROR:
365 {
366#ifdef VBOX_WITH_DRAG_AND_DROP_GH
367 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
368 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
369 {
370 rc = VINF_SUCCESS;
371 }
372 else
373#endif
374 LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
375 break;
376 }
377
378 default:
379 /* Reach through to DnD manager. */
380 rc = VINF_SUCCESS;
381 break;
382 }
383
384#ifdef DEBUG_andy
385 LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
386#endif
387
388#define DO_HOST_CALLBACK(); \
389 if ( RT_SUCCESS(rc) \
390 && m_SvcCtx.pfnHostCallback) \
391 { \
392 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
393 }
394
395 /*
396 * Lookup client.
397 */
398 DragAndDropClient *pClient = NULL;
399
400 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
401 if (itClient != m_clientMap.end())
402 {
403 pClient = itClient->second;
404 AssertPtr(pClient);
405 }
406 else
407 {
408 LogFunc(("Client %RU32 was not found\n", u32ClientID));
409 rc = VERR_NOT_FOUND;
410 }
411
412 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
413 {
414 LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
415
416 rc = VERR_INVALID_PARAMETER; /* Play safe. */
417
418 switch (u32Function)
419 {
420 /*
421 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
422 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
423 * HOST_DND_GH_REQ_PENDING, which led this service returning
424 * VERR_INVALID_PARAMETER when the guest wanted to actually
425 * handle HOST_DND_GH_REQ_PENDING.
426 */
427 case GUEST_DND_GET_NEXT_HOST_MSG:
428 {
429 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
430 if (cParms == 3)
431 {
432 rc = m_pManager->GetNextMsgInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
433 if (RT_FAILURE(rc)) /* No queued messages available? */
434 {
435 if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
436 {
437 VBOXDNDCBHGGETNEXTHOSTMSG data;
438 RT_ZERO(data);
439 data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
440 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
441 if (RT_SUCCESS(rc))
442 {
443 paParms[0].u.uint32 = data.uMsg; /* uMsg */
444 paParms[1].u.uint32 = data.cParms; /* cParms */
445 /* Note: paParms[2] was set by the guest as blocking flag. */
446 }
447 }
448 else /* No host callback in place, so drag and drop is not supported by the host. */
449 rc = VERR_NOT_SUPPORTED;
450
451 if (RT_FAILURE(rc))
452 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
453
454 /* Some error occurred or no (new) messages available? */
455 if (RT_FAILURE(rc))
456 {
457 uint32_t fFlags = 0;
458 int rc2 = HGCMSvcGetU32(&paParms[2], &fFlags);
459 if ( RT_SUCCESS(rc2)
460 && fFlags) /* Blocking flag set? */
461 {
462 /* Defer client returning. */
463 rc = VINF_HGCM_ASYNC_EXECUTE;
464 }
465 else
466 rc = VERR_INVALID_PARAMETER;
467
468 LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
469 }
470 }
471 }
472 break;
473 }
474 case GUEST_DND_CONNECT:
475 {
476 LogFlowFunc(("GUEST_DND_CONNECT\n"));
477 if (cParms >= 2)
478 {
479 const uint8_t idxProto = cParms >= 3 ? 1 : 0;
480
481 VBOXDNDCBCONNECTMSGDATA data;
482 RT_ZERO(data);
483 data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
484 if (cParms >= 3)
485 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
486 else /* Older protocols don't have a context ID. */
487 rc = VINF_SUCCESS;
488 if (RT_SUCCESS(rc))
489 rc = HGCMSvcGetU32(&paParms[idxProto], &data.uProtocol);
490 if (RT_SUCCESS(rc))
491 rc = HGCMSvcGetU32(&paParms[idxProto + 1], &data.uFlags);
492 if (RT_SUCCESS(rc))
493 pClient->SetProtocolVer(data.uProtocol);
494 if (RT_SUCCESS(rc))
495 {
496 LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
497 DO_HOST_CALLBACK();
498 }
499 }
500 break;
501 }
502 case GUEST_DND_HG_ACK_OP:
503 {
504 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
505
506 VBOXDNDCBHGACKOPDATA data;
507 RT_ZERO(data);
508 data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
509
510 switch (pClient->GetProtocolVer())
511 {
512 case 3:
513 {
514 if (cParms == 2)
515 {
516 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
517 if (RT_SUCCESS(rc))
518 rc = HGCMSvcGetU32(&paParms[1], &data.uAction); /* Get drop action. */
519 }
520 break;
521 }
522
523 case 2:
524 default:
525 {
526 if (cParms == 1)
527 rc = HGCMSvcGetU32(&paParms[0], &data.uAction); /* Get drop action. */
528 break;
529 }
530 }
531
532 DO_HOST_CALLBACK();
533 break;
534 }
535 case GUEST_DND_HG_REQ_DATA:
536 {
537 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
538
539 VBOXDNDCBHGREQDATADATA data;
540 RT_ZERO(data);
541 data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
542
543 switch (pClient->GetProtocolVer())
544 {
545 case 3:
546 {
547 if (cParms == 3)
548 {
549 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
550 if (RT_SUCCESS(rc))
551 rc = HGCMSvcGetPv(&paParms[1], (void **)&data.pszFormat, &data.cbFormat);
552 if (RT_SUCCESS(rc))
553 rc = HGCMSvcGetU32(&paParms[2], &data.cbFormat);
554 }
555 break;
556 }
557
558 case 2:
559 default:
560 {
561 if (cParms == 1)
562 rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszFormat, &data.cbFormat);
563 break;
564 }
565 }
566
567 DO_HOST_CALLBACK();
568 break;
569 }
570 case GUEST_DND_HG_EVT_PROGRESS:
571 {
572 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
573
574 VBOXDNDCBHGEVTPROGRESSDATA data;
575 RT_ZERO(data);
576 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
577
578 switch (pClient->GetProtocolVer())
579 {
580 case 3:
581 {
582 if (cParms == 4)
583 {
584 rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
585 if (RT_SUCCESS(rc))
586 rc = HGCMSvcGetU32(&paParms[1], &data.uStatus);
587 if (RT_SUCCESS(rc))
588 rc = HGCMSvcGetU32(&paParms[2], &data.uPercentage);
589 if (RT_SUCCESS(rc))
590 rc = HGCMSvcGetU32(&paParms[3], &data.rc);
591 }
592 break;
593 }
594
595 case 2:
596 default:
597 {
598 if (cParms == 3)
599 {
600 rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
601 if (RT_SUCCESS(rc))
602 rc = HGCMSvcGetU32(&paParms[1], &data.uPercentage);
603 if (RT_SUCCESS(rc))
604 rc = HGCMSvcGetU32(&paParms[2], &data.rc);
605 }
606 break;
607 }
608 }
609
610 DO_HOST_CALLBACK();
611 break;
612 }
613#ifdef VBOX_WITH_DRAG_AND_DROP_GH
614 case GUEST_DND_GH_ACK_PENDING:
615 {
616 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
617
618 VBOXDNDCBGHACKPENDINGDATA data;
619 RT_ZERO(data);
620 data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
621
622 switch (pClient->GetProtocolVer())
623 {
624 case 3:
625 {
626 if (cParms == 5)
627 {
628 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
629 if (RT_SUCCESS(rc))
630 rc = HGCMSvcGetU32(&paParms[1], &data.uDefAction);
631 if (RT_SUCCESS(rc))
632 rc = HGCMSvcGetU32(&paParms[2], &data.uAllActions);
633 if (RT_SUCCESS(rc))
634 rc = HGCMSvcGetPv(&paParms[3], (void**)&data.pszFormat, &data.cbFormat);
635 if (RT_SUCCESS(rc))
636 rc = HGCMSvcGetU32(&paParms[4], &data.cbFormat);
637 }
638 break;
639 }
640
641 case 2:
642 default:
643 {
644 if (cParms == 3)
645 {
646 rc = HGCMSvcGetU32(&paParms[0], &data.uDefAction);
647 if (RT_SUCCESS(rc))
648 rc = HGCMSvcGetU32(&paParms[1], &data.uAllActions);
649 if (RT_SUCCESS(rc))
650 rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pszFormat, &data.cbFormat);
651 }
652 break;
653 }
654 }
655
656 DO_HOST_CALLBACK();
657 break;
658 }
659 /* New since protocol v3. */
660 case GUEST_DND_GH_SND_DATA_HDR:
661 {
662 LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n"));
663 if (cParms == 12)
664 {
665 VBOXDNDCBSNDDATAHDRDATA data;
666 RT_ZERO(data);
667 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
668 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
669 if (RT_SUCCESS(rc))
670 rc = HGCMSvcGetU32(&paParms[1], &data.data.uFlags);
671 if (RT_SUCCESS(rc))
672 rc = HGCMSvcGetU32(&paParms[2], &data.data.uScreenId);
673 if (RT_SUCCESS(rc))
674 rc = HGCMSvcGetU64(&paParms[3], &data.data.cbTotal);
675 if (RT_SUCCESS(rc))
676 rc = HGCMSvcGetU32(&paParms[4], &data.data.cbMeta);
677 if (RT_SUCCESS(rc))
678 rc = HGCMSvcGetPv(&paParms[5], &data.data.pvMetaFmt, &data.data.cbMetaFmt);
679 if (RT_SUCCESS(rc))
680 rc = HGCMSvcGetU32(&paParms[6], &data.data.cbMetaFmt);
681 if (RT_SUCCESS(rc))
682 rc = HGCMSvcGetU64(&paParms[7], &data.data.cObjects);
683 if (RT_SUCCESS(rc))
684 rc = HGCMSvcGetU32(&paParms[8], &data.data.enmCompression);
685 if (RT_SUCCESS(rc))
686 rc = HGCMSvcGetU32(&paParms[9], (uint32_t *)&data.data.enmChecksumType);
687 if (RT_SUCCESS(rc))
688 rc = HGCMSvcGetPv(&paParms[10], &data.data.pvChecksum, &data.data.cbChecksum);
689 if (RT_SUCCESS(rc))
690 rc = HGCMSvcGetU32(&paParms[11], &data.data.cbChecksum);
691
692 LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n",
693 data.data.uFlags, data.data.cbTotal, data.data.cObjects));
694 DO_HOST_CALLBACK();
695 }
696 break;
697 }
698 case GUEST_DND_GH_SND_DATA:
699 {
700 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
701 switch (pClient->GetProtocolVer())
702 {
703 case 3:
704 {
705 if (cParms == 5)
706 {
707 VBOXDNDCBSNDDATADATA data;
708 RT_ZERO(data);
709 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
710 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
711 if (RT_SUCCESS(rc))
712 rc = HGCMSvcGetPv(&paParms[1], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
713 if (RT_SUCCESS(rc))
714 rc = HGCMSvcGetU32(&paParms[2], &data.data.u.v3.cbData);
715 if (RT_SUCCESS(rc))
716 rc = HGCMSvcGetPv(&paParms[3], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
717 if (RT_SUCCESS(rc))
718 rc = HGCMSvcGetU32(&paParms[4], &data.data.u.v3.cbChecksum);
719 DO_HOST_CALLBACK();
720 }
721 break;
722 }
723
724 case 2:
725 default:
726 {
727 if (cParms == 2)
728 {
729 VBOXDNDCBSNDDATADATA data;
730 RT_ZERO(data);
731 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
732 rc = HGCMSvcGetPv(&paParms[0], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
733 if (RT_SUCCESS(rc))
734 rc = HGCMSvcGetU32(&paParms[1], &data.data.u.v1.cbTotalSize);
735 DO_HOST_CALLBACK();
736 }
737 break;
738 }
739 }
740 break;
741 }
742 case GUEST_DND_GH_SND_DIR:
743 {
744 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
745
746 VBOXDNDCBSNDDIRDATA data;
747 RT_ZERO(data);
748 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
749
750 switch (pClient->GetProtocolVer())
751 {
752 case 3:
753 {
754 if (cParms == 4)
755 {
756 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
757 if (RT_SUCCESS(rc))
758 rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszPath, &data.cbPath);
759 if (RT_SUCCESS(rc))
760 rc = HGCMSvcGetU32(&paParms[2], &data.cbPath);
761 if (RT_SUCCESS(rc))
762 rc = HGCMSvcGetU32(&paParms[3], &data.fMode);
763 }
764 break;
765 }
766
767 case 2:
768 default:
769 {
770 if (cParms == 3)
771 {
772 rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszPath, &data.cbPath);
773 if (RT_SUCCESS(rc))
774 rc = HGCMSvcGetU32(&paParms[1], &data.cbPath);
775 if (RT_SUCCESS(rc))
776 rc = HGCMSvcGetU32(&paParms[2], &data.fMode);
777 }
778 break;
779 }
780 }
781
782 DO_HOST_CALLBACK();
783 break;
784 }
785 /* New since protocol v2 (>= VBox 5.0). */
786 case GUEST_DND_GH_SND_FILE_HDR:
787 {
788 LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
789 if (cParms == 6)
790 {
791 VBOXDNDCBSNDFILEHDRDATA data;
792 RT_ZERO(data);
793 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
794
795 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
796 if (RT_SUCCESS(rc))
797 rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszFilePath, &data.cbFilePath);
798 if (RT_SUCCESS(rc))
799 rc = HGCMSvcGetU32(&paParms[2], &data.cbFilePath);
800 if (RT_SUCCESS(rc))
801 rc = HGCMSvcGetU32(&paParms[3], &data.fFlags);
802 if (RT_SUCCESS(rc))
803 rc = HGCMSvcGetU32(&paParms[4], &data.fMode);
804 if (RT_SUCCESS(rc))
805 rc = HGCMSvcGetU64(&paParms[5], &data.cbSize);
806
807 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
808 data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
809 DO_HOST_CALLBACK();
810 }
811 break;
812 }
813 case GUEST_DND_GH_SND_FILE_DATA:
814 {
815 LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
816
817 switch (pClient->GetProtocolVer())
818 {
819 /* Protocol v3 adds (optional) checksums. */
820 case 3:
821 {
822 if (cParms == 5)
823 {
824 VBOXDNDCBSNDFILEDATADATA data;
825 RT_ZERO(data);
826 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
827
828 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
829 if (RT_SUCCESS(rc))
830 rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
831 if (RT_SUCCESS(rc))
832 rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
833 if (RT_SUCCESS(rc))
834 rc = HGCMSvcGetPv(&paParms[3], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
835 if (RT_SUCCESS(rc))
836 rc = HGCMSvcGetU32(&paParms[4], &data.u.v3.cbChecksum);
837
838 LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData));
839 DO_HOST_CALLBACK();
840 }
841 break;
842 }
843 /* Protocol v2 only sends the next data chunks to reduce traffic. */
844 case 2:
845 {
846 if (cParms == 3)
847 {
848 VBOXDNDCBSNDFILEDATADATA data;
849 RT_ZERO(data);
850 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
851 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
852 if (RT_SUCCESS(rc))
853 rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
854 if (RT_SUCCESS(rc))
855 rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
856
857 LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
858 DO_HOST_CALLBACK();
859 }
860 break;
861 }
862 /* Protocol v1 sends the file path and attributes for every file chunk (!). */
863 default:
864 {
865 if (cParms == 5)
866 {
867 VBOXDNDCBSNDFILEDATADATA data;
868 RT_ZERO(data);
869 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
870 uint32_t cTmp;
871 rc = HGCMSvcGetPv(&paParms[0], (void**)&data.u.v1.pszFilePath, &cTmp);
872 if (RT_SUCCESS(rc))
873 rc = HGCMSvcGetU32(&paParms[1], &data.u.v1.cbFilePath);
874 if (RT_SUCCESS(rc))
875 rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pvData, &cTmp);
876 if (RT_SUCCESS(rc))
877 rc = HGCMSvcGetU32(&paParms[3], &data.cbData);
878 if (RT_SUCCESS(rc))
879 rc = HGCMSvcGetU32(&paParms[4], &data.u.v1.fMode);
880
881 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
882 data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
883 DO_HOST_CALLBACK();
884 }
885 break;
886 }
887 }
888 break;
889 }
890 case GUEST_DND_GH_EVT_ERROR:
891 {
892 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
893
894 VBOXDNDCBEVTERRORDATA data;
895 RT_ZERO(data);
896 data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
897
898 switch (pClient->GetProtocolVer())
899 {
900 case 3:
901 {
902 if (cParms == 2)
903 {
904 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
905 if (RT_SUCCESS(rc))
906 {
907 uint32_t rcOp;
908 rc = HGCMSvcGetU32(&paParms[1], &rcOp);
909 if (RT_SUCCESS(rc))
910 data.rc = rcOp;
911 }
912 }
913 break;
914 }
915
916 case 2:
917 default:
918 {
919 if (cParms == 1)
920 {
921 uint32_t rcOp;
922 rc = HGCMSvcGetU32(&paParms[0], &rcOp);
923 if (RT_SUCCESS(rc))
924 data.rc = (int32_t)rcOp;
925 }
926 break;
927 }
928 }
929
930 DO_HOST_CALLBACK();
931 break;
932 }
933#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
934
935 /*
936 * Note: This is a fire-and-forget message, as the host should
937 * not rely on an answer from the guest side in order to
938 * properly cancel the operation.
939 */
940 case HOST_DND_HG_EVT_CANCEL:
941 {
942 LogFlowFunc(("HOST_DND_HG_EVT_CANCEL\n"));
943
944 VBOXDNDCBEVTERRORDATA data;
945 RT_ZERO(data);
946 data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
947
948 switch (pClient->GetProtocolVer())
949 {
950 case 3:
951 {
952 /* Protocol v3+ at least requires the context ID. */
953 if (cParms == 1)
954 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
955
956 break;
957 }
958
959 default:
960 break;
961 }
962
963 /* Tell the host that the guest has cancelled the operation. */
964 data.rc = VERR_CANCELLED;
965
966 DO_HOST_CALLBACK();
967
968 /* Note: If the host is not prepared for handling the cancelling reply
969 * from the guest, don't report this back to the guest. */
970 if (RT_FAILURE(rc))
971 rc = VINF_SUCCESS;
972 break;
973 }
974
975 default:
976 {
977 /* All other messages are handled by the DnD manager. */
978 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
979 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
980 {
981 if (m_SvcCtx.pfnHostCallback)
982 {
983 VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
984 RT_ZERO(data);
985
986 data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
987
988 data.uMsg = u32Function;
989 data.cParms = cParms;
990 data.paParms = paParms;
991
992 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
993 &data, sizeof(data));
994 if (RT_SUCCESS(rc))
995 {
996 cParms = data.cParms;
997 paParms = data.paParms;
998 }
999 else
1000 {
1001 /*
1002 * In case the guest is too fast asking for the next message
1003 * and the host did not supply it yet, just defer the client's
1004 * return until a response from the host available.
1005 */
1006 LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\n", rc));
1007 rc = VINF_HGCM_ASYNC_EXECUTE;
1008 }
1009 }
1010 else /* No host callback in place, so drag and drop is not supported by the host. */
1011 rc = VERR_NOT_SUPPORTED;
1012 }
1013 break;
1014 }
1015 }
1016 }
1017
1018 /*
1019 * If async execution is requested, we didn't notify the guest yet about
1020 * completion. The client is queued into the waiters list and will be
1021 * notified as soon as a new event is available.
1022 */
1023 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1024 {
1025 try
1026 {
1027 AssertPtr(pClient);
1028 pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
1029 m_clientQueue.push_back(u32ClientID);
1030 }
1031 catch (std::bad_alloc &)
1032 {
1033 rc = VERR_NO_MEMORY;
1034 /* Don't report to guest. */
1035 }
1036 }
1037 else if (pClient)
1038 pClient->Complete(callHandle, rc);
1039 else
1040 {
1041 AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
1042 rc = VERR_NOT_IMPLEMENTED;
1043 }
1044
1045 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1046}
1047
1048int DragAndDropService::hostCall(uint32_t u32Function,
1049 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1050{
1051 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
1052 u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
1053
1054 int rc;
1055
1056 do
1057 {
1058 bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
1059
1060 switch (u32Function)
1061 {
1062 case HOST_DND_SET_MODE:
1063 {
1064 if (cParms != 1)
1065 rc = VERR_INVALID_PARAMETER;
1066 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
1067 rc = VERR_INVALID_PARAMETER;
1068 else
1069 rc = modeSet(paParms[0].u.uint32);
1070 break;
1071 }
1072
1073 case HOST_DND_HG_EVT_ENTER:
1074 {
1075 /* Reset the message queue as a new DnD operation just began. */
1076 m_pManager->Reset();
1077
1078 fSendToGuest = true;
1079 rc = VINF_SUCCESS;
1080 break;
1081 }
1082
1083 case HOST_DND_HG_EVT_CANCEL:
1084 {
1085 LogFlowFunc(("Cancelling all waiting clients ...\n"));
1086
1087 /* Reset the message queue as the host cancelled the whole operation. */
1088 m_pManager->Reset();
1089
1090 /*
1091 * Wake up all deferred clients and tell them to process
1092 * the cancelling message next.
1093 */
1094 DnDClientQueue::iterator itQueue = m_clientQueue.begin();
1095 while (itQueue != m_clientQueue.end())
1096 {
1097 DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
1098 Assert(itClient != m_clientMap.end());
1099
1100 DragAndDropClient *pClient = itClient->second;
1101 AssertPtr(pClient);
1102
1103 int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_HG_EVT_CANCEL,
1104 /* Protocol v3+ also contains the context ID. */
1105 pClient->GetProtocolVer() >= 3 ? 1 : 0);
1106 pClient->CompleteDeferred(rc2);
1107
1108 m_clientQueue.erase(itQueue);
1109 itQueue = m_clientQueue.begin();
1110 }
1111
1112 Assert(m_clientQueue.empty());
1113
1114 /* Tell the host that everything went well. */
1115 rc = VINF_SUCCESS;
1116 break;
1117 }
1118
1119 default:
1120 {
1121 fSendToGuest = true;
1122 rc = VINF_SUCCESS;
1123 break;
1124 }
1125 }
1126
1127 if (fSendToGuest)
1128 {
1129 if (modeGet() == VBOX_DRAG_AND_DROP_MODE_OFF)
1130 {
1131 /* Tell the host that a wrong drag'n drop mode is set. */
1132 rc = VERR_ACCESS_DENIED;
1133 break;
1134 }
1135
1136 if (m_clientMap.empty()) /* At least one client on the guest connected? */
1137 {
1138 /*
1139 * Tell the host that the guest does not support drag'n drop.
1140 * This might happen due to not installed Guest Additions or
1141 * not running VBoxTray/VBoxClient.
1142 */
1143 rc = VERR_NOT_SUPPORTED;
1144 break;
1145 }
1146
1147 rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
1148 if (RT_FAILURE(rc))
1149 {
1150 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
1151 break;
1152 }
1153
1154 /* Any clients in our queue ready for processing the next command? */
1155 if (m_clientQueue.empty())
1156 {
1157 LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
1158 break;
1159 }
1160
1161 uint32_t uClientNext = m_clientQueue.front();
1162 DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
1163 Assert(itClientNext != m_clientMap.end());
1164
1165 DragAndDropClient *pClient = itClientNext->second;
1166 AssertPtr(pClient);
1167
1168 /*
1169 * Check if this was a request for getting the next host
1170 * message. If so, return the message ID and the parameter
1171 * count. The message itself has to be queued.
1172 */
1173 uint32_t uMsgClient = pClient->GetMsgType();
1174
1175 uint32_t uMsgNext = 0;
1176 uint32_t cParmsNext = 0;
1177 int rcNext = m_pManager->GetNextMsgInfo(&uMsgNext, &cParmsNext);
1178
1179 LogFlowFunc(("uMsgClient=%RU32, uMsgNext=%RU32, cParmsNext=%RU32, rcNext=%Rrc\n",
1180 uMsgClient, uMsgNext, cParmsNext, rcNext));
1181
1182 if (RT_SUCCESS(rcNext))
1183 {
1184 if (uMsgClient == GUEST_DND_GET_NEXT_HOST_MSG)
1185 {
1186 rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext);
1187
1188 /* Note: Report the current rc back to the guest. */
1189 pClient->CompleteDeferred(rc);
1190 }
1191 /*
1192 * Does the message the client is waiting for match the message
1193 * next in the queue? Process it right away then.
1194 */
1195 else if (uMsgClient == uMsgNext)
1196 {
1197 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
1198
1199 /* Note: Report the current rc back to the guest. */
1200 pClient->CompleteDeferred(rc);
1201 }
1202 else /* Should not happen; cancel the operation on the guest. */
1203 {
1204 LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
1205 pClient->GetClientID(), uMsgClient, uMsgNext));
1206
1207 pClient->CompleteDeferred(VERR_CANCELLED);
1208 }
1209
1210 m_clientQueue.pop_front();
1211 }
1212
1213 } /* fSendToGuest */
1214
1215 } while (0); /* To use breaks. */
1216
1217 LogFlowFuncLeaveRC(rc);
1218 return rc;
1219}
1220
1221DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
1222{
1223 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1224
1225 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
1226 AssertPtr(pSelf);
1227
1228 if (pSelf->m_SvcCtx.pfnHostCallback)
1229 {
1230 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
1231 uStatus, uPercentage, rc));
1232
1233 VBOXDNDCBHGEVTPROGRESSDATA data;
1234 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
1235 data.uPercentage = RT_MIN(uPercentage, 100);
1236 data.uStatus = uStatus;
1237 data.rc = rc; /** @todo uin32_t vs. int. */
1238
1239 return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
1240 GUEST_DND_HG_EVT_PROGRESS,
1241 &data, sizeof(data));
1242 }
1243
1244 return VINF_SUCCESS;
1245}
1246
1247/**
1248 * @copydoc VBOXHGCMSVCLOAD
1249 */
1250extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
1251{
1252 return DragAndDropService::svcLoad(pTable);
1253}
1254
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