VirtualBox

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

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

SharedClipboardSvc,Vbgl: Worked over the host messages, taking locks when needed, not using C++ lists and a bunch of other simplifications. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.4 KB
Line 
1/** $Id: clipboard.cpp 82527 2019-12-10 01:46:40Z 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/*********************************************************************************************************************************
48* Global Variables *
49*********************************************************************************************************************************/
50
51#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
52typedef struct _SHCLCTXFUSE
53{
54 RTTHREAD Thread;
55} SHCLCTXFUSE;
56#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
57
58/**
59 * Global clipboard context information.
60 */
61struct SHCLCONTEXT
62{
63 /** Client command context */
64 VBGLR3SHCLCMDCTX CmdCtx;
65#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
66 /** Associated transfer data. */
67 SHCLTRANSFERCTX TransferCtx;
68# ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
69 SHCLCTXFUSE FUSE;
70# endif
71#endif
72 /** X11 clipboard context. */
73 SHCLX11CTX X11;
74};
75
76/** Only one client is supported. There seems to be no need for more clients. */
77static SHCLCONTEXT g_Ctx;
78
79
80/**
81 * Get clipboard data from the host.
82 *
83 * @returns VBox result code
84 * @param pCtx Our context information.
85 * @param Format The format of the data being requested.
86 * @param ppv On success and if pcb > 0, this will point to a buffer
87 * to be freed with RTMemFree containing the data read.
88 * @param pcb On success, this contains the number of bytes of data
89 * 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 SHCLDATABLOCK dataBlock;
110 RT_ZERO(dataBlock);
111
112 dataBlock.uFormat = Format;
113 dataBlock.cbData = _4K;
114 dataBlock.pvData = RTMemAlloc(dataBlock.cbData);
115 if (dataBlock.pvData)
116 {
117 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, &dataBlock, &cbRead);
118 }
119 else
120 rc = VERR_NO_MEMORY;
121
122 /*
123 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
124 * larger buffer. The size of the buffer needed is placed in *pcb.
125 * So we start all over again.
126 */
127 if (rc == VINF_BUFFER_OVERFLOW)
128 {
129 /* cbRead contains the size required. */
130
131 dataBlock.cbData = cbRead;
132 dataBlock.pvData = RTMemRealloc(dataBlock.pvData, cbRead);
133 if (dataBlock.pvData)
134 {
135 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, &dataBlock, &cbRead);
136 if (rc == VINF_BUFFER_OVERFLOW)
137 rc = VERR_BUFFER_OVERFLOW;
138 }
139 else
140 rc = VERR_NO_MEMORY;
141 }
142
143 if (RT_SUCCESS(rc))
144 {
145 *pcb = cbRead; /* Actual bytes read. */
146 *ppv = dataBlock.pvData;
147 }
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 if (RT_FAILURE(rc))
156 RTMemFree(dataBlock.pvData);
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 rc The IPRT result code 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, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
203{
204 RT_NOREF(pCtx);
205
206 LogFlowFunc(("rc=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", rc, pReq->Format, pv, cb));
207
208 SHCLDATABLOCK dataBlock;
209 RT_ZERO(dataBlock);
210
211 dataBlock.uFormat = pReq->Format;
212
213 if (RT_SUCCESS(rc))
214 {
215 dataBlock.pvData = pv;
216 dataBlock.cbData = cb;
217 }
218
219 int rc2 = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, &dataBlock);
220 RT_NOREF(rc2);
221
222 RTMemFree(pReq);
223
224 LogFlowFuncLeaveRC(rc2);
225}
226
227/**
228 * Connect the guest clipboard to the host.
229 *
230 * @returns VBox status code.
231 */
232static int vboxClipboardConnect(void)
233{
234 LogFlowFuncEnter();
235
236 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
237 if (RT_SUCCESS(rc))
238 {
239 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
240 if (RT_SUCCESS(rc))
241 {
242 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
243 if (RT_FAILURE(rc))
244 ShClX11ThreadStop(&g_Ctx.X11);
245 }
246 }
247 else
248 rc = VERR_NO_MEMORY;
249
250 if (RT_FAILURE(rc))
251 {
252 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
253
254 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
255 ShClX11Destroy(&g_Ctx.X11);
256 }
257
258 LogFlowFuncLeaveRC(rc);
259 return rc;
260}
261
262/**
263 * The main loop of our clipboard reader.
264 */
265int vboxClipboardMain(void)
266{
267 LogRel(("Worker loop running\n"));
268
269 int rc;
270
271 PSHCLCONTEXT pCtx = &g_Ctx;
272
273 bool fShutdown = false;
274
275 /* The thread waits for incoming messages from the host. */
276 for (;;)
277 {
278 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
279 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
280
281 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
282 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
283
284 uint32_t idMsg = 0;
285 uint32_t cParms = 0;
286 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
287 if (RT_SUCCESS(rc))
288 {
289#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
290 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
291#else
292 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
293#endif
294 }
295
296 if (RT_FAILURE(rc))
297 {
298 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
299
300 VbglR3ClipboardEventFree(pEvent);
301 pEvent = NULL;
302
303 if (fShutdown)
304 break;
305
306 /* Wait a bit before retrying. */
307 RTThreadSleep(1000);
308 continue;
309 }
310 else
311 {
312 AssertPtr(pEvent);
313 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
314
315 switch (pEvent->enmType)
316 {
317 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
318 {
319 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.ReportedFormats.Formats);
320 break;
321 }
322
323 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
324 {
325 /* The host needs data in the specified format. */
326 CLIPREADCBREQ *pReq;
327 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
328 if (pReq)
329 {
330 pReq->Format = pEvent->u.fReadData;
331 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
332 }
333 else
334 rc = VERR_NO_MEMORY;
335 break;
336 }
337
338 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
339 {
340 LogRel2(("Host requested termination\n"));
341 fShutdown = true;
342 break;
343 }
344
345#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
346 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
347 {
348 /* Nothing to do here. */
349 rc = VINF_SUCCESS;
350 break;
351 }
352#endif
353 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
354 {
355 /* Nothing to do here. */
356 rc = VINF_SUCCESS;
357 break;
358 }
359
360 default:
361 {
362 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
363 }
364 }
365
366 if (pEvent)
367 {
368 VbglR3ClipboardEventFree(pEvent);
369 pEvent = NULL;
370 }
371 }
372
373 if (fShutdown)
374 break;
375 }
376
377 LogRel(("Worker loop ended\n"));
378
379 LogFlowFuncLeaveRC(rc);
380 return rc;
381}
382
383#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
384static DECLCALLBACK(int) vboxClipoardFUSEThread(RTTHREAD hThreadSelf, void *pvUser)
385{
386 RT_NOREF(hThreadSelf, pvUser);
387
388 VbglR3Init();
389
390 LogFlowFuncEnter();
391
392 RTThreadUserSignal(hThreadSelf);
393
394 char szExecPath[RTPATH_MAX];
395 RTProcGetExecutablePath(szExecPath, sizeof(szExecPath));
396
397 char szTempDir[RTPATH_MAX];
398 RTStrPrintf(szTempDir, sizeof(szTempDir), "VBoxSharedClipboard-XXXXXXXX");
399
400 int rc = RTDirCreateTemp(szTempDir, 0700);
401 if (RT_SUCCESS(rc))
402 {
403 char *paArgs[2];
404 paArgs[0] = szExecPath;
405 paArgs[1] = szTempDir;
406
407 rc = ShClFuseMain(2 /* argc */, paArgs);
408 }
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.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette