The open source OpenXR runtime
1// Copyright 2020-2023, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief IPC message channel functions for UNIX platforms.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Pete Black <pblack@collabora.com>
8 * @author Jakob Bornecrantz <jakob@collabora.com>
9 * @ingroup ipc_shared
10 */
11
12#include "xrt/xrt_config_os.h"
13
14#ifdef XRT_OS_WINDOWS
15#error "This file shouldn't be compiled on Windows!"
16#endif
17
18#include "util/u_logging.h"
19#include "util/u_pretty_print.h"
20
21#include "shared/ipc_protocol.h"
22#include "shared/ipc_message_channel.h"
23
24#include <errno.h>
25#include <sys/socket.h>
26#include <unistd.h>
27#include <stdio.h>
28#include <string.h>
29#include <stdint.h>
30#include <assert.h>
31
32
33/*
34 *
35 * Logging
36 *
37 */
38
39#define IPC_TRACE(d, ...) U_LOG_IFL_T(d->log_level, __VA_ARGS__)
40#define IPC_DEBUG(d, ...) U_LOG_IFL_D(d->log_level, __VA_ARGS__)
41#define IPC_INFO(d, ...) U_LOG_IFL_I(d->log_level, __VA_ARGS__)
42#define IPC_WARN(d, ...) U_LOG_IFL_W(d->log_level, __VA_ARGS__)
43#define IPC_ERROR(d, ...) U_LOG_IFL_E(d->log_level, __VA_ARGS__)
44
45
46/*
47 *
48 * Structs and defines.
49 *
50 */
51
52union imcontrol_buf {
53 uint8_t buf[512];
54 struct cmsghdr align;
55};
56
57
58/*
59 *
60 * 'Exported' functions.
61 *
62 */
63
64void
65ipc_message_channel_close(struct ipc_message_channel *imc)
66{
67 if (imc->ipc_handle < 0) {
68 return;
69 }
70 close(imc->ipc_handle);
71 imc->ipc_handle = -1;
72}
73
74xrt_result_t
75ipc_send(struct ipc_message_channel *imc, const void *data, size_t size)
76{
77 struct msghdr msg = {0};
78 struct iovec iov = {0};
79
80 iov.iov_base = (void *)data;
81 iov.iov_len = size;
82
83 msg.msg_name = NULL;
84 msg.msg_namelen = 0;
85 msg.msg_iov = &iov;
86 msg.msg_iovlen = 1;
87 msg.msg_flags = 0;
88
89 ssize_t ret = sendmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
90 if (ret < 0) {
91 int code = errno;
92 IPC_ERROR(imc, "sendmsg(%i) failed: '%i' '%s'!", imc->ipc_handle, code, strerror(code));
93 return XRT_ERROR_IPC_FAILURE;
94 }
95
96 return XRT_SUCCESS;
97}
98
99xrt_result_t
100ipc_receive(struct ipc_message_channel *imc, void *out_data, size_t size)
101{
102 // wait for the response
103 struct iovec iov = {0};
104 struct msghdr msg = {0};
105
106 iov.iov_base = out_data;
107 iov.iov_len = size;
108
109 msg.msg_name = 0;
110 msg.msg_namelen = 0;
111 msg.msg_iov = &iov;
112 msg.msg_iovlen = 1;
113 msg.msg_flags = 0;
114
115 ssize_t len = recvmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
116
117 if (len < 0) {
118 int code = errno;
119 IPC_ERROR(imc, "recvmsg(%i) failed: '%i' '%s'!", (int)imc->ipc_handle, code, strerror(code));
120 return XRT_ERROR_IPC_FAILURE;
121 }
122
123 if ((size_t)len != size) {
124 IPC_ERROR(imc, "recvmsg(%i) failed: wrong size '%i', expected '%i'!", (int)imc->ipc_handle, (int)len,
125 (int)size);
126 return XRT_ERROR_IPC_FAILURE;
127 }
128
129 return XRT_SUCCESS;
130}
131
132xrt_result_t
133ipc_receive_fds(struct ipc_message_channel *imc, void *out_data, size_t size, int *out_handles, uint32_t handle_count)
134{
135 assert(imc != NULL);
136 assert(out_data != NULL);
137 assert(size != 0);
138 assert(out_handles != NULL);
139 assert(handle_count != 0);
140
141 union imcontrol_buf u;
142 const size_t fds_size = sizeof(int) * handle_count;
143 const size_t cmsg_size = CMSG_SPACE(fds_size);
144 memset(u.buf, 0, cmsg_size);
145
146 struct iovec iov = {0};
147 iov.iov_base = out_data;
148 iov.iov_len = size;
149
150 struct msghdr msg = {0};
151 msg.msg_iov = &iov;
152 msg.msg_iovlen = 1;
153 msg.msg_control = u.buf;
154 msg.msg_controllen = cmsg_size;
155
156 ssize_t len = recvmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
157 if (len < 0) {
158 IPC_ERROR(imc, "recvmsg(%i) failed: '%s'!", imc->ipc_handle, strerror(errno));
159 return XRT_ERROR_IPC_FAILURE;
160 }
161
162 if (len == 0) {
163 IPC_ERROR(imc, "recvmsg(%i) failed: no data!", imc->ipc_handle);
164 return XRT_ERROR_IPC_FAILURE;
165 }
166
167 // Did the other side actually send file descriptors.
168 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
169 if (cmsg == NULL) {
170 return XRT_SUCCESS;
171 }
172
173 memcpy(out_handles, (int *)CMSG_DATA(cmsg), fds_size);
174
175 return XRT_SUCCESS;
176}
177
178xrt_result_t
179ipc_send_fds(struct ipc_message_channel *imc, const void *data, size_t size, const int *handles, uint32_t handle_count)
180{
181 assert(imc != NULL);
182 assert(data != NULL);
183 assert(size != 0);
184 assert(handles != NULL);
185
186 const size_t fds_size = sizeof(int) * handle_count;
187
188 union imcontrol_buf u = {0};
189 size_t cmsg_size = CMSG_SPACE(fds_size);
190
191 struct iovec iov = {0};
192 iov.iov_base = (void *)data;
193 iov.iov_len = size;
194
195 struct msghdr msg = {0};
196 msg.msg_name = NULL;
197 msg.msg_namelen = 0;
198 msg.msg_iov = &iov;
199 msg.msg_iovlen = 1;
200 msg.msg_flags = 0;
201 msg.msg_control = u.buf;
202 msg.msg_controllen = cmsg_size;
203
204 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
205 cmsg->cmsg_level = SOL_SOCKET;
206 cmsg->cmsg_type = SCM_RIGHTS;
207 cmsg->cmsg_len = CMSG_LEN(fds_size);
208
209 memcpy(CMSG_DATA(cmsg), handles, fds_size);
210
211 ssize_t ret = sendmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
212 if (ret >= 0) {
213 return XRT_SUCCESS;
214 }
215
216 /*
217 * Error path.
218 */
219
220 struct u_pp_sink_stack_only sink;
221 u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
222
223 u_pp(dg, "sendmsg(%i) failed: count: %u, error: '%i' '%s'!", imc->ipc_handle, handle_count, errno,
224 strerror(errno));
225
226 for (uint32_t i = 0; i < handle_count; i++) {
227 u_pp(dg, "\n\tfd #%i: %i", i, handles[i]);
228 }
229
230 IPC_ERROR(imc, "%s", sink.buffer);
231
232 return XRT_ERROR_IPC_FAILURE;
233}
234
235xrt_result_t
236ipc_receive_handles_shmem(struct ipc_message_channel *imc,
237 void *out_data,
238 size_t size,
239 xrt_shmem_handle_t *out_handles,
240 uint32_t handle_count)
241{
242 return ipc_receive_fds(imc, out_data, size, out_handles, handle_count);
243}
244
245xrt_result_t
246ipc_send_handles_shmem(struct ipc_message_channel *imc,
247 const void *data,
248 size_t size,
249 const xrt_shmem_handle_t *handles,
250 uint32_t handle_count)
251{
252 return ipc_send_fds(imc, data, size, handles, handle_count);
253}
254
255
256/*
257 *
258 * AHardwareBuffer graphics buffer functions.
259 *
260 */
261
262#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER)
263
264#include <android/hardware_buffer.h>
265
266
267xrt_result_t
268ipc_receive_handles_graphics_buffer(struct ipc_message_channel *imc,
269 void *out_data,
270 size_t size,
271 xrt_graphics_buffer_handle_t *out_handles,
272 uint32_t handle_count)
273{
274 xrt_result_t result = ipc_receive(imc, out_data, size);
275 if (result != XRT_SUCCESS) {
276 return result;
277 }
278 bool failed = false;
279 for (uint32_t i = 0; i < handle_count; ++i) {
280 int err = AHardwareBuffer_recvHandleFromUnixSocket(imc->ipc_handle, &(out_handles[i]));
281 if (err != 0) {
282 failed = true;
283 }
284 }
285 return failed ? XRT_ERROR_IPC_FAILURE : XRT_SUCCESS;
286}
287
288
289xrt_result_t
290ipc_send_handles_graphics_buffer(struct ipc_message_channel *imc,
291 const void *data,
292 size_t size,
293 const xrt_graphics_buffer_handle_t *handles,
294 uint32_t handle_count)
295{
296 xrt_result_t result = ipc_send(imc, data, size);
297 if (result != XRT_SUCCESS) {
298 return result;
299 }
300 bool failed = false;
301 for (uint32_t i = 0; i < handle_count; ++i) {
302 int err = AHardwareBuffer_sendHandleToUnixSocket(handles[i], imc->ipc_handle);
303 if (err != 0) {
304 failed = true;
305 }
306 }
307 return failed ? XRT_ERROR_IPC_FAILURE : XRT_SUCCESS;
308}
309
310
311/*
312 *
313 * FD graphics buffer functions.
314 *
315 */
316
317#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_FD)
318
319xrt_result_t
320ipc_receive_handles_graphics_buffer(struct ipc_message_channel *imc,
321 void *out_data,
322 size_t size,
323 xrt_graphics_buffer_handle_t *out_handles,
324 uint32_t handle_count)
325{
326 return ipc_receive_fds(imc, out_data, size, out_handles, handle_count);
327}
328
329xrt_result_t
330ipc_send_handles_graphics_buffer(struct ipc_message_channel *imc,
331 const void *data,
332 size_t size,
333 const xrt_graphics_buffer_handle_t *handles,
334 uint32_t handle_count)
335{
336 return ipc_send_fds(imc, data, size, handles, handle_count);
337}
338
339#else
340#error "Need port to transport these graphics buffers"
341#endif
342
343
344/*
345 *
346 * FD graphics sync functions.
347 *
348 */
349
350#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD)
351
352xrt_result_t
353ipc_receive_handles_graphics_sync(struct ipc_message_channel *imc,
354 void *out_data,
355 size_t size,
356 xrt_graphics_sync_handle_t *out_handles,
357 uint32_t handle_count)
358{
359 //! @todo Temporary hack to send no handles.
360 if (handle_count == 0) {
361 return ipc_receive(imc, out_data, size);
362 }
363 return ipc_receive_fds(imc, out_data, size, out_handles, handle_count);
364}
365
366xrt_result_t
367ipc_send_handles_graphics_sync(struct ipc_message_channel *imc,
368 const void *data,
369 size_t size,
370 const xrt_graphics_sync_handle_t *handles,
371 uint32_t handle_count)
372{
373 //! @todo Temporary hack to send no handles.
374 if (handle_count == 0) {
375 return ipc_send(imc, data, size);
376 }
377 return ipc_send_fds(imc, data, size, handles, handle_count);
378}
379
380#else
381#error "Need port to transport these graphics buffers"
382#endif