VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp@ 78396

Last change on this file since 78396 was 78346, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 78346 2019-04-29 11:07:26Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
19/** @page pg_hostclip The Shared Clipboard Host Service
20 *
21 * The shared clipboard host service provides a proxy between the host's
22 * clipboard and a similar proxy running on a guest. The service is split
23 * into a platform-independent core and platform-specific backends. The
24 * service defines two communication protocols - one to communicate with the
25 * clipboard service running on the guest, and one to communicate with the
26 * backend. These will be described in a very skeletal fashion here.
27 *
28 * @section sec_hostclip_guest_proto The guest communication protocol
29 *
30 * The guest clipboard service communicates with the host service via HGCM
31 * (the host service runs as an HGCM service). The guest clipboard must
32 * connect to the host service before all else (Windows hosts currently only
33 * support one simultaneous connection). Once it has connected, it can send
34 * HGCM messages to the host services, some of which will receive replies from
35 * the host. The host can only reply to a guest message, it cannot initiate
36 * any communication. The guest can in theory send any number of messages in
37 * parallel (see the descriptions of the messages for the practice), and the
38 * host will receive these in sequence, and may reply to them at once
39 * (releasing the caller in the guest) or defer the reply until later.
40 *
41 * There are currently four messages defined. The first is
42 * VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the
43 * host. Host messages currently defined are
44 * VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused),
45 * VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the
46 * contents of its clipboard to the host) and
47 * VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS (to notify the guest that new
48 * clipboard data is available). If a host message is sent while the guest is
49 * not waiting, it will be queued until the guest requests it. At most one
50 * host message of each type will be kept in the queue. The host code only
51 * supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call
52 * from the guest.
53 *
54 * The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells
55 * the host that the guest has new clipboard data available. The third is
56 * VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its
57 * clipboard data and waits until it arrives. The host supports at most one
58 * simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a
59 * second call is made before the first has returned, the first will be
60 * aborted.
61 *
62 * The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is
63 * used to send the contents of the guest clipboard to the host. This call
64 * should be used after the host has requested data from the guest.
65 *
66 * @section sec_hostclip_backend_proto The communication protocol with the
67 * platform-specific backend
68 *
69 * This section may be written in the future :)
70 */
71
72
73/*********************************************************************************************************************************
74* Header Files *
75*********************************************************************************************************************************/
76#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
77#include <VBox/HostServices/VBoxClipboardSvc.h>
78#include <VBox/HostServices/VBoxClipboardExt.h>
79
80#include <iprt/alloc.h>
81#include <iprt/string.h>
82#include <iprt/assert.h>
83#include <iprt/critsect.h>
84#include <VBox/err.h>
85#include <VBox/vmm/ssm.h>
86
87#include "VBoxClipboard.h"
88
89
90/*********************************************************************************************************************************
91* Global Variables *
92*********************************************************************************************************************************/
93static PVBOXHGCMSVCHELPERS g_pHelpers;
94
95static RTCRITSECT critsect;
96static uint32_t g_u32Mode;
97
98static PFNHGCMSVCEXT g_pfnExtension;
99static void *g_pvExtension;
100
101static VBOXCLIPBOARDCLIENTDATA *g_pClient;
102
103/* Serialization of data reading and format announcements from the RDP client. */
104static bool g_fReadingData = false;
105static bool g_fDelayedAnnouncement = false;
106static uint32_t g_u32DelayedFormats = 0;
107
108/** Is the clipboard running in headless mode? */
109static bool g_fHeadless = false;
110
111
112static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
113{
114 pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
115 pParm->u.uint32 = u32;
116}
117
118static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
119{
120 if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
121 {
122 *pu32 = pParm->u.uint32;
123 return VINF_SUCCESS;
124 }
125
126 return VERR_INVALID_PARAMETER;
127}
128
129#if 0
130static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
131{
132 pParm->type = VBOX_HGCM_SVC_PARM_PTR;
133 pParm->u.pointer.size = cb;
134 pParm->u.pointer.addr = pv;
135}
136#endif
137
138static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
139{
140 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
141 {
142 *ppv = pParm->u.pointer.addr;
143 *pcb = pParm->u.pointer.size;
144 return VINF_SUCCESS;
145 }
146
147 return VERR_INVALID_PARAMETER;
148}
149
150
151static uint32_t vboxSvcClipboardMode(void)
152{
153 return g_u32Mode;
154}
155
156#ifdef UNIT_TEST
157/** Testing interface, getter for clipboard mode */
158uint32_t TestClipSvcGetMode(void)
159{
160 return vboxSvcClipboardMode();
161}
162#endif
163
164/** Getter for headless setting. Also needed by testcase. */
165bool VBoxSvcClipboardGetHeadless(void)
166{
167 return g_fHeadless;
168}
169
170static void vboxSvcClipboardModeSet(uint32_t u32Mode)
171{
172 switch (u32Mode)
173 {
174 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
175 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
176 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
177 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
178 g_u32Mode = u32Mode;
179 break;
180
181 default:
182 g_u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
183 }
184}
185
186bool VBoxSvcClipboardLock(void)
187{
188 return RT_SUCCESS(RTCritSectEnter(&critsect));
189}
190
191void VBoxSvcClipboardUnlock(void)
192{
193 RTCritSectLeave(&critsect);
194}
195
196/* Set the HGCM parameters according to pending messages.
197 * Executed under the clipboard lock.
198 */
199static bool vboxSvcClipboardReturnMsg(VBOXCLIPBOARDCLIENTDATA *pClient, VBOXHGCMSVCPARM paParms[])
200{
201 /* Message priority is taken into account. */
202 if (pClient->fMsgQuit)
203 {
204 LogRelFlow(("vboxSvcClipboardReturnMsg: Quit\n"));
205 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
206 VBoxHGCMParmUInt32Set(&paParms[1], 0);
207 pClient->fMsgQuit = false;
208 }
209 else if (pClient->fMsgReadData)
210 {
211 uint32_t fFormat = 0;
212
213 LogRelFlow(("vboxSvcClipboardReturnMsg: ReadData %02X\n", pClient->u32RequestedFormat));
214 if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
215 fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
216 else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
217 fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
218 else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
219 fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
220 else
221 AssertStmt(pClient->u32RequestedFormat == 0, pClient->u32RequestedFormat = 0);
222 pClient->u32RequestedFormat &= ~fFormat;
223 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
224 VBoxHGCMParmUInt32Set(&paParms[1], fFormat);
225 if (pClient->u32RequestedFormat == 0)
226 pClient->fMsgReadData = false;
227 }
228 else if (pClient->fMsgFormats)
229 {
230 LogRelFlow(("vboxSvcClipboardReturnMsg: Formats %02X\n", pClient->u32AvailableFormats));
231 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS);
232 VBoxHGCMParmUInt32Set(&paParms[1], pClient->u32AvailableFormats);
233 pClient->fMsgFormats = false;
234 }
235 else
236 {
237 /* No pending messages. */
238 LogRelFlow(("vboxSvcClipboardReturnMsg: no message\n"));
239 return false;
240 }
241
242 /* Message information assigned. */
243 return true;
244}
245
246void vboxSvcClipboardReportMsg(VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
247{
248 AssertPtrReturnVoid(pClient);
249
250 if (VBoxSvcClipboardLock())
251 {
252 switch (u32Msg)
253 {
254 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
255 {
256 LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n"));
257 pClient->fMsgQuit = true;
258 } break;
259 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
260 {
261 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
262 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
263 {
264 /* Skip the message. */
265 break;
266 }
267
268 LogRelFlow(("vboxSvcClipboardReportMsg: ReadData %02X\n", u32Formats));
269 pClient->u32RequestedFormat = u32Formats;
270 pClient->fMsgReadData = true;
271 } break;
272 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
273 {
274 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
275 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
276 {
277 /* Skip the message. */
278 break;
279 }
280
281 LogRelFlow(("vboxSvcClipboardReportMsg: Formats %02X\n", u32Formats));
282 pClient->u32AvailableFormats = u32Formats;
283 pClient->fMsgFormats = true;
284 } break;
285 default:
286 {
287 /* Invalid message. */
288 LogRelFlow(("vboxSvcClipboardReportMsg: invalid message %d\n", u32Msg));
289 } break;
290 }
291
292 if (pClient->fAsync)
293 {
294 /* The client waits for a response. */
295 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClient, pClient->async.paParms);
296
297 /* Make a copy of the handle. */
298 VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle;
299
300 if (fMessageReturned)
301 {
302 /* There is a response. */
303 pClient->fAsync = false;
304 }
305
306 VBoxSvcClipboardUnlock();
307
308 if (fMessageReturned)
309 {
310 LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n"));
311 g_pHelpers->pfnCallComplete(callHandle, VINF_SUCCESS);
312 }
313 }
314 else
315 {
316 VBoxSvcClipboardUnlock();
317 }
318 }
319}
320
321static int svcInit(void)
322{
323 int rc = RTCritSectInit(&critsect);
324
325 if (RT_SUCCESS(rc))
326 {
327 vboxSvcClipboardModeSet(VBOX_SHARED_CLIPBOARD_MODE_OFF);
328
329 rc = vboxClipboardInit();
330
331 /* Clean up on failure, because 'svnUnload' will not be called
332 * if the 'svcInit' returns an error.
333 */
334 if (RT_FAILURE(rc))
335 {
336 RTCritSectDelete(&critsect);
337 }
338 }
339
340 return rc;
341}
342
343static DECLCALLBACK(int) svcUnload(void *)
344{
345 vboxClipboardDestroy();
346 RTCritSectDelete(&critsect);
347 return VINF_SUCCESS;
348}
349
350/**
351 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
352 * to the guest side.
353 */
354static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
355{
356 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
357
358 LogRel2(("svcDisconnect: u32ClientID = %d\n", u32ClientID));
359
360 vboxSvcClipboardReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0);
361
362 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
363
364 vboxClipboardDisconnect(pClient);
365
366 memset(pClient, 0, sizeof(*pClient));
367
368 g_pClient = NULL;
369
370 return VINF_SUCCESS;
371}
372
373static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
374{
375 RT_NOREF(fRequestor, fRestoring);
376 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
377
378 int rc = VINF_SUCCESS;
379
380 /* If there is already a client connected then we want to release it first. */
381 if (g_pClient != NULL)
382 {
383 uint32_t u32OldClientID = g_pClient->u32ClientID;
384
385 svcDisconnect(NULL, u32OldClientID, g_pClient);
386 /* And free the resources in the hgcm subsystem. */
387 g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID);
388 }
389
390 /* Register the client. */
391 memset(pClient, 0, sizeof(*pClient));
392
393 pClient->u32ClientID = u32ClientID;
394
395 rc = vboxClipboardConnect(pClient, VBoxSvcClipboardGetHeadless());
396
397 if (RT_SUCCESS(rc))
398 {
399 g_pClient = pClient;
400 }
401
402 LogRel2(("vboxClipboardConnect: rc = %Rrc\n", rc));
403
404 return rc;
405}
406
407static DECLCALLBACK(void) svcCall(void *,
408 VBOXHGCMCALLHANDLE callHandle,
409 uint32_t u32ClientID,
410 void *pvClient,
411 uint32_t u32Function,
412 uint32_t cParms,
413 VBOXHGCMSVCPARM paParms[],
414 uint64_t tsArrival)
415{
416 RT_NOREF_PV(tsArrival);
417 int rc = VINF_SUCCESS;
418
419 LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
420 u32ClientID, u32Function, cParms, paParms));
421
422 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
423
424 bool fAsynchronousProcessing = false;
425
426#ifdef DEBUG
427 uint32_t i;
428
429 for (i = 0; i < cParms; i++)
430 {
431 /** @todo parameters other than 32 bit */
432 LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
433 }
434#endif
435
436 switch (u32Function)
437 {
438 case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG:
439 {
440 /* The quest requests a host message. */
441 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
442
443 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
444 {
445 rc = VERR_INVALID_PARAMETER;
446 }
447 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
448 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
449 )
450 {
451 rc = VERR_INVALID_PARAMETER;
452 }
453 else
454 {
455 /* Atomically verify the client's state. */
456 if (VBoxSvcClipboardLock())
457 {
458 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms);
459
460 if (fMessageReturned)
461 {
462 /* Just return to the caller. */
463 pClient->fAsync = false;
464 }
465 else
466 {
467 /* No event available at the time. Process asynchronously. */
468 fAsynchronousProcessing = true;
469
470 pClient->fAsync = true;
471 pClient->async.callHandle = callHandle;
472 pClient->async.paParms = paParms;
473
474 LogRel2(("svcCall: async.\n"));
475 }
476
477 VBoxSvcClipboardUnlock();
478 }
479 else
480 {
481 rc = VERR_NOT_SUPPORTED;
482 }
483 }
484 } break;
485
486 case VBOX_SHARED_CLIPBOARD_FN_REPORT_FORMATS:
487 {
488 /* The guest reports that some formats are available. */
489 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
490
491 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS)
492 {
493 rc = VERR_INVALID_PARAMETER;
494 }
495 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
496 )
497 {
498 rc = VERR_INVALID_PARAMETER;
499 }
500 else
501 {
502 uint32_t u32Formats;
503
504 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats);
505
506 if (RT_SUCCESS (rc))
507 {
508 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
509 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
510 {
511 rc = VERR_NOT_SUPPORTED;
512 break;
513 }
514
515 if (g_pfnExtension)
516 {
517 VBOXCLIPBOARDEXTPARMS parms;
518
519 parms.u32Format = u32Formats;
520
521 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
522 }
523 else
524 {
525 vboxClipboardFormatAnnounce (pClient, u32Formats);
526 }
527 }
528 }
529 } break;
530
531 case VBOX_SHARED_CLIPBOARD_FN_READ_DATA:
532 {
533 /* The guest wants to read data in the given format. */
534 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
535
536 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
537 {
538 rc = VERR_INVALID_PARAMETER;
539 }
540 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
541 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
542 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
543 )
544 {
545 rc = VERR_INVALID_PARAMETER;
546 }
547 else
548 {
549 uint32_t u32Format;
550 void *pv;
551 uint32_t cb;
552
553 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
554
555 if (RT_SUCCESS (rc))
556 {
557 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
558
559 if (RT_SUCCESS (rc))
560 {
561 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
562 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
563 {
564 rc = VERR_NOT_SUPPORTED;
565 break;
566 }
567
568 uint32_t cbActual = 0;
569
570 if (g_pfnExtension)
571 {
572 VBOXCLIPBOARDEXTPARMS parms;
573
574 parms.u32Format = u32Format;
575 parms.u.pvData = pv;
576 parms.cbData = cb;
577
578 g_fReadingData = true;
579 rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
580 LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
581 if (g_fDelayedAnnouncement)
582 {
583 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, g_u32DelayedFormats);
584 g_fDelayedAnnouncement = false;
585 g_u32DelayedFormats = 0;
586 }
587 g_fReadingData = false;
588
589 if (RT_SUCCESS (rc))
590 {
591 cbActual = parms.cbData;
592 }
593 }
594 else
595 {
596 /* Release any other pending read, as we only
597 * support one pending read at one time. */
598 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
599 rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual);
600 }
601
602 /* Remember our read request until it is completed.
603 * See the protocol description above for more
604 * information. */
605 if (rc == VINF_HGCM_ASYNC_EXECUTE)
606 {
607 if (VBoxSvcClipboardLock())
608 {
609 pClient->asyncRead.callHandle = callHandle;
610 pClient->asyncRead.paParms = paParms;
611 pClient->fReadPending = true;
612 fAsynchronousProcessing = true;
613 VBoxSvcClipboardUnlock();
614 }
615 else
616 rc = VERR_NOT_SUPPORTED;
617 }
618 else if (RT_SUCCESS (rc))
619 {
620 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
621 }
622 }
623 }
624 }
625 } break;
626
627 case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA:
628 {
629 /* The guest writes the requested data. */
630 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
631
632 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
633 {
634 rc = VERR_INVALID_PARAMETER;
635 }
636 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
637 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
638 )
639 {
640 rc = VERR_INVALID_PARAMETER;
641 }
642 else
643 {
644 void *pv;
645 uint32_t cb;
646 uint32_t u32Format;
647
648 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
649
650 if (RT_SUCCESS (rc))
651 {
652 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
653
654 if (RT_SUCCESS (rc))
655 {
656 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
657 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
658 {
659 rc = VERR_NOT_SUPPORTED;
660 break;
661 }
662
663 if (g_pfnExtension)
664 {
665 VBOXCLIPBOARDEXTPARMS parms;
666
667 parms.u32Format = u32Format;
668 parms.u.pvData = pv;
669 parms.cbData = cb;
670
671 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
672 }
673 else
674 {
675 vboxClipboardWriteData (pClient, pv, cb, u32Format);
676 }
677 }
678 }
679 }
680 } break;
681
682 default:
683 {
684 rc = VERR_NOT_IMPLEMENTED;
685 }
686 }
687
688 LogRelFlow(("svcCall: rc = %Rrc\n", rc));
689
690 if (!fAsynchronousProcessing)
691 {
692 g_pHelpers->pfnCallComplete(callHandle, rc);
693 }
694}
695
696/** If the client in the guest is waiting for a read operation to complete
697 * then complete it, otherwise return. See the protocol description in the
698 * shared clipboard module description. */
699void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
700{
701 VBOXHGCMCALLHANDLE callHandle = NULL;
702 VBOXHGCMSVCPARM *paParms = NULL;
703 bool fReadPending = false;
704 if (VBoxSvcClipboardLock()) /* if not can we do anything useful? */
705 {
706 callHandle = pClient->asyncRead.callHandle;
707 paParms = pClient->asyncRead.paParms;
708 fReadPending = pClient->fReadPending;
709 pClient->fReadPending = false;
710 VBoxSvcClipboardUnlock();
711 }
712 if (fReadPending)
713 {
714 VBoxHGCMParmUInt32Set(&paParms[2], cbActual);
715 g_pHelpers->pfnCallComplete(callHandle, rc);
716 }
717}
718
719/*
720 * We differentiate between a function handler for the guest and one for the host.
721 */
722static DECLCALLBACK(int) svcHostCall(void *,
723 uint32_t u32Function,
724 uint32_t cParms,
725 VBOXHGCMSVCPARM paParms[])
726{
727 int rc = VINF_SUCCESS;
728
729 LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
730 u32Function, cParms, paParms));
731
732 switch (u32Function)
733 {
734 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
735 {
736 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
737
738 if (cParms != 1)
739 {
740 rc = VERR_INVALID_PARAMETER;
741 }
742 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
743 )
744 {
745 rc = VERR_INVALID_PARAMETER;
746 }
747 else
748 {
749 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
750
751 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode);
752
753 /* The setter takes care of invalid values. */
754 vboxSvcClipboardModeSet (u32Mode);
755 }
756 } break;
757
758 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
759 {
760 uint32_t u32Headless = g_fHeadless;
761
762 rc = VERR_INVALID_PARAMETER;
763 if (cParms != 1)
764 break;
765 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Headless);
766 if (RT_SUCCESS(rc))
767 LogRelFlow(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
768 (unsigned) u32Headless));
769 g_fHeadless = RT_BOOL(u32Headless);
770 } break;
771
772 default:
773 break;
774 }
775
776 LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
777 return rc;
778}
779
780#ifndef UNIT_TEST
781/**
782 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
783 */
784static SSMFIELD const g_aClipboardClientDataFields[] =
785{
786 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */
787 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgQuit),
788 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgReadData),
789 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgFormats),
790 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat),
791 SSMFIELD_ENTRY_TERM()
792};
793#endif
794
795static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
796{
797#ifndef UNIT_TEST
798 /*
799 * When the state will be restored, pending requests will be reissued
800 * by VMMDev. The service therefore must save state as if there were no
801 * pending request.
802 * Pending requests, if any, will be completed in svcDisconnect.
803 */
804 LogRel2(("svcSaveState: u32ClientID = %d\n", u32ClientID));
805
806 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
807
808 /* This field used to be the length. We're using it as a version field
809 with the high bit set. */
810 SSMR3PutU32(pSSM, UINT32_C(0x80000002));
811 int rc = SSMR3PutStructEx(pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
812 AssertRCReturn(rc, rc);
813
814#else /* UNIT_TEST */
815 RT_NOREF3(u32ClientID, pvClient, pSSM);
816#endif /* UNIT_TEST */
817 return VINF_SUCCESS;
818}
819
820/**
821 * This structure corresponds to the original layout of the
822 * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
823 * when saving state, we need to remember it forever in order to preserve
824 * compatibility.
825 *
826 * (Starting with 3.1 this is no longer used.)
827 *
828 * @remarks Putting this outside svcLoadState to avoid visibility warning caused
829 * by -Wattributes.
830 */
831typedef struct CLIPSAVEDSTATEDATA
832{
833 struct CLIPSAVEDSTATEDATA *pNext;
834 struct CLIPSAVEDSTATEDATA *pPrev;
835
836 VBOXCLIPBOARDCONTEXT *pCtx;
837
838 uint32_t u32ClientID;
839
840 bool fAsync: 1; /* Guest is waiting for a message. */
841
842 bool fMsgQuit: 1;
843 bool fMsgReadData: 1;
844 bool fMsgFormats: 1;
845
846 struct
847 {
848 VBOXHGCMCALLHANDLE callHandle;
849 VBOXHGCMSVCPARM *paParms;
850 } async;
851
852 struct
853 {
854 void *pv;
855 uint32_t cb;
856 uint32_t u32Format;
857 } data;
858
859 uint32_t u32AvailableFormats;
860 uint32_t u32RequestedFormat;
861
862} CLIPSAVEDSTATEDATA;
863
864static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
865{
866#ifndef UNIT_TEST
867 RT_NOREF(uVersion);
868 LogRel2(("svcLoadState: u32ClientID = %d\n", u32ClientID));
869
870 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
871
872 /* Existing client can not be in async state yet. */
873 Assert(!pClient->fAsync);
874
875 /* Save the client ID for data validation. */
876 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
877 uint32_t const u32ClientIDOld = pClient->u32ClientID;
878
879 /* Restore the client data. */
880 uint32_t lenOrVer;
881 int rc = SSMR3GetU32(pSSM, &lenOrVer);
882 AssertRCReturn(rc, rc);
883 if (lenOrVer == UINT32_C(0x80000002))
884 {
885 rc = SSMR3GetStructEx(pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
886 AssertRCReturn(rc, rc);
887 }
888 else if (lenOrVer == (SSMR3HandleHostBits(pSSM) == 64 ? 72U : 48U))
889 {
890 /**
891 * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
892 */
893 static SSMFIELD const s_aClipSavedStateDataFields30[] =
894 {
895 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
896 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
897 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
898 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
899 SSMFIELD_ENTRY_CUSTOM(fMsgQuit + fMsgReadData + fMsgFormats, RT_UOFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
900 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
901 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
902 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
903 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
904 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
905 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
906 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
907 SSMFIELD_ENTRY_TERM()
908 };
909
910 CLIPSAVEDSTATEDATA savedState;
911 RT_ZERO(savedState);
912 rc = SSMR3GetStructEx(pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
913 &s_aClipSavedStateDataFields30[0], NULL);
914 AssertRCReturn(rc, rc);
915
916 pClient->fMsgQuit = savedState.fMsgQuit;
917 pClient->fMsgReadData = savedState.fMsgReadData;
918 pClient->fMsgFormats = savedState.fMsgFormats;
919 pClient->u32RequestedFormat = savedState.u32RequestedFormat;
920 }
921 else
922 {
923 LogRel(("Client data size mismatch: got %#x\n", lenOrVer));
924 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
925 }
926
927 /* Verify the client ID. */
928 if (pClient->u32ClientID != u32ClientIDOld)
929 {
930 LogRel(("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
931 pClient->u32ClientID = u32ClientIDOld;
932 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
933 }
934
935 /* Actual host data are to be reported to guest (SYNC). */
936 vboxClipboardSync(pClient);
937
938#else /* UNIT_TEST*/
939 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
940#endif /* UNIT_TEST */
941 return VINF_SUCCESS;
942}
943
944static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
945{
946 RT_NOREF2(pvData, cbData);
947 if (g_pClient != NULL)
948 {
949 switch (u32Function)
950 {
951 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
952 {
953 LogRelFlow(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData));
954 if (g_fReadingData)
955 {
956 g_fDelayedAnnouncement = true;
957 g_u32DelayedFormats = u32Format;
958 }
959 else
960 {
961 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, u32Format);
962 }
963 } break;
964
965 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
966 {
967 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
968 } break;
969
970 default:
971 return VERR_NOT_SUPPORTED;
972 }
973 }
974
975 return VINF_SUCCESS;
976}
977
978static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
979{
980 LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension));
981
982 VBOXCLIPBOARDEXTPARMS parms;
983
984 if (pfnExtension)
985 {
986 /* Install extension. */
987 g_pfnExtension = pfnExtension;
988 g_pvExtension = pvExtension;
989
990 parms.u.pfnCallback = extCallback;
991 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
992 }
993 else
994 {
995 if (g_pfnExtension)
996 {
997 parms.u.pfnCallback = NULL;
998 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
999 }
1000
1001 /* Uninstall extension. */
1002 g_pfnExtension = NULL;
1003 g_pvExtension = NULL;
1004 }
1005
1006 return VINF_SUCCESS;
1007}
1008
1009extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1010{
1011 int rc = VINF_SUCCESS;
1012
1013 LogRelFlowFunc(("ptable = %p\n", ptable));
1014
1015 if (!ptable)
1016 {
1017 rc = VERR_INVALID_PARAMETER;
1018 }
1019 else
1020 {
1021 LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1022
1023 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1024 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1025 {
1026 rc = VERR_INVALID_PARAMETER;
1027 }
1028 else
1029 {
1030 g_pHelpers = ptable->pHelpers;
1031
1032 ptable->cbClient = sizeof(VBOXCLIPBOARDCLIENTDATA);
1033
1034 ptable->pfnUnload = svcUnload;
1035 ptable->pfnConnect = svcConnect;
1036 ptable->pfnDisconnect = svcDisconnect;
1037 ptable->pfnCall = svcCall;
1038 ptable->pfnHostCall = svcHostCall;
1039 ptable->pfnSaveState = svcSaveState;
1040 ptable->pfnLoadState = svcLoadState;
1041 ptable->pfnRegisterExtension = svcRegisterExtension;
1042 ptable->pfnNotify = NULL;
1043 ptable->pvService = NULL;
1044
1045 /* Service specific initialization. */
1046 rc = svcInit();
1047 }
1048 }
1049
1050 return rc;
1051}
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