VirtualBox

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

Last change on this file since 98474 was 98474, checked in by vboxsync, 22 months ago

Additions: X11: Add possibility restart VBoxClient processes during Guest Additions update, bugref:10359.

This commit makes VBoxClient processes to temporary release reference to vboxguest kernel module and then
restart itself. So module can be reloaded and newly started VBoxClient instance will utilize updated module.

When VBoxClient starts in demonized mode, it forks a child process which represents actual service. Parent
process continues to run and its main function is to restart child when it crashes or terminates with exit
status != 0. This commit makes child process to catch SIGUSR1 and terminate with exit
status VBGLR3EXITCODERELOAD (currently equal to 2). Parent process will detect this and in turn will release
its reference to vboxguest kernel module, allowing to reload it. The parent process will then wait for SIGUSR1
itself. Once received, it will restart itself (loading new, updated VBoxClient process image). This is a part
of the procedure to install and reload/restart Guest Additions kernel modules and user services without
requiring guest reboot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.8 KB
Line 
1/** $Id: clipboard.cpp 98474 2023-02-03 19:20:53Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/alloc.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
36# include <iprt/dir.h>
37#endif
38#include <iprt/initterm.h>
39#include <iprt/mem.h>
40#include <iprt/string.h>
41#include <iprt/path.h>
42#include <iprt/process.h>
43#include <iprt/semaphore.h>
44
45#include <VBox/log.h>
46#include <VBox/VBoxGuestLib.h>
47#include <VBox/HostServices/VBoxClipboardSvc.h>
48#include <VBox/GuestHost/SharedClipboard.h>
49#include <VBox/GuestHost/SharedClipboard-x11.h>
50
51#include "VBoxClient.h"
52
53#include "clipboard.h"
54#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
55# include "clipboard-fuse.h"
56#endif
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62
63/** Only one context is supported at a time for now. */
64SHCLCONTEXT g_Ctx;
65#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
66SHCLFUSECTX g_FuseCtx;
67#endif
68
69
70static DECLCALLBACK(int) vbclOnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
71 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
72{
73 RT_NOREF(pvUser);
74
75 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
76
77 int rc = VINF_SUCCESS;
78
79#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
80 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
81 {
82 //rc = VbglR3ClipboardRootListRead()
83 rc = VERR_NO_DATA;
84 }
85 else
86#endif
87 {
88 uint32_t cbRead = 0;
89
90 uint32_t cbData = _4K; /** @todo Make this dynamic. */
91 void *pvData = RTMemAlloc(cbData);
92 if (pvData)
93 {
94 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
95 }
96 else
97 rc = VERR_NO_MEMORY;
98
99 /*
100 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
101 * larger buffer. The size of the buffer needed is placed in *pcb.
102 * So we start all over again.
103 */
104 if (rc == VINF_BUFFER_OVERFLOW)
105 {
106 /* cbRead contains the size required. */
107
108 cbData = cbRead;
109 pvData = RTMemRealloc(pvData, cbRead);
110 if (pvData)
111 {
112 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
113 if (rc == VINF_BUFFER_OVERFLOW)
114 rc = VERR_BUFFER_OVERFLOW;
115 }
116 else
117 rc = VERR_NO_MEMORY;
118 }
119
120 if (!cbRead)
121 rc = VERR_NO_DATA;
122
123 if (RT_SUCCESS(rc))
124 {
125 *pcb = cbRead; /* Actual bytes read. */
126 *ppv = pvData;
127 }
128 else
129 {
130 /*
131 * Catch other errors. This also catches the case in which the buffer was
132 * too small a second time, possibly because the clipboard contents
133 * changed half-way through the operation. Since we can't say whether or
134 * not this is actually an error, we just return size 0.
135 */
136 RTMemFree(pvData);
137 }
138 }
139
140 if (RT_FAILURE(rc))
141 LogRel(("Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147/**
148 * Opaque data structure describing a request from the host for clipboard
149 * data, passed in when the request is forwarded to the X11 backend so that
150 * it can be completed correctly.
151 */
152struct CLIPREADCBREQ
153{
154 /** The data format that was requested. */
155 SHCLFORMAT uFmt;
156};
157
158static DECLCALLBACK(int) vbclReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
159{
160 RT_NOREF(pvUser);
161
162 LogFlowFunc(("fFormats=%#x\n", fFormats));
163
164 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
165 LogFlowFuncLeaveRC(rc);
166
167 return rc;
168}
169
170static DECLCALLBACK(int) vbclOnSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
171{
172 PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
173 AssertPtrReturn(pData, VERR_INVALID_POINTER);
174
175 LogFlowFunc(("rcCompletion=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", pData->rcCompletion, pData->pReq->uFmt, pv, cb));
176
177 Assert((cb == 0 && pv == NULL) || (cb != 0 && pv != NULL));
178 pData->rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pData->pReq->uFmt, pv, cb);
179
180 RTMemFree(pData->pReq);
181
182 LogFlowFuncLeaveRC(pData->rcCompletion);
183
184 return VINF_SUCCESS;
185}
186
187/**
188 * Connect the guest clipboard to the host.
189 *
190 * @returns VBox status code.
191 */
192static int vboxClipboardConnect(void)
193{
194 LogFlowFuncEnter();
195
196 SHCLCALLBACKS Callbacks;
197 RT_ZERO(Callbacks);
198 Callbacks.pfnReportFormats = vbclReportFormatsCallback;
199 Callbacks.pfnOnRequestDataFromSource = vbclOnRequestDataFromSourceCallback;
200 Callbacks.pfnOnSendDataToDest = vbclOnSendDataToDestCallback;
201
202 int rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
203 if (RT_SUCCESS(rc))
204 {
205 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
206 if (RT_SUCCESS(rc))
207 {
208 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
209 if (RT_FAILURE(rc))
210 ShClX11ThreadStop(&g_Ctx.X11);
211 }
212 }
213 else
214 rc = VERR_NO_MEMORY;
215
216 if (RT_FAILURE(rc))
217 {
218 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
219
220 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
221 ShClX11Destroy(&g_Ctx.X11);
222 }
223
224 LogFlowFuncLeaveRC(rc);
225 return rc;
226}
227
228/**
229 * The main loop of our clipboard reader.
230 */
231int vboxClipboardMain(void)
232{
233 int rc;
234
235 PSHCLCONTEXT pCtx = &g_Ctx;
236
237 bool fShutdown = false;
238
239 /* The thread waits for incoming messages from the host. */
240 for (;;)
241 {
242 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
243 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
244
245 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
246 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
247
248 uint32_t idMsg = 0;
249 uint32_t cParms = 0;
250 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
251 if (RT_SUCCESS(rc))
252 {
253#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
254 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
255#else
256 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
257#endif
258 }
259
260 if (RT_FAILURE(rc))
261 {
262 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
263
264 VbglR3ClipboardEventFree(pEvent);
265 pEvent = NULL;
266
267 if (fShutdown)
268 break;
269
270 /* Wait a bit before retrying. */
271 RTThreadSleep(1000);
272 continue;
273 }
274 else
275 {
276 AssertPtr(pEvent);
277 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
278
279 switch (pEvent->enmType)
280 {
281 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
282 {
283 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
284 break;
285 }
286
287 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
288 {
289 /* The host needs data in the specified format. */
290 CLIPREADCBREQ *pReq;
291 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
292 if (pReq)
293 {
294 pReq->uFmt = pEvent->u.fReadData;
295 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->uFmt, pReq);
296 }
297 else
298 rc = VERR_NO_MEMORY;
299 break;
300 }
301
302 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
303 {
304 VBClLogVerbose(2, "Host requested termination\n");
305 fShutdown = true;
306 break;
307 }
308
309#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
310 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
311 {
312 /* Nothing to do here. */
313 rc = VINF_SUCCESS;
314 break;
315 }
316#endif
317 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
318 {
319 /* Nothing to do here. */
320 rc = VINF_SUCCESS;
321 break;
322 }
323
324 default:
325 {
326 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
327 }
328 }
329
330 if (pEvent)
331 {
332 VbglR3ClipboardEventFree(pEvent);
333 pEvent = NULL;
334 }
335 }
336
337 if (fShutdown)
338 break;
339 }
340
341 LogFlowFuncLeaveRC(rc);
342 return rc;
343}
344
345/**
346 * @interface_method_impl{VBCLSERVICE,pfnInit}
347 */
348static DECLCALLBACK(int) vbclShClInit(void)
349{
350 int rc;
351
352#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
353 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
354#else
355 rc = VINF_SUCCESS;
356#endif
357
358 LogFlowFuncLeaveRC(rc);
359 return rc;
360}
361
362/**
363 * @interface_method_impl{VBCLSERVICE,pfnWorker}
364 */
365static DECLCALLBACK(int) vbclShClWorker(bool volatile *pfShutdown)
366{
367 RT_NOREF(pfShutdown);
368
369 /* Initialise the guest library. */
370 int rc = vboxClipboardConnect();
371 if (RT_SUCCESS(rc))
372 {
373#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
374 rc = VbClShClFUSEInit(&g_FuseCtx, &g_Ctx);
375 if (RT_SUCCESS(rc))
376 {
377 rc = VbClShClFUSEStart(&g_FuseCtx);
378 if (RT_SUCCESS(rc))
379 {
380#endif
381 /* Let the main thread know that it can continue spawning services. */
382 RTThreadUserSignal(RTThreadSelf());
383
384 rc = vboxClipboardMain();
385
386#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
387 int rc2 = VbClShClFUSEStop(&g_FuseCtx);
388 if (RT_SUCCESS(rc))
389 rc = rc2;
390 }
391 }
392#endif
393 }
394
395 if (RT_FAILURE(rc))
396 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
397
398 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
399 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
400
401 return rc;
402}
403
404/**
405 * @interface_method_impl{VBCLSERVICE,pfnStop}
406 */
407static DECLCALLBACK(void) vbclShClStop(void)
408{
409 /* Disconnect from the host service.
410 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
411 VbglR3ClipboardDisconnect(g_Ctx.CmdCtx.idClient);
412 g_Ctx.CmdCtx.idClient = 0;
413}
414
415/**
416 * @interface_method_impl{VBCLSERVICE,pfnTerm}
417 */
418static DECLCALLBACK(int) vbclShClTerm(void)
419{
420#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
421 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
422#endif
423
424 return VINF_SUCCESS;
425}
426
427VBCLSERVICE g_SvcClipboard =
428{
429 "shcl", /* szName */
430 "Shared Clipboard", /* pszDescription */
431 ".vboxclient-clipboard", /* pszPidFilePathTemplate */
432 NULL, /* pszUsage */
433 NULL, /* pszOptions */
434 NULL, /* pfnOption */
435 vbclShClInit, /* pfnInit */
436 vbclShClWorker, /* pfnWorker */
437 vbclShClStop, /* pfnStop*/
438 vbclShClTerm /* pfnTerm */
439};
440
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