VirtualBox

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

Last change on this file since 100204 was 100204, checked in by vboxsync, 19 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 100204 2023-06-19 09:11:37Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <iprt/assert.h>
34#include <iprt/critsect.h>
35#include <iprt/env.h>
36#include <iprt/mem.h>
37#include <iprt/semaphore.h>
38#include <iprt/string.h>
39#include <iprt/asm.h>
40
41#include <VBox/GuestHost/SharedClipboard.h>
42#include <VBox/GuestHost/SharedClipboard-x11.h>
43#include <VBox/HostServices/VBoxClipboardSvc.h>
44#include <iprt/errcore.h>
45
46#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
47# include <VBox/GuestHost/SharedClipboard-transfers.h>
48#endif
49
50#include "VBoxSharedClipboardSvc-internal.h"
51
52/* Number of currently extablished connections. */
53static volatile uint32_t g_cShClConnections;
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59/**
60 * Global context information used by the host glue for the X11 clipboard backend.
61 */
62struct SHCLCONTEXT
63{
64 /** This mutex is grabbed during any critical operations on the clipboard
65 * which might clash with others. */
66 RTCRITSECT CritSect;
67 /** X11 context data. */
68 SHCLX11CTX X11;
69 /** Pointer to the VBox host client data structure. */
70 PSHCLCLIENT pClient;
71 /** We set this when we start shutting down as a hint not to post any new
72 * requests. */
73 bool fShuttingDown;
74};
75
76
77/*********************************************************************************************************************************
78* Prototypes *
79*********************************************************************************************************************************/
80static DECLCALLBACK(int) shClSvcX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser);
81static DECLCALLBACK(int) shClSvcX11RequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser);
82
83
84int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
85{
86 RT_NOREF(pBackend);
87
88 LogFlowFuncEnter();
89
90 /* Override the connection limit. */
91 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
92 pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
93
94 RT_ZERO(pBackend->Callbacks);
95 /* Use internal callbacks by default. */
96 pBackend->Callbacks.pfnReportFormats = shClSvcX11ReportFormatsCallback;
97 pBackend->Callbacks.pfnOnRequestDataFromSource = shClSvcX11RequestDataFromSourceCallback;
98
99 return VINF_SUCCESS;
100}
101
102void ShClBackendDestroy(PSHCLBACKEND pBackend)
103{
104 RT_NOREF(pBackend);
105
106 LogFlowFuncEnter();
107}
108
109void ShClBackendSetCallbacks(PSHCLBACKEND pBackend, PSHCLCALLBACKS pCallbacks)
110{
111#define SET_FN_IF_NOT_NULL(a_Fn) \
112 if (pCallbacks->pfn##a_Fn) \
113 pBackend->Callbacks.pfn##a_Fn = pCallbacks->pfn##a_Fn;
114
115 SET_FN_IF_NOT_NULL(ReportFormats);
116 SET_FN_IF_NOT_NULL(OnRequestDataFromSource);
117
118#undef SET_FN_IF_NOT_NULL
119}
120
121/**
122 * @note On the host, we assume that some other application already owns
123 * the clipboard and leave ownership to X11.
124 */
125int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
126{
127 int rc;
128
129 /* Check if maximum allowed connections count has reached. */
130 if (ASMAtomicIncU32(&g_cShClConnections) > VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX)
131 {
132 ASMAtomicDecU32(&g_cShClConnections);
133 LogRel(("Shared Clipboard: maximum amount for client connections reached\n"));
134 return VERR_OUT_OF_RESOURCES;
135 }
136
137 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
138 if (pCtx)
139 {
140 rc = RTCritSectInit(&pCtx->CritSect);
141 if (RT_SUCCESS(rc))
142 {
143 rc = ShClX11Init(&pCtx->X11, &pBackend->Callbacks, pCtx, fHeadless);
144 if (RT_SUCCESS(rc))
145 {
146 pClient->State.pCtx = pCtx;
147 pCtx->pClient = pClient;
148
149 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
150 if (RT_FAILURE(rc))
151 ShClX11Destroy(&pCtx->X11);
152 }
153
154 if (RT_FAILURE(rc))
155 RTCritSectDelete(&pCtx->CritSect);
156 }
157
158 if (RT_FAILURE(rc))
159 {
160 pClient->State.pCtx = NULL;
161 RTMemFree(pCtx);
162 }
163 }
164 else
165 rc = VERR_NO_MEMORY;
166
167 if (RT_FAILURE(rc))
168 {
169 /* Restore active connections count. */
170 ASMAtomicDecU32(&g_cShClConnections);
171 }
172
173 LogFlowFuncLeaveRC(rc);
174 return rc;
175}
176
177int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
178{
179 RT_NOREF(pBackend);
180
181 LogFlowFuncEnter();
182
183 /* Tell the guest we have no data in case X11 is not available. If
184 * there is data in the host clipboard it will automatically be sent to
185 * the guest when the clipboard starts up. */
186 if (ShClSvcIsBackendActive())
187 return ShClSvcHostReportFormats(pClient, VBOX_SHCL_FMT_NONE);
188 return VINF_SUCCESS;
189}
190
191/**
192 * Shuts down the shared clipboard service and "disconnect" the guest.
193 * Note! Host glue code
194 */
195int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
196{
197 RT_NOREF(pBackend);
198
199 LogFlowFuncEnter();
200
201 PSHCLCONTEXT pCtx = pClient->State.pCtx;
202 AssertPtr(pCtx);
203
204 /* Drop the reference to the client, in case it is still there. This
205 * will cause any outstanding clipboard data requests from X11 to fail
206 * immediately. */
207 pCtx->fShuttingDown = true;
208
209 int rc = ShClX11ThreadStop(&pCtx->X11);
210 /** @todo handle this slightly more reasonably, or be really sure
211 * it won't go wrong. */
212 AssertRC(rc);
213
214 ShClX11Destroy(&pCtx->X11);
215 RTCritSectDelete(&pCtx->CritSect);
216
217 RTMemFree(pCtx);
218
219 /* Decrease active connections count. */
220 ASMAtomicDecU32(&g_cShClConnections);
221
222 LogFlowFuncLeaveRC(rc);
223 return rc;
224}
225
226/**
227 * Reports clipboard formats to the host clipboard.
228 */
229int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
230{
231 RT_NOREF(pBackend);
232
233 int rc = ShClX11ReportFormatsToX11Async(&pClient->State.pCtx->X11, fFormats);
234
235 LogFlowFuncLeaveRC(rc);
236 return rc;
237}
238
239/**
240 * Reads data from the host clipboard.
241 *
242 * Schedules a request to the X11 event thread.
243 *
244 * @note We always fail or complete asynchronously.
245 */
246int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
247 void *pvData, uint32_t cbData, uint32_t *pcbActual)
248{
249 RT_NOREF(pBackend);
250
251 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
252 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
253 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
254 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
255
256 RT_NOREF(pCmdCtx);
257
258 LogFlowFunc(("pClient=%p, uFormat=%#x, pv=%p, cb=%RU32, pcbActual=%p\n",
259 pClient, uFormat, pvData, cbData, pcbActual));
260
261 PSHCLEVENT pEvent;
262 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
263 if (RT_SUCCESS(rc))
264 {
265 rc = ShClX11ReadDataFromX11Async(&pClient->State.pCtx->X11, uFormat, cbData, pEvent);
266 if (RT_SUCCESS(rc))
267 {
268 PSHCLEVENTPAYLOAD pPayload;
269 rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
270 if (RT_SUCCESS(rc))
271 {
272 if (pPayload)
273 {
274 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
275 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
276
277 size_t const cbToCopy = RT_MIN(cbData, pPayload->cbData);
278 if (cbToCopy) /* memcpy doesn't like 0 byte inputs. */
279 memcpy(pvData, pResp->Read.pvData, RT_MIN(cbData, pPayload->cbData));
280
281 LogRel2(("Shared Clipboard: Read %RU32 bytes host X11 clipboard data\n", pResp->Read.cbData));
282
283 *pcbActual = pResp->Read.cbData;
284
285 RTMemFree(pResp->Read.pvData);
286 pResp->Read.cbData = 0;
287
288 ShClPayloadFree(pPayload);
289 }
290 else /* No payload given; could happen on invalid / not-expected formats. */
291 *pcbActual = 0;
292 }
293 }
294
295 ShClEventRelease(pEvent);
296 }
297
298 if (RT_FAILURE(rc))
299 LogRel(("Shared Clipboard: Error reading host clipboard data from X11, rc=%Rrc\n", rc));
300
301 LogFlowFuncLeaveRC(rc);
302 return rc;
303}
304
305int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
306 SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
307{
308 RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
309
310 LogFlowFuncEnter();
311
312 /* Nothing to do here yet. */
313
314 LogFlowFuncLeave();
315 return VINF_SUCCESS;
316}
317
318/**
319 * @copydoc SHCLCALLBACKS::pfnReportFormats
320 *
321 * Reports clipboard formats to the guest.
322 */
323static DECLCALLBACK(int) shClSvcX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
324{
325 RT_NOREF(pvUser);
326
327 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
328
329 int rc = VINF_SUCCESS;
330 PSHCLCLIENT pClient = pCtx->pClient;
331 AssertPtr(pClient);
332
333 rc = RTCritSectEnter(&pClient->CritSect);
334 if (RT_SUCCESS(rc))
335 {
336 if (ShClSvcIsBackendActive())
337 {
338 /** @todo r=bird: BUGBUG: Revisit this */
339 if (fFormats != VBOX_SHCL_FMT_NONE) /* No formats to report? */
340 {
341 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
342 }
343 }
344
345 RTCritSectLeave(&pClient->CritSect);
346 }
347
348 LogFlowFuncLeaveRC(rc);
349 return rc;
350}
351
352/**
353 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
354 *
355 * Requests clipboard data from the guest.
356 *
357 * @thread Called from X11 event thread.
358 */
359static DECLCALLBACK(int) shClSvcX11RequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
360 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
361{
362 RT_NOREF(pvUser);
363
364 LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
365
366 if (pCtx->fShuttingDown)
367 {
368 /* The shared clipboard is disconnecting. */
369 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
370 return VERR_WRONG_ORDER;
371 }
372
373 int rc = VINF_SUCCESS;
374
375#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
376 /*
377 * Note: We always return a generic URI list here.
378 * As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
379 * to decide & transform the list into the actual clipboard Atom target format the caller wanted.
380 */
381 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
382 {
383 PSHCLTRANSFER pTransfer = ShClTransferHttpServerGetTransferFirst(&pCtx->X11.HttpCtx.HttpServer);
384 if (pTransfer)
385 {
386 if (RT_SUCCESS(rc))
387 rc = ShClTransferRootListRead(pTransfer);
388 }
389
390 /** @todo BUGBUG IMPLEMENT THIS! */
391
392 *ppv = NULL;
393 *pcb = 0;
394
395 rc = VERR_NO_DATA;
396 }
397#endif
398
399 if (RT_SUCCESS(rc))
400 {
401 /* Request data from the guest and for data to arrive. */
402 PSHCLEVENT pEvent;
403 rc = ShClSvcReadDataFromGuestAsync(pCtx->pClient, uFmt, &pEvent);
404 if (RT_SUCCESS(rc))
405 {
406 PSHCLEVENTPAYLOAD pPayload;
407 rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
408 if (RT_SUCCESS(rc))
409 {
410 if ( !pPayload
411 || !pPayload->cbData)
412 {
413 rc = VERR_NO_DATA;
414 }
415 else
416 {
417 *ppv = pPayload->pvData;
418 *pcb = pPayload->cbData;
419 }
420 }
421
422 ShClEventRelease(pEvent);
423 }
424 }
425
426 if (RT_FAILURE(rc))
427 LogRel(("Shared Clipboard: Requesting data in format %#x for X11 host failed with %Rrc\n", uFmt, rc));
428
429 LogFlowFuncLeaveRC(rc);
430 return rc;
431}
432
433#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
434
435int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
436{
437 RT_NOREF(pBackend);
438#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
439 /* We only need to start the HTTP server (and register the transfer to it) when we actually receive data from the guest. */
440 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
441 return ShClTransferHttpServerMaybeStart(&pClient->State.pCtx->X11.HttpCtx);
442#else
443 RT_NOREF(pClient, pTransfer);
444#endif
445 return VINF_SUCCESS;
446}
447
448int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
449{
450 RT_NOREF(pBackend);
451#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
452 /* See comment in ShClBackendTransferCreate(). */
453 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
454 return ShClTransferHttpServerMaybeStop(&pClient->State.pCtx->X11.HttpCtx);
455#else
456 RT_NOREF(pClient, pTransfer);
457#endif
458 return VINF_SUCCESS;
459}
460
461int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
462{
463 RT_NOREF(pBackend);
464
465 LogFlowFuncEnter();
466
467 PSHCLEVENT pEvent;
468 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
469 if (RT_SUCCESS(rc))
470 {
471 rc = ShClX11ReadDataFromX11Async(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, UINT32_MAX, pEvent);
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(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
477 if (RT_SUCCESS(rc))
478 {
479 if (pPayload)
480 {
481 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
482 AssertPtr(pPayload->pvData);
483 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
484
485 rc = ShClTransferRootsInitFromStringList(pTransfer,
486 (char *)pResp->Read.pvData, pResp->Read.cbData + 1 /* Include zero terminator */);
487 if (RT_SUCCESS(rc))
488 rc = ShClTransferRootListRead(pTransfer);
489
490 if (RT_SUCCESS(rc))
491 LogRel2(("Shared Clipboard: Host reported %RU64 X11 root entries for transfer to guest\n", ShClTransferRootsCount(pTransfer)));
492
493 RTMemFree(pResp->Read.pvData);
494 pResp->Read.cbData = 0;
495
496 ShClPayloadFree(pPayload);
497 pPayload = NULL;
498 }
499 else
500 rc = VERR_NO_DATA; /* No payload. */
501 }
502 }
503
504 ShClEventRelease(pEvent);
505 }
506 else
507 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
508
509 LogFlowFuncLeaveRC(rc);
510 return rc;
511}
512#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
513
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