The open source OpenXR runtime
at main 299 lines 6.3 kB view raw
1// Copyright 2020-2021, Collabora, Ltd. 2// Copyright 2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Server mainloop details on Linux. 7 * @author Pete Black <pblack@collabora.com> 8 * @author Jakob Bornecrantz <jakob@collabora.com> 9 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 10 * @ingroup ipc_server 11 */ 12 13#include "xrt/xrt_device.h" 14#include "xrt/xrt_instance.h" 15#include "xrt/xrt_compositor.h" 16#include "xrt/xrt_config_have.h" 17#include "xrt/xrt_config_os.h" 18 19#include "os/os_time.h" 20#include "util/u_var.h" 21#include "util/u_misc.h" 22#include "util/u_debug.h" 23#include "util/u_trace_marker.h" 24#include "util/u_file.h" 25 26#include "shared/ipc_shmem.h" 27#include "server/ipc_server.h" 28 29#include <stdlib.h> 30#include <unistd.h> 31#include <stdbool.h> 32#include <sys/types.h> 33#include <sys/stat.h> 34#include <sys/mman.h> 35#include <sys/socket.h> 36#include <sys/un.h> 37#include <sys/epoll.h> 38#include <fcntl.h> 39#include <errno.h> 40#include <stdio.h> 41#include <string.h> 42#include <assert.h> 43#include <limits.h> 44#include "util/u_debug.h" 45 46#ifdef XRT_HAVE_SYSTEMD 47#include <systemd/sd-daemon.h> 48#endif 49 50 51/* 52 * 53 * Static functions. 54 * 55 */ 56static int 57get_systemd_socket(struct ipc_server_mainloop *ml, int *out_fd) 58{ 59#ifdef XRT_HAVE_SYSTEMD 60 // We may have been launched with socket activation 61 int num_fds = sd_listen_fds(0); 62 if (num_fds > 1) { 63 U_LOG_E("Too many file descriptors passed by systemd."); 64 return -1; 65 } 66 if (num_fds == 1) { 67 *out_fd = SD_LISTEN_FDS_START + 0; 68 ml->launched_by_socket = true; 69 U_LOG_D("Got existing socket from systemd."); 70 } 71#endif 72 return 0; 73} 74 75static int 76create_listen_socket(struct ipc_server_mainloop *ml, int *out_fd) 77{ 78 // no fd provided 79 struct sockaddr_un addr; 80 int fd; 81 int ret; 82 83 fd = socket(PF_UNIX, SOCK_STREAM, 0); 84 if (fd < 0) { 85 U_LOG_E("Message Socket Create Error!"); 86 return fd; 87 } 88 89 90 char sock_file[PATH_MAX]; 91 92 int size = u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, sock_file, PATH_MAX); 93 if (size == -1) { 94 U_LOG_E("Could not get socket file name"); 95 return -1; 96 } 97 98 memset(&addr, 0, sizeof(addr)); 99 100 addr.sun_family = AF_UNIX; 101 strcpy(addr.sun_path, sock_file); 102 103 ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 104 105#ifdef XRT_HAVE_LIBBSD 106 // no other instance is running, or we would have never arrived here 107 if (ret < 0 && errno == EADDRINUSE) { 108 U_LOG_W("Removing stale socket file %s", sock_file); 109 110 ret = unlink(sock_file); 111 if (ret < 0) { 112 U_LOG_E("Failed to remove stale socket file %s: %s", sock_file, strerror(errno)); 113 return ret; 114 } 115 ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 116 } 117#endif 118 119 if (ret < 0) { 120 U_LOG_E("Could not bind socket to path %s: %s. Is the service running already?", sock_file, 121 strerror(errno)); 122#ifdef XRT_HAVE_SYSTEMD 123 U_LOG_E("Or, is the systemd unit monado.socket or monado-dev.socket active?"); 124#endif 125 if (errno == EADDRINUSE) { 126 U_LOG_E("If monado-service is not running, delete %s before starting a new instance", 127 sock_file); 128 } 129 close(fd); 130 return ret; 131 } 132 // Save for later 133 ml->socket_filename = strdup(sock_file); 134 135 ret = listen(fd, IPC_MAX_CLIENTS); 136 if (ret < 0) { 137 close(fd); 138 return ret; 139 } 140 U_LOG_D("Created listening socket %s.", sock_file); 141 *out_fd = fd; 142 return 0; 143} 144 145static int 146init_listen_socket(struct ipc_server_mainloop *ml) 147{ 148 int fd = -1; 149 int ret; 150 ml->listen_socket = -1; 151 152 ret = get_systemd_socket(ml, &fd); 153 if (ret < 0) { 154 return ret; 155 } 156 157 if (fd == -1) { 158 ret = create_listen_socket(ml, &fd); 159 if (ret < 0) { 160 return ret; 161 } 162 } 163 // All ok! 164 ml->listen_socket = fd; 165 U_LOG_D("Listening socket is fd %d", ml->listen_socket); 166 167 return fd; 168} 169 170static int 171init_epoll(struct ipc_server_mainloop *ml, bool no_stdin) 172{ 173 int ret = epoll_create1(EPOLL_CLOEXEC); 174 if (ret < 0) { 175 return ret; 176 } 177 178 ml->epoll_fd = ret; 179 180 struct epoll_event ev = {0}; 181 182 if (!ml->launched_by_socket && !no_stdin) { 183 // Can't do this when launched by systemd socket activation by 184 // default. 185 // This polls stdin. 186 ev.events = EPOLLIN; 187 ev.data.fd = 0; // stdin 188 ret = epoll_ctl(ml->epoll_fd, EPOLL_CTL_ADD, 0, &ev); 189 if (ret < 0) { 190 U_LOG_E("epoll_ctl(stdin) failed '%i'", ret); 191 return ret; 192 } 193 } 194 195 ev.events = EPOLLIN; 196 ev.data.fd = ml->listen_socket; 197 ret = epoll_ctl(ml->epoll_fd, EPOLL_CTL_ADD, ml->listen_socket, &ev); 198 if (ret < 0) { 199 U_LOG_E("epoll_ctl(listen_socket) failed '%i'", ret); 200 return ret; 201 } 202 203 return 0; 204} 205 206static void 207handle_listen(struct ipc_server *vs, struct ipc_server_mainloop *ml) 208{ 209 int ret = accept(ml->listen_socket, NULL, NULL); 210 if (ret < 0) { 211 U_LOG_E("accept '%i'", ret); 212 ipc_server_handle_failure(vs); 213 return; 214 } 215 216 // Call into the generic client connected handling code. 217 ipc_server_handle_client_connected(vs, ret); 218} 219 220#define NUM_POLL_EVENTS 8 221#define NO_SLEEP 0 222 223/* 224 * 225 * Exported functions 226 * 227 */ 228 229void 230ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml) 231{ 232 IPC_TRACE_MARKER(); 233 234 int epoll_fd = ml->epoll_fd; 235 236 struct epoll_event events[NUM_POLL_EVENTS] = {0}; 237 238 // No sleeping, returns immediately. 239 int ret = epoll_wait(epoll_fd, events, NUM_POLL_EVENTS, NO_SLEEP); 240 if (ret < 0) { 241 U_LOG_E("epoll_wait failed with '%i'.", ret); 242 ipc_server_handle_failure(vs); 243 return; 244 } 245 246 for (int i = 0; i < ret; i++) { 247 // If we get data on stdin, stop. 248 if (events[i].data.fd == 0) { 249 ipc_server_handle_shutdown_signal(vs); 250 return; 251 } 252 253 // Somebody new at the door. 254 if (events[i].data.fd == ml->listen_socket) { 255 handle_listen(vs, ml); 256 } 257 } 258} 259 260int 261ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin) 262{ 263 IPC_TRACE_MARKER(); 264 265 int ret = init_listen_socket(ml); 266 if (ret < 0) { 267 ipc_server_mainloop_deinit(ml); 268 return ret; 269 } 270 271 ret = init_epoll(ml, no_stdin); 272 if (ret < 0) { 273 ipc_server_mainloop_deinit(ml); 274 return ret; 275 } 276 return 0; 277} 278 279void 280ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml) 281{ 282 IPC_TRACE_MARKER(); 283 284 if (ml == NULL) { 285 return; 286 } 287 if (ml->listen_socket > 0) { 288 // Close socket on exit 289 close(ml->listen_socket); 290 ml->listen_socket = -1; 291 if (!ml->launched_by_socket && ml->socket_filename) { 292 // Unlink it too, but only if we bound it. 293 unlink(ml->socket_filename); 294 free(ml->socket_filename); 295 ml->socket_filename = NULL; 296 } 297 } 298 //! @todo close epoll_fd? 299}