VirtualBox

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

Last change on this file since 92182 was 91749, checked in by vboxsync, 3 years ago

VRDP: Shared Clipboard: delegate clipboard processing to VRDP service when remote RDP clients are connected, bugref:10117.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 91749 2021-10-14 21:02:32Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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#include <iprt/asm.h>
30
31#include <VBox/GuestHost/SharedClipboard.h>
32#include <VBox/GuestHost/SharedClipboard-x11.h>
33#include <VBox/HostServices/VBoxClipboardSvc.h>
34#include <iprt/errcore.h>
35
36#include "VBoxSharedClipboardSvc-internal.h"
37
38/* Number of currently extablished connections. */
39static volatile uint32_t g_cShClConnections;
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/**
46 * Global context information used by the host glue for the X11 clipboard backend.
47 */
48struct SHCLCONTEXT
49{
50 /** This mutex is grabbed during any critical operations on the clipboard
51 * which might clash with others. */
52 RTCRITSECT CritSect;
53 /** X11 context data. */
54 SHCLX11CTX X11;
55 /** Pointer to the VBox host client data structure. */
56 PSHCLCLIENT pClient;
57 /** We set this when we start shutting down as a hint not to post any new
58 * requests. */
59 bool fShuttingDown;
60};
61
62
63int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
64{
65 LogFlowFuncEnter();
66
67 /* Override the connection limit. */
68 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
69 pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
70
71 return VINF_SUCCESS;
72}
73
74void ShClBackendDestroy(void)
75{
76 LogFlowFuncEnter();
77}
78
79/**
80 * @note On the host, we assume that some other application already owns
81 * the clipboard and leave ownership to X11.
82 */
83int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
84{
85 int rc;
86
87 /* Check if maximum allowed connections count has reached. */
88 if (ASMAtomicIncU32(&g_cShClConnections) > VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX)
89 {
90 ASMAtomicDecU32(&g_cShClConnections);
91 LogRel(("Shared Clipboard: maximum amount for client connections reached\n"));
92 return VERR_OUT_OF_RESOURCES;
93 }
94
95 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
96 if (pCtx)
97 {
98 rc = RTCritSectInit(&pCtx->CritSect);
99 if (RT_SUCCESS(rc))
100 {
101 rc = ShClX11Init(&pCtx->X11, pCtx, fHeadless);
102 if (RT_SUCCESS(rc))
103 {
104 pClient->State.pCtx = pCtx;
105 pCtx->pClient = pClient;
106
107 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
108 if (RT_FAILURE(rc))
109 ShClX11Destroy(&pCtx->X11);
110 }
111
112 if (RT_FAILURE(rc))
113 RTCritSectDelete(&pCtx->CritSect);
114 }
115
116 if (RT_FAILURE(rc))
117 {
118 pClient->State.pCtx = NULL;
119 RTMemFree(pCtx);
120 }
121 }
122 else
123 rc = VERR_NO_MEMORY;
124
125 if (RT_FAILURE(rc))
126 {
127 /* Restore active connections count. */
128 ASMAtomicDecU32(&g_cShClConnections);
129 }
130
131 LogFlowFuncLeaveRC(rc);
132 return rc;
133}
134
135int ShClBackendSync(PSHCLCLIENT pClient)
136{
137 LogFlowFuncEnter();
138
139 /* Tell the guest we have no data in case X11 is not available. If
140 * there is data in the host clipboard it will automatically be sent to
141 * the guest when the clipboard starts up. */
142 if (ShClSvcIsBackendActive())
143 return ShClSvcHostReportFormats(pClient, VBOX_SHCL_FMT_NONE);
144 return VINF_SUCCESS;
145}
146
147/*
148 * Shut down the shared clipboard service and "disconnect" the guest.
149 * Note! Host glue code
150 */
151int ShClBackendDisconnect(PSHCLCLIENT pClient)
152{
153 LogFlowFuncEnter();
154
155 PSHCLCONTEXT pCtx = pClient->State.pCtx;
156 AssertPtr(pCtx);
157
158 /* Drop the reference to the client, in case it is still there. This
159 * will cause any outstanding clipboard data requests from X11 to fail
160 * immediately. */
161 pCtx->fShuttingDown = true;
162
163 int rc = ShClX11ThreadStop(&pCtx->X11);
164 /** @todo handle this slightly more reasonably, or be really sure
165 * it won't go wrong. */
166 AssertRC(rc);
167
168 ShClX11Destroy(&pCtx->X11);
169 RTCritSectDelete(&pCtx->CritSect);
170
171 RTMemFree(pCtx);
172
173 /* Decrease active connections count. */
174 ASMAtomicDecU32(&g_cShClConnections);
175
176 LogFlowFuncLeaveRC(rc);
177 return rc;
178}
179
180int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
181{
182 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, fFormats);
183
184 LogFlowFuncLeaveRC(rc);
185 return rc;
186}
187
188/** Structure describing a request for clipoard data from the guest. */
189struct CLIPREADCBREQ
190{
191 /** User-supplied data pointer, based on the request type. */
192 void *pv;
193 /** The size (in bytes) of the the user-supplied pointer in pv. */
194 uint32_t cb;
195 /** The actual size of the data written. */
196 uint32_t *pcbActual;
197 /** The request's event ID. */
198 SHCLEVENTID idEvent;
199};
200
201/**
202 * @note We always fail or complete asynchronously.
203 * @note On success allocates a CLIPREADCBREQ structure which must be
204 * freed in ClipCompleteDataRequestFromX11 when it is called back from
205 * the backend code.
206 */
207int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
208 void *pvData, uint32_t cbData, uint32_t *pcbActual)
209{
210 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
211 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
212 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
213 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
214
215 RT_NOREF(pCmdCtx);
216
217 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
218 pClient, uFormat, pvData, cbData, pcbActual));
219
220 int rc = VINF_SUCCESS;
221
222 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
223 if (pReq)
224 {
225 pReq->pv = pvData;
226 pReq->cb = cbData;
227 pReq->pcbActual = pcbActual;
228 const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
229 pReq->idEvent = idEvent;
230 if (idEvent != NIL_SHCLEVENTID)
231 {
232 /* Note: ShClX11ReadDataFromX11() will consume pReq on success. */
233 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, uFormat, pReq);
234 if (RT_SUCCESS(rc))
235 {
236 PSHCLEVENTPAYLOAD pPayload;
237 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
238 if (RT_SUCCESS(rc))
239 {
240 if (pPayload)
241 {
242 memcpy(pvData, pPayload->pvData, RT_MIN(cbData, pPayload->cbData));
243
244 *pcbActual = (uint32_t)pPayload->cbData;
245
246 ShClPayloadFree(pPayload);
247 }
248 else /* No payload given; could happen on invalid / not-expected formats. */
249 *pcbActual = 0;
250 }
251 }
252
253 ShClEventUnregister(&pClient->EventSrc, idEvent);
254 }
255 else
256 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
257
258 if (RT_FAILURE(rc))
259 RTMemFree(pReq);
260 }
261 else
262 rc = VERR_NO_MEMORY;
263
264 if (RT_FAILURE(rc))
265 LogRel(("Shared Clipboard: Error reading host clipboard data from X11, rc=%Rrc\n", rc));
266
267 LogFlowFuncLeaveRC(rc);
268 return rc;
269}
270
271int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
272{
273 RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
274
275 LogFlowFuncEnter();
276
277 /* Nothing to do here yet. */
278
279 LogFlowFuncLeave();
280 return VINF_SUCCESS;
281}
282
283DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
284{
285 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
286
287 int rc = VINF_SUCCESS;
288 PSHCLCLIENT pClient = pCtx->pClient;
289 AssertPtr(pClient);
290
291 rc = RTCritSectEnter(&pClient->CritSect);
292 if (RT_SUCCESS(rc))
293 {
294 if (ShClSvcIsBackendActive())
295 {
296 /** @todo r=bird: BUGBUG: Revisit this */
297 if (fFormats != VBOX_SHCL_FMT_NONE) /* No formats to report? */
298 {
299 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
300 }
301 }
302
303 RTCritSectLeave(&pClient->CritSect);
304 }
305
306 LogFlowFuncLeaveRC(rc);
307}
308
309DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
310 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
311{
312 AssertPtrReturnVoid(pCtx);
313 AssertPtrReturnVoid(pReq);
314
315 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->idEvent));
316
317 if (pReq->idEvent != NIL_SHCLEVENTID)
318 {
319 int rc2;
320
321 PSHCLEVENTPAYLOAD pPayload = NULL;
322 if ( RT_SUCCESS(rcCompletion)
323 && pv
324 && cb)
325 {
326 rc2 = ShClPayloadAlloc(pReq->idEvent, pv, cb, &pPayload);
327 AssertRC(rc2);
328 }
329
330 rc2 = RTCritSectEnter(&pCtx->pClient->CritSect);
331 if (RT_SUCCESS(rc2))
332 {
333 rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->idEvent, pPayload);
334 RTCritSectLeave(&pCtx->pClient->CritSect);
335 if (RT_SUCCESS(rc2))
336 pPayload = NULL;
337 }
338
339 if (pPayload)
340 ShClPayloadFree(pPayload);
341 }
342
343 if (pReq)
344 RTMemFree(pReq);
345
346 LogRel2(("Shared Clipboard: Request for clipboard data from X11 host completed with %Rrc\n", rcCompletion));
347}
348
349DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb)
350{
351 LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
352
353 if (pCtx->fShuttingDown)
354 {
355 /* The shared clipboard is disconnecting. */
356 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
357 return VERR_WRONG_ORDER;
358 }
359
360 PSHCLCLIENT pClient = pCtx->pClient;
361 AssertPtr(pClient);
362
363 RTCritSectEnter(&pClient->CritSect);
364
365 int rc = VINF_SUCCESS;
366
367#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
368 /*
369 * Note: We always return a generic URI list here.
370 * As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
371 * to decide & transform the list into the actual clipboard Atom target format the caller wanted.
372 */
373 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
374 {
375 PSHCLTRANSFER pTransfer;
376 rc = shClSvcTransferStart(pCtx->pClient,
377 SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
378 &pTransfer);
379 if (RT_SUCCESS(rc))
380 {
381
382 }
383 else
384 LogRel(("Shared Clipboard: Initializing read transfer from guest failed with %Rrc\n", rc));
385
386 *ppv = NULL;
387 *pcb = 0;
388
389 rc = VERR_NO_DATA;
390 }
391#endif
392
393 if (RT_SUCCESS(rc))
394 {
395 /* Request data from the guest. */
396 SHCLEVENTID idEvent;
397 rc = ShClSvcGuestDataRequest(pCtx->pClient, uFmt, &idEvent);
398 if (RT_SUCCESS(rc))
399 {
400 RTCritSectLeave(&pClient->CritSect);
401
402 PSHCLEVENTPAYLOAD pPayload;
403 rc = ShClEventWait(&pCtx->pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
404 if (RT_SUCCESS(rc))
405 {
406 if ( !pPayload
407 || !pPayload->cbData)
408 {
409 rc = VERR_NO_DATA;
410 }
411 else
412 {
413 *ppv = pPayload->pvData;
414 *pcb = pPayload->cbData;
415 }
416 }
417
418 RTCritSectEnter(&pClient->CritSect);
419
420 ShClEventRelease(&pCtx->pClient->EventSrc, idEvent);
421 ShClEventUnregister(&pCtx->pClient->EventSrc, idEvent);
422 }
423 }
424
425 RTCritSectLeave(&pClient->CritSect);
426
427 if (RT_FAILURE(rc))
428 LogRel(("Shared Clipboard: Requesting data in format %#x for X11 host failed with %Rrc\n", uFmt, rc));
429
430 LogFlowFuncLeaveRC(rc);
431 return rc;
432}
433
434#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
435
436int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
437{
438#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
439 return ShClHttpTransferRegister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
440#else
441 RT_NOREF(pClient, pTransfer);
442#endif
443 return VERR_NOT_IMPLEMENTED;
444}
445
446int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
447{
448#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
449 return ShClHttpTransferUnregister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
450#else
451 RT_NOREF(pClient, pTransfer);
452#endif
453
454 return VINF_SUCCESS;
455}
456
457int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
458{
459 LogFlowFuncEnter();
460
461 int rc;
462
463 SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
464 if (idEvent != NIL_SHCLEVENTID)
465 {
466 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
467 if (pReq)
468 {
469 pReq->idEvent = idEvent;
470
471 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
472 if (RT_SUCCESS(rc))
473 {
474 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
475 PSHCLEVENTPAYLOAD pPayload;
476 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
477 if (RT_SUCCESS(rc))
478 {
479 rc = ShClTransferRootsSet(pTransfer,
480 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
481 }
482 }
483 }
484 else
485 rc = VERR_NO_MEMORY;
486
487 ShClEventUnregister(&pClient->EventSrc, idEvent);
488 }
489 else
490 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
491
492 LogFlowFuncLeaveRC(rc);
493 return rc;
494}
495#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
496
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