VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper-ipc.h@ 108787

Last change on this file since 108787 was 108787, checked in by vboxsync, 3 weeks ago

Additions: Linux/Wayland/VBoxClient: Add IPC workflow definitions for DnD, bugref:10796.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.5 KB
Line 
1/* $Id: wayland-helper-ipc.h 108787 2025-03-28 19:34:05Z vboxsync $ */
2/** @file
3 * Guest Additions - Definitions for IPC between VBoxClient and vboxwl tool.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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#ifndef GA_INCLUDED_SRC_x11_VBoxClient_wayland_helper_ipc_h
29#define GA_INCLUDED_SRC_x11_VBoxClient_wayland_helper_ipc_h
30#ifndef RT_WITHOUT_PRAGMA_ONCE
31# pragma once
32#endif
33
34#include <sys/types.h>
35#include <pwd.h>
36#include <unistd.h>
37
38#include <iprt/cdefs.h>
39#include <iprt/err.h>
40#include <iprt/linux/sysfs.h>
41#include <iprt/localipc.h>
42#include <iprt/mem.h>
43#include <iprt/crc.h>
44#include <iprt/env.h>
45#include <iprt/process.h>
46#include <iprt/asm.h>
47#include <iprt/time.h>
48#include <iprt/nocrt/new>
49
50#include <VBox/GuestHost/clipboard-helper.h>
51
52#include "VBoxClient.h"
53#include "wayland-helper.h"
54
55/** Path to Gtk helper tool which raises popup window and gets
56 * access to Wayland clipboard. */
57#ifndef VBOXWL_PATH
58# define VBOXWL_PATH "/usr/bin/vboxwl"
59#endif
60/** Limit maximum log verbosity level for popup tool. */
61#define VBOXWL_VERBOSITY_MAX (5)
62
63/** IPC server socket name prefixes. */
64#define VBOXWL_SRV_NAME_PREFIX_CLIP "clip"
65#define VBOXWL_SRV_NAME_PREFIX_DND "dnd"
66
67/** Arguments to vboxwl tool. */
68#define VBOXWL_ARG_CLIP_HG_COPY "--clip-hg-copy"
69#define VBOXWL_ARG_CLIP_GH_ANNOUNCE "--clip-gh-announce"
70#define VBOXWL_ARG_CLIP_GH_COPY "--clip-gh-copy"
71#define VBOXWL_ARG_DND_GH "--dnd-gh"
72#define VBOXWL_ARG_DND_HG "--dnd-hg"
73#define VBOXWL_ARG_SESSION_ID "--session-id"
74
75/** Time in milliseconds to wait for IPC socket events. */
76#define VBOX_GTKIPC_RX_TIMEOUT_MS (VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS)
77
78namespace vbcl
79{
80 namespace ipc
81 {
82 /** List of commands which VBoxClient and vboxwl use for IPC communication. */
83 typedef enum
84 {
85 /** Initializer. */
86 CMD_UNKNOWN = 0,
87 /** Send or receive list of clipboard formats which
88 * host or guest announces. */
89 VBOX_FORMATS,
90 /** Send or receive a clipboard format which host
91 * or guest requests. */
92 VBOX_FORMAT,
93 /** Send or receive clipboard data in given format. */
94 VBOX_DATA,
95 /** Termination of commands list. */
96 CMD_MAX
97 } command_t;
98
99 /** IPC command flow direction: from VBoxClient to vboxwl. */
100 const bool FLOW_DIRECTION_CLIENT = false;
101 /** IPC command flow direction: from vboxwl to VBoxClient. */
102 const bool FLOW_DIRECTION_SERVER = true;
103
104 /** IPC flow entry. */
105 typedef struct
106 {
107 /** Command ID. */
108 command_t enmCmd;
109 /** Flow direction. */
110 bool fDirection;
111 } flow_t;
112
113 /** IPC command header. */
114 typedef struct
115 {
116 /** IPC command packet checksum, includes header and payload. */
117 uint64_t u64Crc;
118 /** IPC session ID. Generated by VBoxClient instance and
119 * provided to vboxwl tool as a command line argument. Needs to be
120 * checked by both parties when data is received over IPC. */
121 uint32_t uSessionId;
122 /** IPC command identificator (opaque). */
123 command_t idCmd;
124 /** Size of payload data. */
125 uint64_t cbData;
126
127 } packet_t;
128
129 /**
130 * Log IPC packet content.
131 *
132 * @param pPacket Pointer to IPC packet data.
133 */
134 void packet_dump(vbcl::ipc::packet_t *pPacket);
135
136 /**
137 * Verify IPC packet integrity.
138 *
139 * @returns True if packet integrity check passed, False otherwise.
140 * @param pPacket Pointer to buffer which contains raw IPC packet data.
141 * @param cbPacket Size of buffer.
142 */
143 bool packet_verify(vbcl::ipc::packet_t *pPacket, size_t cbPacket);
144
145 /**
146 * Read entire packet from IPC socket.
147 *
148 * @returns IPRT status code. In case if success, output IPC packet
149 * is validated and its fields, such as packet size, can be trusted.
150 * @param uSessionId IPC session ID.
151 * @param hSession IPC session handle.
152 * @param mcTimeout Read operation timeout in milliseconds.
153 * @param ppvData Output buffer structured as validated
154 * IPC packet (contains size inside).
155 */
156 int packet_read(uint32_t uSessionId, RTLOCALIPCSESSION hSession, uint32_t msTimeout, void **ppvData);
157
158 /**
159 * Write entire IPC packet into IPC socket.
160 *
161 * @returns IPRT status code.
162 * @param hSession IPC session handle.
163 * @param pPacket IPC packet.
164 */
165 int packet_write(RTLOCALIPCSESSION hSession, vbcl::ipc::packet_t *pPacket);
166
167 namespace data
168 {
169 /** Payload for IPC commands VBOX_FORMATS and VBOX_FORMAT. */
170 typedef struct
171 {
172 /** IPC command header. */
173 vbcl::ipc::packet_t Hdr;
174 /** Clipboard formats bitmask. */
175 SHCLFORMATS fFormats;
176 } formats_packet_t;
177
178 /** Payload for IPC command VBOX_DATA. */
179 typedef struct
180 {
181 /* IPC command header. */
182 vbcl::ipc::packet_t Hdr;
183 /** Clipboard buffer format. */
184 SHCLFORMAT uFmt;
185 /** Size of clipboard buffer data. */
186 uint64_t cbData;
187 } data_packet_t;
188
189 /**
190 * IPC commands flow is described as a table. Each entry of
191 * such table contains command ID and flow direction. Both
192 * sides, VBoxClient and vboxwl, go through exactly the same
193 * command flow table sequentially and depending on a role
194 * (client or server) either send or wait for data and receive
195 * it. When last table entry is reached by both sides
196 * (simultaneously) it means that IPC session is completed.
197 * If error occurs on either side in the middle of the flow,
198 * IPC session is aborted.
199 */
200
201 /** IPC flow description (clipboard): Copy clipboard from host to guest. */
202 const flow_t HGCopyFlow[4] =
203 {
204 { VBOX_FORMATS, FLOW_DIRECTION_CLIENT },
205 { VBOX_FORMAT, FLOW_DIRECTION_SERVER },
206 { VBOX_DATA, FLOW_DIRECTION_CLIENT },
207 { CMD_MAX, false }
208 };
209
210 /** IPC flow description (clipboard): Copy clipboard from guest to host. */
211 const flow_t GHCopyFlow[3] =
212 {
213 { VBOX_FORMAT, FLOW_DIRECTION_CLIENT },
214 { VBOX_DATA, FLOW_DIRECTION_SERVER },
215 { CMD_MAX, false }
216 };
217
218 /** IPC flow description (clipboard): Announce guest's clipboard to the host
219 * and copy it to the host in format selected by host. */
220 const flow_t GHAnnounceAndCopyFlow[4] =
221 {
222 { VBOX_FORMATS, FLOW_DIRECTION_SERVER },
223 { VBOX_FORMAT, FLOW_DIRECTION_CLIENT },
224 { VBOX_DATA, FLOW_DIRECTION_SERVER },
225 { CMD_MAX, false }
226 };
227
228 /** IPC flow description (DnD): DnD operation started inside guest and
229 * guest reports DnD content mime-type list. Host side picks up one
230 * of the formats and requests data in this format. Guest sends
231 * data in requested format. */
232 const flow_t GHDragFlow[4] =
233 {
234 { VBOX_FORMATS, FLOW_DIRECTION_SERVER },
235 { VBOX_FORMAT, FLOW_DIRECTION_CLIENT },
236 { VBOX_DATA, FLOW_DIRECTION_SERVER },
237 { CMD_MAX, false }
238 };
239
240 /** IPC flow description (DnD): DnD operation started on host and
241 * host reports DnD content mime-type list. Guest side picks up one
242 * of the formats and requests data in this format. Host sends
243 * data in requested format. */
244 const flow_t HGDragFlow[4] =
245 {
246 { VBOX_FORMATS, FLOW_DIRECTION_CLIENT },
247 { VBOX_FORMAT, FLOW_DIRECTION_SERVER },
248 { VBOX_DATA, FLOW_DIRECTION_CLIENT },
249 { CMD_MAX, false }
250 };
251
252 class DataIpc
253 {
254 public:
255
256#ifdef RT_NEED_NEW_AND_DELETE
257 RTMEM_IMPLEMENT_NEW_AND_DELETE();
258#endif
259 DataIpc()
260 {}
261
262 /**
263 * Initialize memory.
264 *
265 * @param fServer Specify IPC role; if True, server role
266 * is assigned (set by VBoxClient),
267 * otherwise client role is assigned (vboxwl).
268 * @param uSessionId Unique IPC session ID (randomly generated
269 * by server).
270 */
271 void init(bool fServer, uint32_t uSessionId)
272 {
273 m_fFmts.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS);
274 m_uFmt.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS);
275 m_pvDataBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS);
276 m_cbDataBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS);
277 m_fServer = fServer;
278 m_uSessionId = uSessionId;
279 }
280
281 /**
282 * Reset IPC session data and free allocated resources.
283 */
284 void reset()
285 {
286 void *pvData = (void *)m_pvDataBuf.reset();
287 if (RT_VALID_PTR(pvData))
288 RTMemFree(pvData);
289
290 m_fFmts.reset();
291 m_uFmt.reset();
292 m_cbDataBuf.reset();
293 }
294
295 /**
296 * Process IPC flow from start to finish.
297 *
298 * @returns IPRT status code.
299 * @param pFlow Pointer to selected IPC flow.
300 * @param hIpcSession IPC connection handle.
301 */
302 int flow(const flow_t *pFlow, RTLOCALIPCSESSION hIpcSession)
303 {
304 int idx = 0;
305 int rc = VINF_SUCCESS;
306
307 while ( RT_SUCCESS(rc)
308 && pFlow[idx].enmCmd != CMD_MAX)
309 {
310 rc = select_fn(pFlow[idx].enmCmd, pFlow[idx].fDirection, hIpcSession);
311 idx++;
312 }
313
314 return rc;
315 }
316
317 /** IPC session internal data. */
318 Waitable<volatile SHCLFORMATS> m_fFmts;
319 Waitable<volatile SHCLFORMAT> m_uFmt;
320 Waitable<volatile uint64_t> m_pvDataBuf;
321 Waitable<volatile uint32_t> m_cbDataBuf;
322
323 protected:
324
325 /**
326 * Send available clipboard formats over IPC.
327 *
328 * @returns IPRT status code.
329 * @param uSessionId IPC session ID.
330 * @param hIpcSession IPC connection handle.
331 */
332 int send_formats(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
333
334 /**
335 * Receive available clipboard formats over IPC.
336 *
337 * @returns IPRT status code.
338 * @param uSessionId IPC session ID.
339 * @param hIpcSession IPC connection handle.
340 */
341 int recv_formats(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
342
343 /**
344 * Send requested clipboard format over IPC.
345 *
346 * @returns IPRT status code.
347 * @param uSessionId IPC session ID.
348 * @param hIpcSession IPC connection handle.
349 */
350 int send_format(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
351
352 /**
353 * Receive requested clipboard format over IPC.
354 *
355 * @returns IPRT status code.
356 * @param uSessionId IPC session ID.
357 * @param hIpcSession IPC connection handle.
358 */
359 int recv_format(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
360
361 /**
362 * Send clipboard buffer over IPC.
363 *
364 * @returns IPRT status code.
365 * @param uSessionId IPC session ID.
366 * @param hIpcSession IPC connection handle.
367 */
368 int send_data(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
369
370 /**
371 * Receive clipboard buffer over IPC.
372 *
373 * @returns IPRT status code.
374 * @param uSessionId IPC session ID.
375 * @param hIpcSession IPC connection handle.
376 */
377 int recv_data(uint32_t uSessionId, RTLOCALIPCSESSION hIpcSession);
378
379 /**
380 * Take one step flow action.
381 *
382 * Taking into account given command and IPC role, either
383 * send or receive data from IPC socket.
384 *
385 * @returns IPRT status code.
386 * @param enmCmd IPC command ID.
387 * @param fDirection IPC role.
388 * @param hIpcSession IPC connection handle.
389 */
390 int select_fn(command_t enmCmd, bool fDirection, RTLOCALIPCSESSION hIpcSession)
391 {
392 int rc = VERR_INVALID_PARAMETER;
393 bool fShouldSend;
394
395 if (m_fServer)
396 fShouldSend = (fDirection == FLOW_DIRECTION_CLIENT);
397 else
398 fShouldSend = (fDirection == FLOW_DIRECTION_SERVER);
399
400 switch(enmCmd)
401 {
402 case VBOX_FORMATS:
403 {
404 if (fShouldSend)
405 rc = send_formats(m_uSessionId, hIpcSession);
406 else
407 rc = recv_formats(m_uSessionId, hIpcSession);
408 break;
409 }
410
411 case VBOX_FORMAT:
412 {
413 if (fShouldSend)
414 rc = send_format(m_uSessionId, hIpcSession);
415 else
416 rc = recv_format(m_uSessionId, hIpcSession);
417 break;
418 }
419
420 case VBOX_DATA:
421 {
422 if (fShouldSend)
423 rc = send_data(m_uSessionId, hIpcSession);
424 else
425 rc = recv_data(m_uSessionId, hIpcSession);
426 break;
427 }
428
429 default:
430 break;
431 }
432
433 return rc;
434 }
435
436 bool m_fServer;
437 uint32_t m_uSessionId;
438 };
439 }
440 }
441}
442
443/**
444 * Helper function to get Gtk helper IPC server name.
445 *
446 * This function should be used by both IPC server and client code
447 * in order to connect one to another. Output string will be in
448 * format: GtkHlpIpcServer-&lt;prefix&gt;--&lt;active tty&gt;-&lt;user name&gt;.
449 *
450 * @returns IPRT status code.
451 * @param szNamePrefix Name prefix.
452 * @param szBuf Where to store generated name string.
453 * @param cbBuf Size of buffer.
454 */
455RTDECL(int) vbcl_wayland_hlp_gtk_ipc_srv_name(const char *szNamePrefix, char *szBuf, size_t cbBuf);
456
457#endif /* !GA_INCLUDED_SRC_x11_VBoxClient_wayland_helper_ipc_h */
458
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