// Copyright 2022, Magic Leap, Inc. // Copyright 2020-2022, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Server mainloop details on Windows. * @author Julian Petrov * @author Ryan Pavlik * @author Jakob Bornecrantz * @ingroup ipc_server */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include "xrt/xrt_device.h" #include "xrt/xrt_instance.h" #include "xrt/xrt_compositor.h" #include "xrt/xrt_config_have.h" #include "xrt/xrt_config_os.h" #include "os/os_time.h" #include "util/u_var.h" #include "util/u_misc.h" #include "util/u_debug.h" #include "util/u_trace_marker.h" #include "util/u_file.h" #include "shared/ipc_shmem.h" #include "server/ipc_server.h" #include /* * * Static functions. * */ static void ipc_server_create_another_pipe_instance(struct ipc_server *vs, struct ipc_server_mainloop *ml) { ml->pipe_handle = CreateNamedPipeA(ml->pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, IPC_MAX_CLIENTS, IPC_BUF_SIZE, IPC_BUF_SIZE, 0, NULL); if (ml->pipe_handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_PIPE_BUSY) { U_LOG_W("CreateNamedPipeA failed: %d %s An existing client must disconnect first!", err, ipc_winerror(err)); } else { U_LOG_E("CreateNamedPipeA failed: %d %s", err, ipc_winerror(err)); ipc_server_handle_failure(vs); } } } /* * * Exported functions * */ void ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml) { IPC_TRACE_MARKER(); if (_kbhit()) { U_LOG_E("console input! exiting..."); ipc_server_handle_shutdown_signal(vs); return; } if (!ml->pipe_handle) { ipc_server_create_another_pipe_instance(vs, ml); } if (!ml->pipe_handle) { return; } if (ConnectNamedPipe(ml->pipe_handle, NULL)) { U_LOG_E("ConnectNamedPipe unexpected return TRUE (last error: %d). Treating as failure...", GetLastError()); ipc_server_handle_failure(vs); return; } switch (DWORD err = GetLastError()) { case ERROR_PIPE_LISTENING: return; case ERROR_PIPE_CONNECTED: { DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT; if (!SetNamedPipeHandleState(ml->pipe_handle, &mode, NULL, NULL)) { err = GetLastError(); U_LOG_E("SetNamedPipeHandleState(PIPE_READMODE_MESSAGE | PIPE_WAIT) failed: %d %s", err, ipc_winerror(err)); ipc_server_handle_failure(vs); return; } ipc_server_start_client_listener_thread(vs, ml->pipe_handle); ipc_server_create_another_pipe_instance(vs, ml); return; } default: U_LOG_E("ConnectNamedPipe failed: %d %s", err, ipc_winerror(err)); ipc_server_handle_failure(vs); return; } } int ipc_server_mainloop_init(struct ipc_server_mainloop *ml) { IPC_TRACE_MARKER(); ml->pipe_handle = INVALID_HANDLE_VALUE; ml->pipe_name = nullptr; constexpr char pipe_prefix[] = "\\\\.\\pipe\\"; constexpr int prefix_len = sizeof(pipe_prefix) - 1; char pipe_name[MAX_PATH + prefix_len]; strcpy(pipe_name, pipe_prefix); if (u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, pipe_name + prefix_len, MAX_PATH) == -1) { U_LOG_E("u_file_get_path_in_runtime_dir failed!"); return -1; } ml->pipe_name = _strdup(pipe_name); if (!ml->pipe_name) { U_LOG_E("_strdup(\"%s\") failed!", pipe_name); } else { ml->pipe_handle = CreateNamedPipeA(ml->pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, IPC_MAX_CLIENTS, IPC_BUF_SIZE, IPC_BUF_SIZE, 0, NULL); if (ml->pipe_handle != INVALID_HANDLE_VALUE) { return 0; } DWORD err = GetLastError(); U_LOG_E("CreateNamedPipeA \"%s\" first instance failed: %d %s", ml->pipe_name, err, ipc_winerror(err)); } ipc_server_mainloop_deinit(ml); return -1; } void ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml) { IPC_TRACE_MARKER(); if (ml->pipe_handle != INVALID_HANDLE_VALUE) { CloseHandle(ml->pipe_handle); ml->pipe_handle = INVALID_HANDLE_VALUE; } if (ml->pipe_name) { free(ml->pipe_name); ml->pipe_name = nullptr; } }