VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp@ 82799

Last change on this file since 82799 was 82628, checked in by vboxsync, 5 years ago

VBoxSharedClipboardSvc: If guest provided zero bytes, we get no payload. bugref:9631

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 82628 2019-12-20 13:41:17Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <iprt/assert.h>
24#include <iprt/critsect.h>
25#include <iprt/env.h>
26#include <iprt/mem.h>
27#include <iprt/semaphore.h>
28#include <iprt/string.h>
29
30#include <VBox/GuestHost/SharedClipboard.h>
31#include <VBox/GuestHost/SharedClipboard-x11.h>
32#include <VBox/HostServices/VBoxClipboardSvc.h>
33#include <iprt/errcore.h>
34
35#include "VBoxSharedClipboardSvc-internal.h"
36
37
38/*********************************************************************************************************************************
39* Structures and Typedefs *
40*********************************************************************************************************************************/
41/**
42 * Global context information used by the host glue for the X11 clipboard backend.
43 */
44struct SHCLCONTEXT
45{
46 /** This mutex is grabbed during any critical operations on the clipboard
47 * which might clash with others. */
48 RTCRITSECT CritSect;
49 /** X11 context data. */
50 SHCLX11CTX X11;
51 /** Pointer to the VBox host client data structure. */
52 PSHCLCLIENT pClient;
53 /** We set this when we start shutting down as a hint not to post any new
54 * requests. */
55 bool fShuttingDown;
56};
57
58
59int ShClSvcImplInit(void)
60{
61 LogFlowFuncEnter();
62 return VINF_SUCCESS;
63}
64
65void ShClSvcImplDestroy(void)
66{
67 LogFlowFuncEnter();
68}
69
70/**
71 * @note On the host, we assume that some other application already owns
72 * the clipboard and leave ownership to X11.
73 */
74int ShClSvcImplConnect(PSHCLCLIENT pClient, bool fHeadless)
75{
76 int rc;
77
78 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
79 if (pCtx)
80 {
81 rc = RTCritSectInit(&pCtx->CritSect);
82 if (RT_SUCCESS(rc))
83 {
84 rc = ShClX11Init(&pCtx->X11, pCtx, fHeadless);
85 if (RT_SUCCESS(rc))
86 {
87 pClient->State.pCtx = pCtx;
88 pCtx->pClient = pClient;
89
90 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
91 if (RT_FAILURE(rc))
92 ShClX11Destroy(&pCtx->X11);
93 }
94
95 if (RT_FAILURE(rc))
96 RTCritSectDelete(&pCtx->CritSect);
97 }
98 else
99 RTMemFree(pCtx);
100 }
101 else
102 rc = VERR_NO_MEMORY;
103
104 LogFlowFuncLeaveRC(rc);
105 return rc;
106}
107
108int ShClSvcImplSync(PSHCLCLIENT pClient)
109{
110 LogFlowFuncEnter();
111
112 /* Tell the guest we have no data in case X11 is not available. If
113 * there is data in the host clipboard it will automatically be sent to
114 * the guest when the clipboard starts up. */
115 return ShClSvcHostReportFormats(pClient, VBOX_SHCL_FMT_NONE);
116}
117
118/**
119 * Shut down the shared clipboard service and "disconnect" the guest.
120 * @note Host glue code
121 */
122int ShClSvcImplDisconnect(PSHCLCLIENT pClient)
123{
124 LogFlowFuncEnter();
125
126 PSHCLCONTEXT pCtx = pClient->State.pCtx;
127 AssertPtr(pCtx);
128
129 /* Drop the reference to the client, in case it is still there. This
130 * will cause any outstanding clipboard data requests from X11 to fail
131 * immediately. */
132 pCtx->fShuttingDown = true;
133
134 int rc = ShClX11ThreadStop(&pCtx->X11);
135 /** @todo handle this slightly more reasonably, or be really sure
136 * it won't go wrong. */
137 AssertRC(rc);
138
139 ShClX11Destroy(&pCtx->X11);
140 RTCritSectDelete(&pCtx->CritSect);
141
142 RTMemFree(pCtx);
143
144 LogFlowFuncLeaveRC(rc);
145 return rc;
146}
147
148int ShClSvcImplFormatAnnounce(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
149 PSHCLFORMATDATA pFormats)
150{
151 RT_NOREF(pCmdCtx);
152
153 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, pFormats->Formats);
154
155 LogFlowFuncLeaveRC(rc);
156 return rc;
157}
158
159/** Structure describing a request for clipoard data from the guest. */
160struct CLIPREADCBREQ
161{
162 /** User-supplied data pointer, based on the request type. */
163 void *pv;
164 /** The size (in bytes) of the the user-supplied pointer in pv. */
165 uint32_t cb;
166 /** The actual size of the data written. */
167 uint32_t *pcbActual;
168 /** The request's event ID. */
169 SHCLEVENTID uEvent;
170};
171
172/**
173 * @note We always fail or complete asynchronously.
174 * @note On success allocates a CLIPREADCBREQ structure which must be
175 * freed in ClipCompleteDataRequestFromX11 when it is called back from
176 * the backend code.
177 */
178int ShClSvcImplReadData(PSHCLCLIENT pClient,
179 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData, uint32_t *pcbActual)
180{
181 RT_NOREF(pCmdCtx);
182
183 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
184 pClient, pData->uFormat, pData->pvData, pData->cbData, pcbActual));
185
186 int rc = VINF_SUCCESS;
187
188 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
189 if (pReq)
190 {
191 pReq->pv = pData->pvData;
192 pReq->cb = pData->cbData;
193 pReq->pcbActual = pcbActual;
194 const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
195 pReq->uEvent = idEvent;
196 if (idEvent)
197 {
198 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, pData->uFormat, pReq);
199 if (RT_SUCCESS(rc))
200 {
201 PSHCLEVENTPAYLOAD pPayload;
202 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
203 if (RT_SUCCESS(rc))
204 {
205 memcpy(pData->pvData, pPayload->pvData, RT_MIN(pData->cbData, pPayload->cbData));
206 pData->cbData = (uint32_t)pPayload->cbData; /** @todo r=bird: Just ditch this data block wrapper, it made you forget to set pcbActual! */
207 *pcbActual = (uint32_t)pPayload->cbData;
208
209 ShClPayloadFree(pPayload);
210 }
211 }
212
213 ShClEventUnregister(&pClient->EventSrc, idEvent);
214 }
215 else
216 {
217 RTMemFree(pReq);
218 rc = VERR_GENERAL_FAILURE;
219 }
220 }
221 else
222 rc = VERR_NO_MEMORY;
223
224 LogFlowFuncLeaveRC(rc);
225 return rc;
226}
227
228int ShClSvcImplWriteData(PSHCLCLIENT pClient,
229 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData)
230{
231 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
232 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
233 AssertPtrReturn(pData, VERR_INVALID_POINTER);
234
235 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
236 pClient, pData->pvData, pData->cbData, pData->uFormat));
237
238 int rc = ShClSvcDataReadSignal(pClient, pCmdCtx, pData);
239
240 LogFlowFuncLeaveRC(rc);
241 return rc;
242}
243
244/**
245 * Reports formats available in the X11 clipboard to VBox.
246 *
247 * @note Runs in Xt event thread.
248 *
249 * @param pCtx Opaque context pointer for the glue code.
250 * @param fFormats The formats available.
251 */
252DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
253{
254 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
255
256 /** @todo r=bird: BUGBUG: Revisit this */
257 if (fFormats == VBOX_SHCL_FMT_NONE) /* No formats to report? Bail out early. */
258 return;
259
260 int rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
261 RT_NOREF(rc);
262
263 LogFlowFuncLeaveRC(rc);
264}
265
266/**
267 * Completes a request from the host service for reading the X11 clipboard data.
268 * The data should be written to the buffer provided in the initial request.
269 *
270 * @note Runs in Xt event thread.
271 *
272 * @param pCtx Request context information.
273 * @param rcCompletion The completion status of the request.
274 * @param pReq Request to complete.
275 * @param pv Address of data from completed request. Optional.
276 * @param cb Size (in bytes) of data from completed request. Optional.
277 *
278 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
279 */
280DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
281 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
282{
283 RT_NOREF(rcCompletion);
284
285 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, uEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->uEvent));
286
287 AssertMsgRC(rcCompletion, ("Clipboard data completion from X11 failed with %Rrc\n", rcCompletion));
288
289 if (pReq->uEvent != NIL_SHCLEVENTID)
290 {
291 int rc2;
292
293 PSHCLEVENTPAYLOAD pPayload = NULL;
294 if (pv && cb)
295 {
296 rc2 = ShClPayloadAlloc(pReq->uEvent, pv, cb, &pPayload);
297 AssertRC(rc2);
298 }
299
300 RTCritSectEnter(&pCtx->pClient->CritSect);
301 rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->uEvent, pPayload);
302 AssertRC(rc2);
303 RTCritSectLeave(&pCtx->pClient->CritSect);
304 }
305
306 RTMemFree(pReq);
307}
308
309/**
310 * Reads clipboard data from the guest and passes it to the X11 clipboard.
311 *
312 * @note Runs in Xt event thread.
313 *
314 * @param pCtx Pointer to the host clipboard structure.
315 * @param fFormat The format in which the data should be transferred
316 * (VBOX_SHCL_FMT_XXX).
317 * @param ppv On success and if pcb > 0, this will point to a buffer
318 * to be freed with RTMemFree containing the data read.
319 * @param pcb On success, this contains the number of bytes of data
320 * returned.
321 */
322DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT fFormat, void **ppv, uint32_t *pcb)
323{
324 LogFlowFunc(("pCtx=%p, Format=0x%x\n", pCtx, fFormat));
325
326 if (pCtx->fShuttingDown)
327 {
328 /* The shared clipboard is disconnecting. */
329 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
330 return VERR_WRONG_ORDER;
331 }
332
333 int rc;
334
335#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
336 if (fFormat == VBOX_SHCL_FMT_URI_LIST)
337 rc = VINF_SUCCESS;
338 else
339#endif
340 {
341 /* Request data from the guest. */
342 SHCLEVENTID uEvent;
343 rc = ShClSvcDataReadRequest(pCtx->pClient, fFormat, &uEvent);
344 if (RT_SUCCESS(rc))
345 {
346 PSHCLEVENTPAYLOAD pPayload;
347 rc = ShClEventWait(&pCtx->pClient->EventSrc, uEvent, 30 * 1000, &pPayload);
348 if (RT_SUCCESS(rc))
349 {
350 *ppv = pPayload ? pPayload->pvData : NULL;
351 *pcb = pPayload ? pPayload->cbData : 0;
352
353 /* Detach the payload, as the caller then will own the data. */
354 ShClEventPayloadDetach(&pCtx->pClient->EventSrc, uEvent);
355 }
356
357 ShClEventUnregister(&pCtx->pClient->EventSrc, uEvent);
358 }
359 }
360
361 LogFlowFuncLeaveRC(rc);
362 return rc;
363}
364
365#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
366
367int ShClSvcImplTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
368{
369 RT_NOREF(pClient, pTransfer);
370
371 int rc = VINF_SUCCESS;
372
373 LogFlowFuncLeaveRC(rc);
374 return rc;
375}
376
377int ShClSvcImplTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
378{
379 RT_NOREF(pClient, pTransfer);
380
381 int rc = VINF_SUCCESS;
382
383 LogFlowFuncLeaveRC(rc);
384 return rc;
385}
386
387int ShClSvcImplTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
388{
389 LogFlowFuncEnter();
390
391 SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->EventSrc);
392
393 int rc = ShClEventRegister(&pClient->EventSrc, uEvent);
394 if (RT_SUCCESS(rc))
395 {
396 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
397 if (pReq)
398 {
399 pReq->uEvent = uEvent;
400
401 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
402 if (RT_SUCCESS(rc))
403 {
404 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
405 PSHCLEVENTPAYLOAD pPayload;
406 rc = ShClEventWait(&pClient->EventSrc, uEvent, 30 * 1000, &pPayload);
407 if (RT_SUCCESS(rc))
408 {
409 rc = ShClTransferRootsSet(pTransfer,
410 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
411 }
412 }
413 }
414
415 ShClEventUnregister(&pClient->EventSrc, uEvent);
416 }
417
418 LogFlowFuncLeaveRC(rc);
419 return rc;
420}
421
422#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
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