VirtualBox

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

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

HGCM,GuestProps: Added HGCM service notifications for VM power-on, VM resume, VM suspend, VM reset and VM power-off. Made use of the first two in guest properties to wake up guest programs waiting on any of the host version properties. Also moved inserting sysprep and host version properties to the services as that's a better home than ConsoleImpl/VMMDevInterface in my opinion.

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