VirtualBox

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

Last change on this file since 38945 was 37472, checked in by vboxsync, 14 years ago

HostServices/SharedClipboard: use headless attribute (X11 only)

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