The open source OpenXR runtime
at mr/scanout-values 301 lines 8.3 kB view raw
1// Copyright 2022, Magic Leap, Inc. 2// Copyright 2020-2022, Collabora, Ltd. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Server mainloop details on Windows. 7 * @author Julian Petrov <jpetrov@magicleap.com> 8 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 9 * @author Jakob Bornecrantz <jakob@collabora.com> 10 * @ingroup ipc_server 11 */ 12 13#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 14#define _CRT_SECURE_NO_WARNINGS 15#endif 16 17#include "xrt/xrt_device.h" 18#include "xrt/xrt_instance.h" 19#include "xrt/xrt_compositor.h" 20#include "xrt/xrt_config_have.h" 21#include "xrt/xrt_config_os.h" 22 23#include "os/os_time.h" 24#include "util/u_var.h" 25#include "util/u_misc.h" 26#include "util/u_debug.h" 27#include "util/u_trace_marker.h" 28#include "util/u_file.h" 29#include "util/u_windows.h" 30 31#include "shared/ipc_utils.h" 32#include "shared/ipc_shmem.h" 33#include "server/ipc_server.h" 34 35#include <conio.h> 36#include <sddl.h> 37 38 39/* 40 * 41 * Helpers. 42 * 43 */ 44 45#define ERROR_STR(BUF, ERR) (u_winerror(BUF, ARRAY_SIZE(BUF), ERR, true)) 46 47DEBUG_GET_ONCE_BOOL_OPTION(relaxed, "IPC_RELAXED_CONNECTION_SECURITY", false) 48 49 50/* 51 * 52 * Static functions. 53 * 54 */ 55 56template <unsigned int N> 57static char * 58get_current_process_name(char (&path)[N]) 59{ 60 GetModuleFileNameA(NULL, path, N); 61 char *exe = strrchr(path, '\\'); 62 if (exe) { 63 return exe + 1; 64 } 65 return path; 66} 67 68ULONG 69get_pipe_server_pid(const char *pipe_name) 70{ 71 ULONG pid = 0; 72 HANDLE h = CreateNamedPipeA( // 73 pipe_name, // 74 PIPE_ACCESS_DUPLEX, // 75 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, // 76 IPC_MAX_CLIENTS, // 77 IPC_BUF_SIZE, // 78 IPC_BUF_SIZE, // 79 0, // 80 nullptr); 81 if (h != INVALID_HANDLE_VALUE) { 82 GetNamedPipeServerProcessId(h, &pid); 83 CloseHandle(h); 84 } 85 return pid; 86} 87 88static bool 89create_pipe_instance(struct ipc_server_mainloop *ml, bool first) 90{ 91 SECURITY_ATTRIBUTES sa{}; 92 sa.nLength = sizeof(sa); 93 sa.lpSecurityDescriptor = nullptr; 94 sa.bInheritHandle = FALSE; 95 96 /* 97 * Change the pipe's DACL to allow other users access. 98 * 99 * https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl 100 * https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-strings 101 */ 102 const TCHAR *str = // 103 TEXT("D:") // Discretionary ACL 104 TEXT("(D;OICI;GA;;;BG)") // Guest: deny 105 TEXT("(D;OICI;GA;;;AN)") // Anonymous: deny 106 TEXT("(A;OICI;GRGWGX;;;AC)") // UWP/AppContainer packages: read/write/execute 107 TEXT("(A;OICI;GRGWGX;;;AU)") // Authenticated user: read/write/execute 108 TEXT("(A;OICI;GA;;;BA)"); // Administrator: full control 109 110 BOOL bret = ConvertStringSecurityDescriptorToSecurityDescriptor( // 111 str, // StringSecurityDescriptor 112 SDDL_REVISION_1, // StringSDRevision 113 &sa.lpSecurityDescriptor, // SecurityDescriptor 114 NULL); // SecurityDescriptorSize 115 if (!bret) { 116 DWORD err = GetLastError(); 117 char buffer[1024]; 118 U_LOG_E("ConvertStringSecurityDescriptorToSecurityDescriptor: %u %s", err, ERROR_STR(buffer, err)); 119 } 120 121 LPSECURITY_ATTRIBUTES lpsa = nullptr; 122 if (debug_get_bool_option_relaxed()) { 123 U_LOG_W("Using relax security permissions on pipe"); 124 lpsa = &sa; 125 } 126 127 DWORD dwOpenMode = PIPE_ACCESS_DUPLEX; 128 DWORD dwPipeMode = PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS; 129 130 if (first) { 131 dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; 132 } 133 134 ml->pipe_handle = CreateNamedPipeA( // 135 ml->pipe_name, // 136 dwOpenMode, // 137 dwPipeMode, // 138 IPC_MAX_CLIENTS, // 139 IPC_BUF_SIZE, // 140 IPC_BUF_SIZE, // 141 0, // 142 lpsa); // 143 144 if (sa.lpSecurityDescriptor != nullptr) { 145 // Need to free the security descriptor. 146 LocalFree(sa.lpSecurityDescriptor); 147 sa.lpSecurityDescriptor = nullptr; 148 } 149 150 if (ml->pipe_handle != INVALID_HANDLE_VALUE) { 151 return true; 152 } 153 154 DWORD err = GetLastError(); 155 if (err == ERROR_PIPE_BUSY) { 156 U_LOG_W("CreateNamedPipeA failed: %d %s An existing client must disconnect first!", err, 157 ipc_winerror(err)); 158 } else { 159 U_LOG_E("CreateNamedPipeA failed: %d %s", err, ipc_winerror(err)); 160 if (err == ERROR_ACCESS_DENIED && first) { 161 char path[MAX_PATH]; 162 char *exe = get_current_process_name(path); 163 ULONG pid = get_pipe_server_pid(ml->pipe_name); 164 if (pid) { 165 U_LOG_E( 166 "An existing process id %d has the communication pipe already created. You likely " 167 "have \"%s\" running already. This service instance cannot continue...", 168 pid, exe); 169 } else { 170 U_LOG_E( 171 "You likely have \"%s\" running already. This service instance cannot continue...", 172 exe); 173 } 174 } 175 } 176 177 return false; 178} 179 180static void 181create_another_pipe_instance(struct ipc_server *vs, struct ipc_server_mainloop *ml) 182{ 183 if (!create_pipe_instance(ml, false)) { 184 ipc_server_handle_failure(vs); 185 } 186} 187 188static void 189handle_connected_client(struct ipc_server *vs, struct ipc_server_mainloop *ml) 190{ 191 DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT; 192 BOOL bRet; 193 194 bRet = SetNamedPipeHandleState(ml->pipe_handle, &mode, nullptr, nullptr); 195 if (bRet) { 196 // Call into the generic client connected handling code. 197 ipc_server_handle_client_connected(vs, ml->pipe_handle); 198 199 // Create another pipe to wait on. 200 create_another_pipe_instance(vs, ml); 201 return; 202 } 203 204 DWORD err = GetLastError(); 205 U_LOG_E("SetNamedPipeHandleState(PIPE_READMODE_MESSAGE | PIPE_WAIT) failed: %d %s", err, ipc_winerror(err)); 206 ipc_server_handle_failure(vs); 207} 208 209 210/* 211 * 212 * Exported functions 213 * 214 */ 215 216void 217ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml) 218{ 219 IPC_TRACE_MARKER(); 220 221 if (_kbhit()) { 222 U_LOG_E("console input! exiting..."); 223 ipc_server_handle_shutdown_signal(vs); 224 return; 225 } 226 227 if (!ml->pipe_handle) { 228 create_another_pipe_instance(vs, ml); 229 } 230 if (!ml->pipe_handle) { 231 return; // Errors already logged. 232 } 233 234 if (ConnectNamedPipe(ml->pipe_handle, nullptr)) { 235 DWORD err = GetLastError(); 236 U_LOG_E("ConnectNamedPipe unexpected return TRUE treating as failure: %d %s", err, ipc_winerror(err)); 237 ipc_server_handle_failure(vs); 238 return; 239 } 240 241 switch (DWORD err = GetLastError()) { 242 case ERROR_PIPE_LISTENING: return; 243 case ERROR_PIPE_CONNECTED: handle_connected_client(vs, ml); return; 244 default: 245 U_LOG_E("ConnectNamedPipe failed: %d %s", err, ipc_winerror(err)); 246 ipc_server_handle_failure(vs); 247 return; 248 } 249} 250 251int 252ipc_server_mainloop_init(struct ipc_server_mainloop *ml) 253{ 254 IPC_TRACE_MARKER(); 255 256 ml->pipe_handle = INVALID_HANDLE_VALUE; 257 ml->pipe_name = nullptr; 258 259 constexpr char pipe_prefix[] = "\\\\.\\pipe\\"; 260 constexpr int prefix_len = sizeof(pipe_prefix) - 1; 261 char pipe_name[MAX_PATH + prefix_len]; 262 strcpy(pipe_name, pipe_prefix); 263 264 if (u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, pipe_name + prefix_len, MAX_PATH) == -1) { 265 U_LOG_E("u_file_get_path_in_runtime_dir failed!"); 266 return -1; 267 } 268 269 ml->pipe_name = _strdup(pipe_name); 270 if (ml->pipe_name == nullptr) { 271 U_LOG_E("_strdup(\"%s\") failed!", pipe_name); 272 goto err; 273 } 274 275 if (!create_pipe_instance(ml, true)) { 276 U_LOG_E("CreateNamedPipeA \"%s\" first instance failed, see above.", ml->pipe_name); 277 goto err; 278 } 279 280 return 0; 281 282err: 283 ipc_server_mainloop_deinit(ml); 284 285 return -1; 286} 287 288void 289ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml) 290{ 291 IPC_TRACE_MARKER(); 292 293 if (ml->pipe_handle != INVALID_HANDLE_VALUE) { 294 CloseHandle(ml->pipe_handle); 295 ml->pipe_handle = INVALID_HANDLE_VALUE; 296 } 297 if (ml->pipe_name) { 298 free(ml->pipe_name); 299 ml->pipe_name = nullptr; 300 } 301}