VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp@ 83184

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

Shared Clipboard: ticketref:19336 Linux guest: shared clipboard doesn't work (on fresh VirtualBox-6.1.4-136177)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.5 KB
Line 
1/** $Id: clipboard.cpp 83184 2020-03-02 14:56:27Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard.
4 */
5
6/*
7 * Copyright (C) 2007-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#include <iprt/alloc.h>
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
26# include <iprt/dir.h>
27#endif
28#include <iprt/initterm.h>
29#include <iprt/mem.h>
30#include <iprt/string.h>
31#include <iprt/path.h>
32#include <iprt/process.h>
33#include <iprt/semaphore.h>
34
35#include <VBox/log.h>
36#include <VBox/VBoxGuestLib.h>
37#include <VBox/HostServices/VBoxClipboardSvc.h>
38#include <VBox/GuestHost/SharedClipboard.h>
39#include <VBox/GuestHost/SharedClipboard-x11.h>
40
41#include "VBoxClient.h"
42
43#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
44# include "clipboard-fuse.h"
45#endif
46
47
48/*********************************************************************************************************************************
49* Global Variables *
50*********************************************************************************************************************************/
51
52#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
53typedef struct _SHCLCTXFUSE
54{
55 RTTHREAD Thread;
56} SHCLCTXFUSE;
57#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
58
59/**
60 * Global clipboard context information.
61 */
62struct SHCLCONTEXT
63{
64 /** Client command context */
65 VBGLR3SHCLCMDCTX CmdCtx;
66#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
67 /** Associated transfer data. */
68 SHCLTRANSFERCTX TransferCtx;
69# ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
70 SHCLCTXFUSE FUSE;
71# endif
72#endif
73 /** X11 clipboard context. */
74 SHCLX11CTX X11;
75};
76
77/** Only one client is supported. There seems to be no need for more clients. */
78static SHCLCONTEXT g_Ctx;
79
80
81/**
82 * Get clipboard data from the host.
83 *
84 * @returns VBox result code
85 * @param pCtx Our context information.
86 * @param Format The format of the data being requested.
87 * @param ppv On success and if pcb > 0, this will point to a buffer
88 * to be freed with RTMemFree containing the data read.
89 * @param pcb On success, this contains the number of bytes of data
90 * returned.
91 */
92DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT Format, void **ppv, uint32_t *pcb)
93{
94 RT_NOREF(pCtx);
95
96 LogFlowFunc(("Format=0x%x\n", Format));
97
98 int rc = VINF_SUCCESS;
99
100 uint32_t cbRead = 0;
101
102#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
103 if (Format == VBOX_SHCL_FMT_URI_LIST)
104 {
105 //rc = VbglR3ClipboardRootListRead()
106 }
107 else
108#endif
109 {
110 uint32_t cbData = _4K; /** @todo Make this dynamic. */
111 void *pvData = RTMemAlloc(cbData);
112 if (pvData)
113 {
114 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, Format, pvData, cbData, &cbRead);
115 }
116 else
117 rc = VERR_NO_MEMORY;
118
119 /*
120 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
121 * larger buffer. The size of the buffer needed is placed in *pcb.
122 * So we start all over again.
123 */
124 if (rc == VINF_BUFFER_OVERFLOW)
125 {
126 /* cbRead contains the size required. */
127
128 cbData = cbRead;
129 pvData = RTMemRealloc(pvData, cbRead);
130 if (pvData)
131 {
132 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, Format, pvData, cbData, &cbRead);
133 if (rc == VINF_BUFFER_OVERFLOW)
134 rc = VERR_BUFFER_OVERFLOW;
135 }
136 else
137 rc = VERR_NO_MEMORY;
138 }
139
140 if (RT_SUCCESS(rc))
141 {
142 *pcb = cbRead; /* Actual bytes read. */
143 *ppv = pvData;
144 }
145
146 /*
147 * Catch other errors. This also catches the case in which the buffer was
148 * too small a second time, possibly because the clipboard contents
149 * changed half-way through the operation. Since we can't say whether or
150 * not this is actually an error, we just return size 0.
151 */
152 if (RT_FAILURE(rc))
153 RTMemFree(pvData);
154 }
155
156 LogFlowFuncLeaveRC(rc);
157 return rc;
158}
159
160/**
161 * Opaque data structure describing a request from the host for clipboard
162 * data, passed in when the request is forwarded to the X11 backend so that
163 * it can be completed correctly.
164 */
165struct CLIPREADCBREQ
166{
167 /** The data format that was requested. */
168 SHCLFORMAT Format;
169};
170
171/**
172 * Tell the host that new clipboard formats are available.
173 *
174 * @param pCtx Our context information.
175 * @param fFormats The formats to report.
176 */
177DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, SHCLFORMATS fFormats)
178{
179 RT_NOREF(pCtx);
180
181 LogFlowFunc(("Formats=0x%x\n", fFormats));
182
183 int rc2 = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
184 RT_NOREF(rc2);
185 LogFlowFuncLeaveRC(rc2);
186}
187
188/**
189 * This is called by the backend to tell us that a request for data from
190 * X11 has completed.
191 *
192 * @param pCtx Our context information.
193 * @param rc The IPRT result code of the request.
194 * @param pReq The request structure that we passed in when we started
195 * the request. We RTMemFree() this in this function.
196 * @param pv The clipboard data returned from X11 if the request succeeded (see @a rc).
197 * @param cb The size of the data in @a pv.
198 */
199DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
200{
201 RT_NOREF(pCtx, rc);
202
203 LogFlowFunc(("rc=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", rc, pReq->Format, pv, cb));
204
205 int rc2 = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pReq->Format, pv, cb);
206 RT_NOREF(rc2);
207
208 RTMemFree(pReq);
209
210 LogFlowFuncLeaveRC(rc2);
211}
212
213/**
214 * Connect the guest clipboard to the host.
215 *
216 * @returns VBox status code.
217 */
218static int vboxClipboardConnect(void)
219{
220 LogFlowFuncEnter();
221
222 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
223 if (RT_SUCCESS(rc))
224 {
225 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
226 if (RT_SUCCESS(rc))
227 {
228 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
229 if (RT_FAILURE(rc))
230 ShClX11ThreadStop(&g_Ctx.X11);
231 }
232 }
233 else
234 rc = VERR_NO_MEMORY;
235
236 if (RT_FAILURE(rc))
237 {
238 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
239
240 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
241 ShClX11Destroy(&g_Ctx.X11);
242 }
243
244 LogFlowFuncLeaveRC(rc);
245 return rc;
246}
247
248/**
249 * The main loop of our clipboard reader.
250 */
251int vboxClipboardMain(void)
252{
253 LogRel(("Worker loop running\n"));
254
255 int rc;
256
257 PSHCLCONTEXT pCtx = &g_Ctx;
258
259 bool fShutdown = false;
260
261 /* The thread waits for incoming messages from the host. */
262 for (;;)
263 {
264 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
265 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
266
267 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
268 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
269
270 uint32_t idMsg = 0;
271 uint32_t cParms = 0;
272 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
273 if (RT_SUCCESS(rc))
274 {
275#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
276 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
277#else
278 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
279#endif
280 }
281
282 if (RT_FAILURE(rc))
283 {
284 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
285
286 VbglR3ClipboardEventFree(pEvent);
287 pEvent = NULL;
288
289 if (fShutdown)
290 break;
291
292 /* Wait a bit before retrying. */
293 RTThreadSleep(1000);
294 continue;
295 }
296 else
297 {
298 AssertPtr(pEvent);
299 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
300
301 switch (pEvent->enmType)
302 {
303 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
304 {
305 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.ReportedFormats.Formats);
306 break;
307 }
308
309 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
310 {
311 /* The host needs data in the specified format. */
312 CLIPREADCBREQ *pReq;
313 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
314 if (pReq)
315 {
316 pReq->Format = pEvent->u.fReadData;
317 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
318 }
319 else
320 rc = VERR_NO_MEMORY;
321 break;
322 }
323
324 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
325 {
326 LogRel2(("Host requested termination\n"));
327 fShutdown = true;
328 break;
329 }
330
331#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
332 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
333 {
334 /* Nothing to do here. */
335 rc = VINF_SUCCESS;
336 break;
337 }
338#endif
339 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
340 {
341 /* Nothing to do here. */
342 rc = VINF_SUCCESS;
343 break;
344 }
345
346 default:
347 {
348 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
349 }
350 }
351
352 if (pEvent)
353 {
354 VbglR3ClipboardEventFree(pEvent);
355 pEvent = NULL;
356 }
357 }
358
359 if (fShutdown)
360 break;
361 }
362
363 LogRel(("Worker loop ended\n"));
364
365 LogFlowFuncLeaveRC(rc);
366 return rc;
367}
368
369#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
370static DECLCALLBACK(int) vboxClipoardFUSEThread(RTTHREAD hThreadSelf, void *pvUser)
371{
372 RT_NOREF(hThreadSelf, pvUser);
373
374 VbglR3Init();
375
376 LogFlowFuncEnter();
377
378 RTThreadUserSignal(hThreadSelf);
379
380 SHCL_FUSE_OPTS Opts;
381 RT_ZERO(Opts);
382
383 Opts.fForeground = true;
384 Opts.fSingleThreaded = false; /** @todo Do we want multithread here? */
385
386 int rc = RTPathTemp(Opts.szMountPoint, sizeof(Opts.szMountPoint));
387 if (RT_SUCCESS(rc))
388 {
389 rc = RTPathAppend(Opts.szMountPoint, sizeof(Opts.szMountPoint), "VBoxSharedClipboard");
390 if (RT_SUCCESS(rc))
391 {
392 rc = RTDirCreate(Opts.szMountPoint, 0700,
393 RTDIRCREATE_FLAGS_NO_SYMLINKS);
394 if (rc == VERR_ALREADY_EXISTS)
395 rc = VINF_SUCCESS;
396 }
397 }
398
399 if (RT_SUCCESS(rc))
400 {
401 rc = ShClFuseMain(&Opts);
402 }
403 else
404 LogRel(("Error creating FUSE mount directory, rc=%Rrc\n", rc));
405
406 LogFlowFuncLeaveRC(rc);
407 return rc;
408}
409
410static int vboxClipboardFUSEStart()
411{
412 LogFlowFuncEnter();
413
414 PSHCLCONTEXT pCtx = &g_Ctx;
415
416 int rc = RTThreadCreate(&pCtx->FUSE.Thread, vboxClipoardFUSEThread, &pCtx->FUSE, 0,
417 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLFUSE");
418 if (RT_SUCCESS(rc))
419 rc = RTThreadUserWait(pCtx->FUSE.Thread, 30 * 1000);
420
421 LogFlowFuncLeaveRC(rc);
422 return rc;
423}
424
425static int vboxClipboardFUSEStop()
426{
427 LogFlowFuncEnter();
428
429 PSHCLCONTEXT pCtx = &g_Ctx;
430
431 int rcThread;
432 int rc = RTThreadWait(pCtx->FUSE.Thread, 1000, &rcThread);
433
434 LogFlowFuncLeaveRC(rc);
435 return rc;
436}
437#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
438
439static const char *getName()
440{
441 return "Shared Clipboard";
442}
443
444static const char *getPidFilePath()
445{
446 return ".vboxclient-clipboard.pid";
447}
448
449static int init(struct VBCLSERVICE **pSelf)
450{
451 RT_NOREF(pSelf);
452
453 int rc;
454
455#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
456 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
457#else
458 rc = VINF_SUCCESS;
459#endif
460
461 LogFlowFuncLeaveRC(rc);
462 return rc;
463}
464
465static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
466{
467 RT_NOREF(ppInterface, fDaemonised);
468
469 /* Initialise the guest library. */
470 int rc = vboxClipboardConnect();
471 if (RT_SUCCESS(rc))
472 {
473#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
474 rc = vboxClipboardFUSEStart();
475 if (RT_SUCCESS(rc))
476 {
477#endif
478 rc = vboxClipboardMain();
479
480#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
481 int rc2 = vboxClipboardFUSEStop();
482 if (RT_SUCCESS(rc))
483 rc = rc2;
484 }
485#endif
486 }
487
488 if (RT_FAILURE(rc))
489 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
490
491 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
492 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
493
494 return rc;
495}
496
497static void cleanup(struct VBCLSERVICE **ppInterface)
498{
499 RT_NOREF(ppInterface);
500
501#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
502 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
503#endif
504}
505
506struct VBCLSERVICE vbclClipboardInterface =
507{
508 getName,
509 getPidFilePath,
510 init,
511 run,
512 cleanup
513};
514
515struct CLIPBOARDSERVICE
516{
517 struct VBCLSERVICE *pInterface;
518};
519
520struct VBCLSERVICE **VBClGetClipboardService(void)
521{
522 struct CLIPBOARDSERVICE *pService =
523 (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService));
524
525 if (!pService)
526 VBClLogFatalError("Out of memory\n");
527 pService->pInterface = &vbclClipboardInterface;
528 return &pService->pInterface;
529}
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