VirtualBox

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

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

Shared Clipboard/VBoxClient: Initial commit for experimental FUSE support.

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