| //===-- PipeWindows.cpp ---------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "lldb/Host/windows/PipeWindows.h" | 
 |  | 
 | #include "llvm/ADT/SmallString.h" | 
 | #include "llvm/Support/Process.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 |  | 
 | #include <fcntl.h> | 
 | #include <io.h> | 
 | #include <rpc.h> | 
 |  | 
 | #include <atomic> | 
 | #include <string> | 
 |  | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 |  | 
 | namespace { | 
 | std::atomic<uint32_t> g_pipe_serial(0); | 
 | constexpr llvm::StringLiteral g_pipe_name_prefix = "\\\\.\\Pipe\\"; | 
 | } // namespace | 
 |  | 
 | PipeWindows::PipeWindows() | 
 |     : m_read(INVALID_HANDLE_VALUE), m_write(INVALID_HANDLE_VALUE), | 
 |       m_read_fd(PipeWindows::kInvalidDescriptor), | 
 |       m_write_fd(PipeWindows::kInvalidDescriptor) { | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |   ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 | } | 
 |  | 
 | PipeWindows::PipeWindows(pipe_t read, pipe_t write) | 
 |     : m_read((HANDLE)read), m_write((HANDLE)write), | 
 |       m_read_fd(PipeWindows::kInvalidDescriptor), | 
 |       m_write_fd(PipeWindows::kInvalidDescriptor) { | 
 |   assert(read != LLDB_INVALID_PIPE || write != LLDB_INVALID_PIPE); | 
 |  | 
 |   // Don't risk in passing file descriptors and getting handles from them by | 
 |   // _get_osfhandle since the retrieved handles are highly likely unrecognized | 
 |   // in the current process and usually crashes the program.  Pass handles | 
 |   // instead since the handle can be inherited. | 
 |  | 
 |   if (read != LLDB_INVALID_PIPE) { | 
 |     m_read_fd = _open_osfhandle((intptr_t)read, _O_RDONLY); | 
 |     // Make sure the fd and native handle are consistent. | 
 |     if (m_read_fd < 0) | 
 |       m_read = INVALID_HANDLE_VALUE; | 
 |   } | 
 |  | 
 |   if (write != LLDB_INVALID_PIPE) { | 
 |     m_write_fd = _open_osfhandle((intptr_t)write, _O_WRONLY); | 
 |     if (m_write_fd < 0) | 
 |       m_write = INVALID_HANDLE_VALUE; | 
 |   } | 
 |  | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |   ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 | } | 
 |  | 
 | PipeWindows::~PipeWindows() { Close(); } | 
 |  | 
 | Status PipeWindows::CreateNew(bool child_process_inherit) { | 
 |   // Create an anonymous pipe with the specified inheritance. | 
 |   SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0, | 
 |                          child_process_inherit ? TRUE : FALSE}; | 
 |   BOOL result = ::CreatePipe(&m_read, &m_write, &sa, 1024); | 
 |   if (result == FALSE) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |   m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |   m_read_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr); | 
 |  | 
 |   m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); | 
 |   ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 |  | 
 |   return Status(); | 
 | } | 
 |  | 
 | Status PipeWindows::CreateNewNamed(bool child_process_inherit) { | 
 |   // Even for anonymous pipes, we open a named pipe.  This is because you | 
 |   // cannot get overlapped i/o on Windows without using a named pipe.  So we | 
 |   // synthesize a unique name. | 
 |   uint32_t serial = g_pipe_serial.fetch_add(1); | 
 |   std::string pipe_name; | 
 |   llvm::raw_string_ostream pipe_name_stream(pipe_name); | 
 |   pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial; | 
 |   pipe_name_stream.flush(); | 
 |  | 
 |   return CreateNew(pipe_name.c_str(), child_process_inherit); | 
 | } | 
 |  | 
 | Status PipeWindows::CreateNew(llvm::StringRef name, | 
 |                               bool child_process_inherit) { | 
 |   if (name.empty()) | 
 |     return Status(ERROR_INVALID_PARAMETER, eErrorTypeWin32); | 
 |  | 
 |   if (CanRead() || CanWrite()) | 
 |     return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32); | 
 |  | 
 |   std::string pipe_path = g_pipe_name_prefix.str(); | 
 |   pipe_path.append(name.str()); | 
 |  | 
 |   // Always open for overlapped i/o.  We implement blocking manually in Read | 
 |   // and Write. | 
 |   DWORD read_mode = FILE_FLAG_OVERLAPPED; | 
 |   m_read = ::CreateNamedPipeA( | 
 |       pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, | 
 |       PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); | 
 |   if (INVALID_HANDLE_VALUE == m_read) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |   m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |   m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); | 
 |  | 
 |   // Open the write end of the pipe. Note that closing either the read or  | 
 |   // write end of the pipe could directly close the pipe itself. | 
 |   Status result = OpenNamedPipe(name, child_process_inherit, false); | 
 |   if (!result.Success()) { | 
 |     CloseReadFileDescriptor(); | 
 |     return result; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | Status PipeWindows::CreateWithUniqueName(llvm::StringRef prefix, | 
 |                                          bool child_process_inherit, | 
 |                                          llvm::SmallVectorImpl<char> &name) { | 
 |   llvm::SmallString<128> pipe_name; | 
 |   Status error; | 
 |   ::UUID unique_id; | 
 |   RPC_CSTR unique_string; | 
 |   RPC_STATUS status = ::UuidCreate(&unique_id); | 
 |   if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) | 
 |     status = ::UuidToStringA(&unique_id, &unique_string); | 
 |   if (status == RPC_S_OK) { | 
 |     pipe_name = prefix; | 
 |     pipe_name += "-"; | 
 |     pipe_name += reinterpret_cast<char *>(unique_string); | 
 |     ::RpcStringFreeA(&unique_string); | 
 |     error = CreateNew(pipe_name, child_process_inherit); | 
 |   } else { | 
 |     error.SetError(status, eErrorTypeWin32); | 
 |   } | 
 |   if (error.Success()) | 
 |     name = pipe_name; | 
 |   return error; | 
 | } | 
 |  | 
 | Status PipeWindows::OpenAsReader(llvm::StringRef name, | 
 |                                  bool child_process_inherit) { | 
 |   if (CanRead()) | 
 |     return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32); | 
 |  | 
 |   return OpenNamedPipe(name, child_process_inherit, true); | 
 | } | 
 |  | 
 | Status | 
 | PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name, | 
 |                                      bool child_process_inherit, | 
 |                                      const std::chrono::microseconds &timeout) { | 
 |   if (CanWrite()) | 
 |     return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32); | 
 |  | 
 |   return OpenNamedPipe(name, child_process_inherit, false); | 
 | } | 
 |  | 
 | Status PipeWindows::OpenNamedPipe(llvm::StringRef name, | 
 |                                   bool child_process_inherit, bool is_read) { | 
 |   if (name.empty()) | 
 |     return Status(ERROR_INVALID_PARAMETER, eErrorTypeWin32); | 
 |  | 
 |   assert(is_read ? !CanRead() : !CanWrite()); | 
 |  | 
 |   SECURITY_ATTRIBUTES attributes = {}; | 
 |   attributes.bInheritHandle = child_process_inherit; | 
 |  | 
 |   std::string pipe_path = g_pipe_name_prefix.str(); | 
 |   pipe_path.append(name.str()); | 
 |  | 
 |   if (is_read) { | 
 |     m_read = ::CreateFileA(pipe_path.c_str(), GENERIC_READ, 0, &attributes, | 
 |                            OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | 
 |     if (INVALID_HANDLE_VALUE == m_read) | 
 |       return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |     m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); | 
 |  | 
 |     ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |     m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); | 
 |   } else { | 
 |     m_write = ::CreateFileA(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, | 
 |                             OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | 
 |     if (INVALID_HANDLE_VALUE == m_write) | 
 |       return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |     m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); | 
 |  | 
 |     ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 |   } | 
 |  | 
 |   return Status(); | 
 | } | 
 |  | 
 | int PipeWindows::GetReadFileDescriptor() const { return m_read_fd; } | 
 |  | 
 | int PipeWindows::GetWriteFileDescriptor() const { return m_write_fd; } | 
 |  | 
 | int PipeWindows::ReleaseReadFileDescriptor() { | 
 |   if (!CanRead()) | 
 |     return PipeWindows::kInvalidDescriptor; | 
 |   int result = m_read_fd; | 
 |   m_read_fd = PipeWindows::kInvalidDescriptor; | 
 |   if (m_read_overlapped.hEvent) | 
 |     ::CloseHandle(m_read_overlapped.hEvent); | 
 |   m_read = INVALID_HANDLE_VALUE; | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 |   return result; | 
 | } | 
 |  | 
 | int PipeWindows::ReleaseWriteFileDescriptor() { | 
 |   if (!CanWrite()) | 
 |     return PipeWindows::kInvalidDescriptor; | 
 |   int result = m_write_fd; | 
 |   m_write_fd = PipeWindows::kInvalidDescriptor; | 
 |   m_write = INVALID_HANDLE_VALUE; | 
 |   ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 |   return result; | 
 | } | 
 |  | 
 | void PipeWindows::CloseReadFileDescriptor() { | 
 |   if (!CanRead()) | 
 |     return; | 
 |  | 
 |   if (m_read_overlapped.hEvent) | 
 |     ::CloseHandle(m_read_overlapped.hEvent); | 
 |  | 
 |   _close(m_read_fd); | 
 |   m_read = INVALID_HANDLE_VALUE; | 
 |   m_read_fd = PipeWindows::kInvalidDescriptor; | 
 |   ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); | 
 | } | 
 |  | 
 | void PipeWindows::CloseWriteFileDescriptor() { | 
 |   if (!CanWrite()) | 
 |     return; | 
 |  | 
 |   _close(m_write_fd); | 
 |   m_write = INVALID_HANDLE_VALUE; | 
 |   m_write_fd = PipeWindows::kInvalidDescriptor; | 
 |   ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); | 
 | } | 
 |  | 
 | void PipeWindows::Close() { | 
 |   CloseReadFileDescriptor(); | 
 |   CloseWriteFileDescriptor(); | 
 | } | 
 |  | 
 | Status PipeWindows::Delete(llvm::StringRef name) { return Status(); } | 
 |  | 
 | bool PipeWindows::CanRead() const { return (m_read != INVALID_HANDLE_VALUE); } | 
 |  | 
 | bool PipeWindows::CanWrite() const { return (m_write != INVALID_HANDLE_VALUE); } | 
 |  | 
 | HANDLE | 
 | PipeWindows::GetReadNativeHandle() { return m_read; } | 
 |  | 
 | HANDLE | 
 | PipeWindows::GetWriteNativeHandle() { return m_write; } | 
 |  | 
 | Status PipeWindows::ReadWithTimeout(void *buf, size_t size, | 
 |                                     const std::chrono::microseconds &duration, | 
 |                                     size_t &bytes_read) { | 
 |   if (!CanRead()) | 
 |     return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32); | 
 |  | 
 |   bytes_read = 0; | 
 |   DWORD sys_bytes_read = size; | 
 |   BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, | 
 |                            &m_read_overlapped); | 
 |   if (!result && GetLastError() != ERROR_IO_PENDING) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |   DWORD timeout = (duration == std::chrono::microseconds::zero()) | 
 |                       ? INFINITE | 
 |                       : duration.count() * 1000; | 
 |   DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); | 
 |   if (wait_result != WAIT_OBJECT_0) { | 
 |     // The operation probably failed.  However, if it timed out, we need to | 
 |     // cancel the I/O. Between the time we returned from WaitForSingleObject | 
 |     // and the time we call CancelIoEx, the operation may complete.  If that | 
 |     // hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. If that | 
 |     // happens, the original operation should be considered to have been | 
 |     // successful. | 
 |     bool failed = true; | 
 |     DWORD failure_error = ::GetLastError(); | 
 |     if (wait_result == WAIT_TIMEOUT) { | 
 |       BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); | 
 |       if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) | 
 |         failed = false; | 
 |     } | 
 |     if (failed) | 
 |       return Status(failure_error, eErrorTypeWin32); | 
 |   } | 
 |  | 
 |   // Now we call GetOverlappedResult setting bWait to false, since we've | 
 |   // already waited as long as we're willing to. | 
 |   if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |   bytes_read = sys_bytes_read; | 
 |   return Status(); | 
 | } | 
 |  | 
 | Status PipeWindows::Write(const void *buf, size_t num_bytes, | 
 |                           size_t &bytes_written) { | 
 |   if (!CanWrite()) | 
 |     return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32); | 
 |  | 
 |   DWORD sys_bytes_written = 0; | 
 |   BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, | 
 |                                   &m_write_overlapped); | 
 |   if (!write_result && GetLastError() != ERROR_IO_PENDING) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |  | 
 |   BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, | 
 |                                     &sys_bytes_written, TRUE); | 
 |   if (!result) | 
 |     return Status(::GetLastError(), eErrorTypeWin32); | 
 |   return Status(); | 
 | } |