VirtualBox

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

Last change on this file since 85922 was 85834, checked in by vboxsync, 4 years ago

Shared Clipboard/X11: Return VERR_NO_DATA in X11 callback implementations to make it easier to spot that there is no data available.

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