VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/service.cpp@ 75506

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

Make scm happy.

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