| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmDebuggerWindowsPipeConnection.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cstring> |
| #include <stdexcept> |
| #include <utility> |
| |
| namespace cmDebugger { |
| |
| #ifdef _WIN32 |
| |
| DuplexPipe_WIN32::DuplexPipe_WIN32(HANDLE pipe) |
| : hPipe(pipe) |
| { |
| readOp.Offset = readOp.OffsetHigh = 0; |
| readOp.hEvent = CreateEvent(NULL, true, false, NULL); |
| writeOp.Offset = readOp.OffsetHigh = 0; |
| writeOp.hEvent = CreateEvent(NULL, true, false, NULL); |
| } |
| |
| DuplexPipe_WIN32::~DuplexPipe_WIN32() |
| { |
| close(); |
| } |
| |
| size_t DuplexPipe_WIN32::read(void* buffer, size_t n) |
| { |
| if (hPipe != INVALID_HANDLE_VALUE) { |
| readOp.Offset = readOp.OffsetHigh = 0; |
| ResetEvent(readOp.hEvent); |
| auto r = ReadFile(hPipe, buffer, n, NULL, &readOp); |
| auto err = GetLastError(); |
| if (r || err == ERROR_IO_PENDING) { |
| DWORD nRead = 0; |
| if (GetOverlappedResult(hPipe, &readOp, &nRead, true)) { |
| return nRead; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| bool DuplexPipe_WIN32::write(void const* buffer, size_t n) |
| { |
| if (hPipe != INVALID_HANDLE_VALUE) { |
| writeOp.Offset = writeOp.OffsetHigh = 0; |
| ResetEvent(writeOp.hEvent); |
| auto w = WriteFile(hPipe, buffer, n, NULL, &writeOp); |
| auto err = GetLastError(); |
| if (w || err == ERROR_IO_PENDING) { |
| DWORD nWrite = 0; |
| if (GetOverlappedResult(hPipe, &writeOp, &nWrite, true)) { |
| return n == nWrite; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| void DuplexPipe_WIN32::close() |
| { |
| CloseHandle(hPipe); |
| hPipe = INVALID_HANDLE_VALUE; |
| CloseHandle(readOp.hEvent); |
| CloseHandle(writeOp.hEvent); |
| readOp.hEvent = writeOp.hEvent = INVALID_HANDLE_VALUE; |
| } |
| |
| bool DuplexPipe_WIN32::WaitForConnection() |
| { |
| auto connect = ConnectNamedPipe(hPipe, &readOp); |
| auto err = GetLastError(); |
| if (!connect && err == ERROR_IO_PENDING) { |
| DWORD ignored; |
| if (GetOverlappedResult(hPipe, &readOp, &ignored, true)) { |
| return true; |
| } |
| } |
| |
| return connect || err == ERROR_PIPE_CONNECTED; |
| } |
| |
| cmDebuggerPipeConnection_WIN32::cmDebuggerPipeConnection_WIN32( |
| std::string name) |
| : PipeName(std::move(name)) |
| , pipes(nullptr) |
| { |
| } |
| |
| cmDebuggerPipeConnection_WIN32::~cmDebuggerPipeConnection_WIN32() |
| { |
| if (isOpen()) { |
| pipes = nullptr; |
| } |
| } |
| |
| bool cmDebuggerPipeConnection_WIN32::StartListening(std::string& errorMessage) |
| { |
| bool result = true; |
| |
| auto hPipe = CreateNamedPipeA( |
| PipeName.c_str(), |
| PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, |
| PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, |
| 1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, NULL); |
| |
| if (hPipe == INVALID_HANDLE_VALUE) { |
| auto err = GetLastError(); |
| errorMessage = GetErrorMessage(err); |
| result = false; |
| } |
| |
| if (result) { |
| pipes = std::make_unique<DuplexPipe_WIN32>(hPipe); |
| } |
| |
| StartedListening.set_value(); |
| return result; |
| } |
| |
| std::string cmDebuggerPipeConnection_WIN32::GetErrorMessage(DWORD errorCode) |
| { |
| LPSTR message = nullptr; |
| DWORD size = FormatMessageA( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_IGNORE_INSERTS, |
| nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPSTR)&message, 0, nullptr); |
| std::string errorMessage = "Internal Error with " + this->PipeName + ": " + |
| std::string(message, size); |
| LocalFree(message); |
| return errorMessage; |
| } |
| |
| std::shared_ptr<dap::Reader> cmDebuggerPipeConnection_WIN32::GetReader() |
| { |
| return std::static_pointer_cast<dap::Reader>(shared_from_this()); |
| } |
| |
| std::shared_ptr<dap::Writer> cmDebuggerPipeConnection_WIN32::GetWriter() |
| { |
| return std::static_pointer_cast<dap::Writer>(shared_from_this()); |
| } |
| |
| bool cmDebuggerPipeConnection_WIN32::isOpen() |
| { |
| return pipes != nullptr; |
| } |
| |
| void cmDebuggerPipeConnection_WIN32::close() |
| { |
| CloseConnection(); |
| } |
| |
| void cmDebuggerPipeConnection_WIN32::CloseConnection() |
| { |
| if (isOpen()) { |
| pipes->close(); |
| pipes = nullptr; |
| } |
| } |
| |
| void cmDebuggerPipeConnection_WIN32::WaitForConnection() |
| { |
| if (!isOpen()) { |
| return; |
| } |
| |
| if (pipes->WaitForConnection()) { |
| return; |
| } |
| |
| CloseConnection(); |
| } |
| |
| size_t cmDebuggerPipeConnection_WIN32::read(void* buffer, size_t n) |
| { |
| size_t result = 0; |
| if (isOpen()) { |
| result = pipes->read(buffer, n); |
| if (result == 0) { |
| CloseConnection(); |
| } |
| } |
| |
| return result; |
| } |
| |
| bool cmDebuggerPipeConnection_WIN32::write(void const* buffer, size_t n) |
| { |
| bool result = false; |
| if (isOpen()) { |
| result = pipes->write(buffer, n); |
| if (!result) { |
| CloseConnection(); |
| } |
| } |
| |
| return result; |
| } |
| |
| cmDebuggerPipeClient_WIN32::cmDebuggerPipeClient_WIN32(std::string name) |
| : PipeName(std::move(name)) |
| { |
| } |
| |
| cmDebuggerPipeClient_WIN32::~cmDebuggerPipeClient_WIN32() |
| { |
| close(); |
| } |
| |
| void cmDebuggerPipeClient_WIN32::WaitForConnection() |
| { |
| if (!isOpen()) { |
| auto hPipe = CreateFileA(PipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, |
| NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); |
| if (hPipe == INVALID_HANDLE_VALUE) { |
| auto err = GetLastError(); |
| throw std::runtime_error(std::string("CreateFile failed for pipe ") + |
| GetErrorMessage(err)); |
| } |
| |
| pipes = std::make_unique<DuplexPipe_WIN32>(hPipe); |
| } |
| } |
| |
| std::string cmDebuggerPipeClient_WIN32::GetErrorMessage(DWORD errorCode) |
| { |
| LPSTR message = nullptr; |
| DWORD size = FormatMessageA( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_IGNORE_INSERTS, |
| nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPSTR)&message, 0, nullptr); |
| std::string errorMessage = |
| this->PipeName + ": " + std::string(message, size); |
| LocalFree(message); |
| return errorMessage; |
| } |
| |
| bool cmDebuggerPipeClient_WIN32::isOpen() |
| { |
| return pipes != nullptr; |
| } |
| |
| void cmDebuggerPipeClient_WIN32::close() |
| { |
| if (isOpen()) { |
| pipes->close(); |
| pipes = nullptr; |
| } |
| } |
| |
| size_t cmDebuggerPipeClient_WIN32::read(void* buffer, size_t n) |
| { |
| size_t result = 0; |
| if (isOpen()) { |
| result = pipes->read(buffer, n); |
| if (result == 0) { |
| close(); |
| } |
| } |
| |
| return result; |
| } |
| |
| bool cmDebuggerPipeClient_WIN32::write(void const* buffer, size_t n) |
| { |
| bool result = false; |
| if (isOpen()) { |
| result = pipes->write(buffer, n); |
| if (!result) { |
| close(); |
| } |
| } |
| |
| return result; |
| } |
| |
| #endif // _WIN32 |
| |
| } // namespace cmDebugger |