VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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