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