The open source OpenXR runtime
at main 382 lines 9.4 kB view raw
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