VirtualBox

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

Last change on this file since 85817 was 85798, checked in by vboxsync, 4 years ago

Shared Clipboard/X11: Only write clipboard data when clipboard request has succeeded.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.7 KB
Line 
1/** $Id: clipboard.cpp 85798 2020-08-17 12:55:46Z 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 rcCompletion The completion status 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,
200 int rcCompletion, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
201{
202 LogFlowFunc(("rcCompletion=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", rcCompletion, pReq->Format, pv, cb));
203
204 if (RT_SUCCESS(rcCompletion)) /* Only write data if the request succeeded. */
205 {
206 AssertPtrReturnVoid(pv);
207 AssertReturnVoid(pv);
208
209 rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pReq->Format, pv, cb);
210 }
211
212 RTMemFree(pReq);
213
214 LogFlowFuncLeaveRC(rcCompletion);
215}
216
217/**
218 * Connect the guest clipboard to the host.
219 *
220 * @returns VBox status code.
221 */
222static int vboxClipboardConnect(void)
223{
224 LogFlowFuncEnter();
225
226 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
227 if (RT_SUCCESS(rc))
228 {
229 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
230 if (RT_SUCCESS(rc))
231 {
232 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
233 if (RT_FAILURE(rc))
234 ShClX11ThreadStop(&g_Ctx.X11);
235 }
236 }
237 else
238 rc = VERR_NO_MEMORY;
239
240 if (RT_FAILURE(rc))
241 {
242 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
243
244 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
245 ShClX11Destroy(&g_Ctx.X11);
246 }
247
248 LogFlowFuncLeaveRC(rc);
249 return rc;
250}
251
252/**
253 * The main loop of our clipboard reader.
254 */
255int vboxClipboardMain(void)
256{
257 LogRel(("Worker loop running\n"));
258
259 int rc;
260
261 PSHCLCONTEXT pCtx = &g_Ctx;
262
263 bool fShutdown = false;
264
265 /* The thread waits for incoming messages from the host. */
266 for (;;)
267 {
268 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
269 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
270
271 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
272 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
273
274 uint32_t idMsg = 0;
275 uint32_t cParms = 0;
276 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
277 if (RT_SUCCESS(rc))
278 {
279#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
280 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
281#else
282 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
283#endif
284 }
285
286 if (RT_FAILURE(rc))
287 {
288 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
289
290 VbglR3ClipboardEventFree(pEvent);
291 pEvent = NULL;
292
293 if (fShutdown)
294 break;
295
296 /* Wait a bit before retrying. */
297 RTThreadSleep(1000);
298 continue;
299 }
300 else
301 {
302 AssertPtr(pEvent);
303 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
304
305 switch (pEvent->enmType)
306 {
307 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
308 {
309 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
310 break;
311 }
312
313 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
314 {
315 /* The host needs data in the specified format. */
316 CLIPREADCBREQ *pReq;
317 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
318 if (pReq)
319 {
320 pReq->Format = pEvent->u.fReadData;
321 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
322 }
323 else
324 rc = VERR_NO_MEMORY;
325 break;
326 }
327
328 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
329 {
330 LogRel2(("Host requested termination\n"));
331 fShutdown = true;
332 break;
333 }
334
335#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
336 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
337 {
338 /* Nothing to do here. */
339 rc = VINF_SUCCESS;
340 break;
341 }
342#endif
343 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
344 {
345 /* Nothing to do here. */
346 rc = VINF_SUCCESS;
347 break;
348 }
349
350 default:
351 {
352 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
353 }
354 }
355
356 if (pEvent)
357 {
358 VbglR3ClipboardEventFree(pEvent);
359 pEvent = NULL;
360 }
361 }
362
363 if (fShutdown)
364 break;
365 }
366
367 LogRel(("Worker loop ended\n"));
368
369 LogFlowFuncLeaveRC(rc);
370 return rc;
371}
372
373#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
374static DECLCALLBACK(int) vboxClipoardFUSEThread(RTTHREAD hThreadSelf, void *pvUser)
375{
376 RT_NOREF(hThreadSelf, pvUser);
377
378 VbglR3Init();
379
380 LogFlowFuncEnter();
381
382 RTThreadUserSignal(hThreadSelf);
383
384 SHCL_FUSE_OPTS Opts;
385 RT_ZERO(Opts);
386
387 Opts.fForeground = true;
388 Opts.fSingleThreaded = false; /** @todo Do we want multithread here? */
389
390 int rc = RTPathTemp(Opts.szMountPoint, sizeof(Opts.szMountPoint));
391 if (RT_SUCCESS(rc))
392 {
393 rc = RTPathAppend(Opts.szMountPoint, sizeof(Opts.szMountPoint), "VBoxSharedClipboard");
394 if (RT_SUCCESS(rc))
395 {
396 rc = RTDirCreate(Opts.szMountPoint, 0700,
397 RTDIRCREATE_FLAGS_NO_SYMLINKS);
398 if (rc == VERR_ALREADY_EXISTS)
399 rc = VINF_SUCCESS;
400 }
401 }
402
403 if (RT_SUCCESS(rc))
404 {
405 rc = ShClFuseMain(&Opts);
406 }
407 else
408 LogRel(("Error creating FUSE mount directory, rc=%Rrc\n", rc));
409
410 LogFlowFuncLeaveRC(rc);
411 return rc;
412}
413
414static int vboxClipboardFUSEStart()
415{
416 LogFlowFuncEnter();
417
418 PSHCLCONTEXT pCtx = &g_Ctx;
419
420 int rc = RTThreadCreate(&pCtx->FUSE.Thread, vboxClipoardFUSEThread, &pCtx->FUSE, 0,
421 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLFUSE");
422 if (RT_SUCCESS(rc))
423 rc = RTThreadUserWait(pCtx->FUSE.Thread, 30 * 1000);
424
425 LogFlowFuncLeaveRC(rc);
426 return rc;
427}
428
429static int vboxClipboardFUSEStop()
430{
431 LogFlowFuncEnter();
432
433 PSHCLCONTEXT pCtx = &g_Ctx;
434
435 int rcThread;
436 int rc = RTThreadWait(pCtx->FUSE.Thread, 1000, &rcThread);
437
438 LogFlowFuncLeaveRC(rc);
439 return rc;
440}
441#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
442
443static const char *getName()
444{
445 return "Shared Clipboard";
446}
447
448static const char *getPidFilePath()
449{
450 return ".vboxclient-clipboard.pid";
451}
452
453static int init(struct VBCLSERVICE **pSelf)
454{
455 RT_NOREF(pSelf);
456
457 int rc;
458
459#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
460 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
461#else
462 rc = VINF_SUCCESS;
463#endif
464
465 LogFlowFuncLeaveRC(rc);
466 return rc;
467}
468
469static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
470{
471 RT_NOREF(ppInterface, fDaemonised);
472
473 /* Initialise the guest library. */
474 int rc = vboxClipboardConnect();
475 if (RT_SUCCESS(rc))
476 {
477#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
478 rc = vboxClipboardFUSEStart();
479 if (RT_SUCCESS(rc))
480 {
481#endif
482 rc = vboxClipboardMain();
483
484#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
485 int rc2 = vboxClipboardFUSEStop();
486 if (RT_SUCCESS(rc))
487 rc = rc2;
488 }
489#endif
490 }
491
492 if (RT_FAILURE(rc))
493 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
494
495 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
496 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
497
498 return rc;
499}
500
501static void cleanup(struct VBCLSERVICE **ppInterface)
502{
503 RT_NOREF(ppInterface);
504
505#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
506 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
507#endif
508}
509
510struct VBCLSERVICE vbclClipboardInterface =
511{
512 getName,
513 getPidFilePath,
514 init,
515 run,
516 cleanup
517};
518
519struct CLIPBOARDSERVICE
520{
521 struct VBCLSERVICE *pInterface;
522};
523
524struct VBCLSERVICE **VBClGetClipboardService(void)
525{
526 struct CLIPBOARDSERVICE *pService =
527 (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService));
528
529 if (!pService)
530 VBClLogFatalError("Out of memory\n");
531 pService->pInterface = &vbclClipboardInterface;
532 return &pService->pInterface;
533}
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