VirtualBox

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

Last change on this file since 77922 was 76970, checked in by vboxsync, 6 years ago

Shared Clipboard/HostServices: Renamed VBoxSharedClipboard.cpp to VBoxSharedClipboardSvc.cpp and VBoxSharedClipboard.rc to VBoxSharedClipboardSvc.rc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 76970 2019-01-24 08:54:53Z 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_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 */
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_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 if (vboxSvcClipboardLock ())
249 {
250 switch (u32Msg)
251 {
252 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
253 {
254 LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n"));
255 pClient->fMsgQuit = true;
256 } break;
257 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
258 {
259 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
260 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
261 {
262 /* Skip the message. */
263 break;
264 }
265
266 LogRelFlow(("vboxSvcClipboardReportMsg: ReadData %02X\n", u32Formats));
267 pClient->u32RequestedFormat = u32Formats;
268 pClient->fMsgReadData = true;
269 } break;
270 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
271 {
272 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
273 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
274 {
275 /* Skip the message. */
276 break;
277 }
278
279 LogRelFlow(("vboxSvcClipboardReportMsg: Formats %02X\n", u32Formats));
280 pClient->u32AvailableFormats = u32Formats;
281 pClient->fMsgFormats = true;
282 } break;
283 default:
284 {
285 /* Invalid message. */
286 LogRelFlow(("vboxSvcClipboardReportMsg: invalid message %d\n", u32Msg));
287 } break;
288 }
289
290 if (pClient->fAsync)
291 {
292 /* The client waits for a response. */
293 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, pClient->async.paParms);
294
295 /* Make a copy of the handle. */
296 VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle;
297
298 if (fMessageReturned)
299 {
300 /* There is a response. */
301 pClient->fAsync = false;
302 }
303
304 vboxSvcClipboardUnlock ();
305
306 if (fMessageReturned)
307 {
308 LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n"));
309 g_pHelpers->pfnCallComplete (callHandle, VINF_SUCCESS);
310 }
311 }
312 else
313 {
314 vboxSvcClipboardUnlock ();
315 }
316 }
317}
318
319static int svcInit (void)
320{
321 int rc = RTCritSectInit (&critsect);
322
323 if (RT_SUCCESS (rc))
324 {
325 vboxSvcClipboardModeSet (VBOX_SHARED_CLIPBOARD_MODE_OFF);
326
327 rc = vboxClipboardInit ();
328
329 /* Clean up on failure, because 'svnUnload' will not be called
330 * if the 'svcInit' returns an error.
331 */
332 if (RT_FAILURE (rc))
333 {
334 RTCritSectDelete (&critsect);
335 }
336 }
337
338 return rc;
339}
340
341static DECLCALLBACK(int) svcUnload (void *)
342{
343 vboxClipboardDestroy ();
344 RTCritSectDelete (&critsect);
345 return VINF_SUCCESS;
346}
347
348/**
349 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
350 * to the guest side.
351 */
352static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
353{
354 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
355
356 LogRel2(("svcDisconnect: u32ClientID = %d\n", u32ClientID));
357
358 vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0);
359
360 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
361
362 vboxClipboardDisconnect (pClient);
363
364 memset (pClient, 0, sizeof (*pClient));
365
366 g_pClient = NULL;
367
368 return VINF_SUCCESS;
369}
370
371static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
372{
373 RT_NOREF(fRequestor, fRestoring);
374 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
375
376 int rc = VINF_SUCCESS;
377
378 /* If there is already a client connected then we want to release it first. */
379 if (g_pClient != NULL)
380 {
381 uint32_t u32OldClientID = g_pClient->u32ClientID;
382
383 svcDisconnect(NULL, u32OldClientID, g_pClient);
384 /* And free the resources in the hgcm subsystem. */
385 g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID);
386 }
387
388 /* Register the client. */
389 memset (pClient, 0, sizeof (*pClient));
390
391 pClient->u32ClientID = u32ClientID;
392
393 rc = vboxClipboardConnect (pClient, vboxSvcClipboardGetHeadless());
394
395 if (RT_SUCCESS (rc))
396 {
397 g_pClient = pClient;
398 }
399
400 LogRel2(("vboxClipboardConnect: rc = %Rrc\n", rc));
401
402 return rc;
403}
404
405static DECLCALLBACK(void) svcCall (void *,
406 VBOXHGCMCALLHANDLE callHandle,
407 uint32_t u32ClientID,
408 void *pvClient,
409 uint32_t u32Function,
410 uint32_t cParms,
411 VBOXHGCMSVCPARM paParms[],
412 uint64_t tsArrival)
413{
414 RT_NOREF_PV(tsArrival);
415 int rc = VINF_SUCCESS;
416
417 LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
418 u32ClientID, u32Function, cParms, paParms));
419
420 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
421
422 bool fAsynchronousProcessing = false;
423
424#ifdef DEBUG
425 uint32_t i;
426
427 for (i = 0; i < cParms; i++)
428 {
429 /** @todo parameters other than 32 bit */
430 LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
431 }
432#endif
433
434 switch (u32Function)
435 {
436 case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG:
437 {
438 /* The quest requests a host message. */
439 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
440
441 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
442 {
443 rc = VERR_INVALID_PARAMETER;
444 }
445 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
446 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
447 )
448 {
449 rc = VERR_INVALID_PARAMETER;
450 }
451 else
452 {
453 /* Atomically verify the client's state. */
454 if (vboxSvcClipboardLock ())
455 {
456 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms);
457
458 if (fMessageReturned)
459 {
460 /* Just return to the caller. */
461 pClient->fAsync = false;
462 }
463 else
464 {
465 /* No event available at the time. Process asynchronously. */
466 fAsynchronousProcessing = true;
467
468 pClient->fAsync = true;
469 pClient->async.callHandle = callHandle;
470 pClient->async.paParms = paParms;
471
472 LogRel2(("svcCall: async.\n"));
473 }
474
475 vboxSvcClipboardUnlock ();
476 }
477 else
478 {
479 rc = VERR_NOT_SUPPORTED;
480 }
481 }
482 } break;
483
484 case VBOX_SHARED_CLIPBOARD_FN_FORMATS:
485 {
486 /* The guest reports that some formats are available. */
487 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
488
489 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS)
490 {
491 rc = VERR_INVALID_PARAMETER;
492 }
493 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
494 )
495 {
496 rc = VERR_INVALID_PARAMETER;
497 }
498 else
499 {
500 uint32_t u32Formats;
501
502 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats);
503
504 if (RT_SUCCESS (rc))
505 {
506 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
507 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
508 {
509 rc = VERR_NOT_SUPPORTED;
510 break;
511 }
512
513 if (g_pfnExtension)
514 {
515 VBOXCLIPBOARDEXTPARMS parms;
516
517 parms.u32Format = u32Formats;
518
519 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
520 }
521 else
522 {
523 vboxClipboardFormatAnnounce (pClient, u32Formats);
524 }
525 }
526 }
527 } break;
528
529 case VBOX_SHARED_CLIPBOARD_FN_READ_DATA:
530 {
531 /* The guest wants to read data in the given format. */
532 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
533
534 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
535 {
536 rc = VERR_INVALID_PARAMETER;
537 }
538 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
539 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
540 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
541 )
542 {
543 rc = VERR_INVALID_PARAMETER;
544 }
545 else
546 {
547 uint32_t u32Format;
548 void *pv;
549 uint32_t cb;
550
551 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
552
553 if (RT_SUCCESS (rc))
554 {
555 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
556
557 if (RT_SUCCESS (rc))
558 {
559 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
560 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
561 {
562 rc = VERR_NOT_SUPPORTED;
563 break;
564 }
565
566 uint32_t cbActual = 0;
567
568 if (g_pfnExtension)
569 {
570 VBOXCLIPBOARDEXTPARMS parms;
571
572 parms.u32Format = u32Format;
573 parms.u.pvData = pv;
574 parms.cbData = cb;
575
576 g_fReadingData = true;
577 rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
578 LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
579 if (g_fDelayedAnnouncement)
580 {
581 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, g_u32DelayedFormats);
582 g_fDelayedAnnouncement = false;
583 g_u32DelayedFormats = 0;
584 }
585 g_fReadingData = false;
586
587 if (RT_SUCCESS (rc))
588 {
589 cbActual = parms.cbData;
590 }
591 }
592 else
593 {
594 /* Release any other pending read, as we only
595 * support one pending read at one time. */
596 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
597 rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual);
598 }
599
600 /* Remember our read request until it is completed.
601 * See the protocol description above for more
602 * information. */
603 if (rc == VINF_HGCM_ASYNC_EXECUTE)
604 {
605 if (vboxSvcClipboardLock())
606 {
607 pClient->asyncRead.callHandle = callHandle;
608 pClient->asyncRead.paParms = paParms;
609 pClient->fReadPending = true;
610 fAsynchronousProcessing = true;
611 vboxSvcClipboardUnlock();
612 }
613 else
614 rc = VERR_NOT_SUPPORTED;
615 }
616 else if (RT_SUCCESS (rc))
617 {
618 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
619 }
620 }
621 }
622 }
623 } break;
624
625 case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA:
626 {
627 /* The guest writes the requested data. */
628 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
629
630 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
631 {
632 rc = VERR_INVALID_PARAMETER;
633 }
634 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
635 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
636 )
637 {
638 rc = VERR_INVALID_PARAMETER;
639 }
640 else
641 {
642 void *pv;
643 uint32_t cb;
644 uint32_t u32Format;
645
646 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
647
648 if (RT_SUCCESS (rc))
649 {
650 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
651
652 if (RT_SUCCESS (rc))
653 {
654 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
655 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
656 {
657 rc = VERR_NOT_SUPPORTED;
658 break;
659 }
660
661 if (g_pfnExtension)
662 {
663 VBOXCLIPBOARDEXTPARMS parms;
664
665 parms.u32Format = u32Format;
666 parms.u.pvData = pv;
667 parms.cbData = cb;
668
669 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
670 }
671 else
672 {
673 vboxClipboardWriteData (pClient, pv, cb, u32Format);
674 }
675 }
676 }
677 }
678 } break;
679
680 default:
681 {
682 rc = VERR_NOT_IMPLEMENTED;
683 }
684 }
685
686 LogRelFlow(("svcCall: rc = %Rrc\n", rc));
687
688 if (!fAsynchronousProcessing)
689 {
690 g_pHelpers->pfnCallComplete (callHandle, rc);
691 }
692}
693
694/** If the client in the guest is waiting for a read operation to complete
695 * then complete it, otherwise return. See the protocol description in the
696 * shared clipboard module description. */
697void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
698{
699 VBOXHGCMCALLHANDLE callHandle = NULL;
700 VBOXHGCMSVCPARM *paParms = NULL;
701 bool fReadPending = false;
702 if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
703 {
704 callHandle = pClient->asyncRead.callHandle;
705 paParms = pClient->asyncRead.paParms;
706 fReadPending = pClient->fReadPending;
707 pClient->fReadPending = false;
708 vboxSvcClipboardUnlock();
709 }
710 if (fReadPending)
711 {
712 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
713 g_pHelpers->pfnCallComplete (callHandle, rc);
714 }
715}
716
717/*
718 * We differentiate between a function handler for the guest and one for the host.
719 */
720static DECLCALLBACK(int) svcHostCall (void *,
721 uint32_t u32Function,
722 uint32_t cParms,
723 VBOXHGCMSVCPARM paParms[])
724{
725 int rc = VINF_SUCCESS;
726
727 LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
728 u32Function, cParms, paParms));
729
730 switch (u32Function)
731 {
732 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
733 {
734 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
735
736 if (cParms != 1)
737 {
738 rc = VERR_INVALID_PARAMETER;
739 }
740 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
741 )
742 {
743 rc = VERR_INVALID_PARAMETER;
744 }
745 else
746 {
747 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
748
749 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode);
750
751 /* The setter takes care of invalid values. */
752 vboxSvcClipboardModeSet (u32Mode);
753 }
754 } break;
755
756 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
757 {
758 uint32_t u32Headless = g_fHeadless;
759
760 rc = VERR_INVALID_PARAMETER;
761 if (cParms != 1)
762 break;
763 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Headless);
764 if (RT_SUCCESS(rc))
765 LogRelFlow(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
766 (unsigned) u32Headless));
767 g_fHeadless = RT_BOOL(u32Headless);
768 } break;
769
770 default:
771 break;
772 }
773
774 LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
775 return rc;
776}
777
778#ifndef UNIT_TEST
779/**
780 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
781 */
782static SSMFIELD const g_aClipboardClientDataFields[] =
783{
784 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */
785 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgQuit),
786 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgReadData),
787 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgFormats),
788 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat),
789 SSMFIELD_ENTRY_TERM()
790};
791#endif
792
793static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
794{
795#ifndef UNIT_TEST
796 /*
797 * When the state will be restored, pending requests will be reissued
798 * by VMMDev. The service therefore must save state as if there were no
799 * pending request.
800 * Pending requests, if any, will be completed in svcDisconnect.
801 */
802 LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
803
804 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
805
806 /* This field used to be the length. We're using it as a version field
807 with the high bit set. */
808 SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
809 int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
810 AssertRCReturn (rc, rc);
811
812#else /* UNIT_TEST */
813 RT_NOREF3(u32ClientID, pvClient, pSSM);
814#endif /* UNIT_TEST */
815 return VINF_SUCCESS;
816}
817
818/**
819 * This structure corresponds to the original layout of the
820 * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
821 * when saving state, we need to remember it forever in order to preserve
822 * compatibility.
823 *
824 * (Starting with 3.1 this is no longer used.)
825 *
826 * @remarks Putting this outside svcLoadState to avoid visibility warning caused
827 * by -Wattributes.
828 */
829typedef struct CLIPSAVEDSTATEDATA
830{
831 struct CLIPSAVEDSTATEDATA *pNext;
832 struct CLIPSAVEDSTATEDATA *pPrev;
833
834 VBOXCLIPBOARDCONTEXT *pCtx;
835
836 uint32_t u32ClientID;
837
838 bool fAsync: 1; /* Guest is waiting for a message. */
839
840 bool fMsgQuit: 1;
841 bool fMsgReadData: 1;
842 bool fMsgFormats: 1;
843
844 struct {
845 VBOXHGCMCALLHANDLE callHandle;
846 VBOXHGCMSVCPARM *paParms;
847 } async;
848
849 struct {
850 void *pv;
851 uint32_t cb;
852 uint32_t u32Format;
853 } data;
854
855 uint32_t u32AvailableFormats;
856 uint32_t u32RequestedFormat;
857
858} CLIPSAVEDSTATEDATA;
859
860static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
861{
862#ifndef UNIT_TEST
863 RT_NOREF(uVersion);
864 LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
865
866 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
867
868 /* Existing client can not be in async state yet. */
869 Assert (!pClient->fAsync);
870
871 /* Save the client ID for data validation. */
872 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
873 uint32_t const u32ClientIDOld = pClient->u32ClientID;
874
875 /* Restore the client data. */
876 uint32_t lenOrVer;
877 int rc = SSMR3GetU32 (pSSM, &lenOrVer);
878 AssertRCReturn (rc, rc);
879 if (lenOrVer == UINT32_C (0x80000002))
880 {
881 rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
882 AssertRCReturn (rc, rc);
883 }
884 else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72U : 48U))
885 {
886 /**
887 * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
888 */
889 static SSMFIELD const s_aClipSavedStateDataFields30[] =
890 {
891 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
892 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
893 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
894 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
895 SSMFIELD_ENTRY_CUSTOM(fMsgQuit + fMsgReadData + fMsgFormats, RT_UOFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
896 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
897 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
898 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
899 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
900 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
901 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
902 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
903 SSMFIELD_ENTRY_TERM()
904 };
905
906 CLIPSAVEDSTATEDATA savedState;
907 RT_ZERO (savedState);
908 rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
909 &s_aClipSavedStateDataFields30[0], NULL);
910 AssertRCReturn (rc, rc);
911
912 pClient->fMsgQuit = savedState.fMsgQuit;
913 pClient->fMsgReadData = savedState.fMsgReadData;
914 pClient->fMsgFormats = savedState.fMsgFormats;
915 pClient->u32RequestedFormat = savedState.u32RequestedFormat;
916 }
917 else
918 {
919 LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
920 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
921 }
922
923 /* Verify the client ID. */
924 if (pClient->u32ClientID != u32ClientIDOld)
925 {
926 LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
927 pClient->u32ClientID = u32ClientIDOld;
928 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
929 }
930
931 /* Actual host data are to be reported to guest (SYNC). */
932 vboxClipboardSync (pClient);
933
934#else /* UNIT_TEST*/
935 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
936#endif /* UNIT_TEST */
937 return VINF_SUCCESS;
938}
939
940static DECLCALLBACK(int) extCallback (uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
941{
942 RT_NOREF2(pvData, cbData);
943 if (g_pClient != NULL)
944 {
945 switch (u32Function)
946 {
947 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
948 {
949 LogRelFlow(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData));
950 if (g_fReadingData)
951 {
952 g_fDelayedAnnouncement = true;
953 g_u32DelayedFormats = u32Format;
954 }
955 else
956 {
957 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Format);
958 }
959 } break;
960
961 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
962 {
963 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
964 } break;
965
966 default:
967 return VERR_NOT_SUPPORTED;
968 }
969 }
970
971 return VINF_SUCCESS;
972}
973
974static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
975{
976 LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension));
977
978 VBOXCLIPBOARDEXTPARMS parms;
979
980 if (pfnExtension)
981 {
982 /* Install extension. */
983 g_pfnExtension = pfnExtension;
984 g_pvExtension = pvExtension;
985
986 parms.u.pfnCallback = extCallback;
987 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
988 }
989 else
990 {
991 if (g_pfnExtension)
992 {
993 parms.u.pfnCallback = NULL;
994 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
995 }
996
997 /* Uninstall extension. */
998 g_pfnExtension = NULL;
999 g_pvExtension = NULL;
1000 }
1001
1002 return VINF_SUCCESS;
1003}
1004
1005extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1006{
1007 int rc = VINF_SUCCESS;
1008
1009 LogRelFlowFunc(("ptable = %p\n", ptable));
1010
1011 if (!ptable)
1012 {
1013 rc = VERR_INVALID_PARAMETER;
1014 }
1015 else
1016 {
1017 LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1018
1019 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1020 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1021 {
1022 rc = VERR_INVALID_PARAMETER;
1023 }
1024 else
1025 {
1026 g_pHelpers = ptable->pHelpers;
1027
1028 ptable->cbClient = sizeof (VBOXCLIPBOARDCLIENTDATA);
1029
1030 ptable->pfnUnload = svcUnload;
1031 ptable->pfnConnect = svcConnect;
1032 ptable->pfnDisconnect = svcDisconnect;
1033 ptable->pfnCall = svcCall;
1034 ptable->pfnHostCall = svcHostCall;
1035 ptable->pfnSaveState = svcSaveState;
1036 ptable->pfnLoadState = svcLoadState;
1037 ptable->pfnRegisterExtension = svcRegisterExtension;
1038 ptable->pfnNotify = NULL;
1039 ptable->pvService = NULL;
1040
1041 /* Service specific initialization. */
1042 rc = svcInit ();
1043 }
1044 }
1045
1046 return rc;
1047}
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