/* 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 #include #include #include #include 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(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 cmDebuggerPipeConnection_WIN32::GetReader() { return std::static_pointer_cast(shared_from_this()); } std::shared_ptr cmDebuggerPipeConnection_WIN32::GetWriter() { return std::static_pointer_cast(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(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