summaryrefslogtreecommitdiffstats
path: root/client/windows/named_pipe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/windows/named_pipe.cpp')
-rw-r--r--client/windows/named_pipe.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/client/windows/named_pipe.cpp b/client/windows/named_pipe.cpp
new file mode 100644
index 00000000..f33c476e
--- /dev/null
+++ b/client/windows/named_pipe.cpp
@@ -0,0 +1,227 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include "named_pipe.h"
+#include "utils.h"
+#include "debug.h"
+
+PipeBuffer::PipeBuffer(HANDLE pipe)
+ : _handler (NULL)
+ , _pipe (pipe)
+ , _start (0)
+ , _end (0)
+ , _pending (false)
+{
+ ZeroMemory(&_overlap, sizeof(_overlap));
+ _overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ _event_handle = _overlap.hEvent;
+ WinPlatform::add_event(*this);
+}
+
+PipeBuffer::~PipeBuffer()
+{
+ WinPlatform::remove_event(*this);
+ CloseHandle(_event_handle);
+}
+
+DWORD PipeBuffer::get_overlapped_bytes()
+{
+ DWORD bytes = 0;
+
+ if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE) || bytes == 0) {
+ _pending = false;
+ _handler->on_data();
+ }
+ return bytes;
+}
+
+int32_t PipeReader::read(uint8_t *buf, int32_t size)
+{
+ ASSERT(buf && size >= 0);
+
+ if (_start < _end) {
+ int32_t bytes_read = 0;
+ bytes_read = MIN(_end - _start, (uint32_t)size);
+ CopyMemory(buf, _data + _start, bytes_read);
+ _start += bytes_read;
+ if (_start == _end) {
+ _start = _end = 0;
+ }
+ return bytes_read;
+ }
+ if (_pending) {
+ return 0;
+ }
+ if (!ReadFile(_pipe, _data + _end, sizeof(_data) - _end, NULL, &_overlap) &&
+ GetLastError() != ERROR_IO_PENDING) {
+ DBG(0, "ReadFile() failed %u", GetLastError());
+ return -1;
+ }
+ _pending = true;
+ return 0;
+}
+
+void PipeReader::on_event()
+{
+ ASSERT(_pending);
+ DWORD bytes = get_overlapped_bytes();
+
+ if (!bytes) {
+ return;
+ }
+ _end += bytes;
+ _pending = false;
+ _handler->on_data();
+}
+
+int32_t PipeWriter::write(const uint8_t *buf, int32_t size)
+{
+ int32_t bytes_written = 0;
+ ASSERT(buf && size >= 0);
+
+ if (!_pending && _start == _end) {
+ _start = _end = 0;
+ }
+ if (_end < sizeof(_data)) {
+ bytes_written = MIN(sizeof(_data) - _end, (uint32_t)size);
+ CopyMemory(_data + _end, buf, bytes_written);
+ _end += bytes_written;
+ }
+ if (!_pending && _start < _end) {
+ if (!WriteFile(_pipe, _data + _start, _end - _start, NULL, &_overlap) &&
+ GetLastError() != ERROR_IO_PENDING) {
+ DBG(0, "WriteFile() failed %u", GetLastError());
+ return -1;
+ }
+ _pending = true;
+ }
+ return bytes_written;
+}
+
+void PipeWriter::on_event()
+{
+ ASSERT(_pending);
+ DWORD bytes = get_overlapped_bytes();
+ if (!bytes) {
+ return;
+ }
+ _start += bytes;
+ _pending = false;
+ if (_start == sizeof(_data)) {
+ _handler->on_data();
+ }
+}
+
+WinConnection::WinConnection(HANDLE pipe)
+ : _pipe (pipe)
+ , _writer (pipe)
+ , _reader (pipe)
+{
+}
+
+WinConnection::~WinConnection()
+{
+ if (!DisconnectNamedPipe(_pipe)) {
+ DBG(0, "DisconnectNamedPipe failed %d", GetLastError());
+ }
+ CloseHandle(_pipe);
+}
+
+int32_t WinConnection::read(uint8_t *buf, int32_t size)
+{
+ return _reader.read(buf, size);
+}
+
+int32_t WinConnection::write(const uint8_t *buf, int32_t size)
+{
+ return _writer.write(buf, size);
+}
+
+void WinConnection::set_handler(NamedPipe::ConnectionInterface* handler)
+{
+ _reader.set_handler(handler);
+ _writer.set_handler(handler);
+}
+
+WinListener::WinListener(const char *name, NamedPipe::ListenerInterface &listener_interface)
+ : _listener_interface (listener_interface)
+ , _pipe (0)
+{
+ _pipename = new TCHAR[PIPE_MAX_NAME_LEN];
+ swprintf_s(_pipename, PIPE_MAX_NAME_LEN, L"%s%S", PIPE_PREFIX, name);
+ ZeroMemory(&_overlap, sizeof(_overlap));
+ _overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ _event_handle = _overlap.hEvent;
+ WinPlatform::add_event(*this);
+ create_pipe();
+}
+
+WinListener::~WinListener()
+{
+ CancelIo(_pipe);
+ WinPlatform::remove_event(*this);
+ CloseHandle(_event_handle);
+ delete[] _pipename;
+}
+
+void WinListener::on_event()
+{
+ DWORD bytes;
+
+ if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE)) {
+ DBG(0, "GetOverlappedResult() failed %u", GetLastError());
+ return;
+ }
+ DBG(0, "Pipe connected 0x%p", _pipe);
+ WinConnection *con = new WinConnection(_pipe);
+ NamedPipe::ConnectionInterface &con_interface = _listener_interface.create();
+ con->set_handler(&con_interface);
+ con_interface.bind((NamedPipe::ConnectionRef)con);
+ create_pipe();
+}
+
+void WinListener::create_pipe()
+{
+ _pipe = CreateNamedPipe(_pipename, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES, PIPE_BUF_SIZE, PIPE_BUF_SIZE,
+ PIPE_TIMEOUT, NULL);
+ if (_pipe == INVALID_HANDLE_VALUE) {
+ THROW("CreateNamedPipe() failed %u", GetLastError());
+ }
+ if (ConnectNamedPipe(_pipe, &_overlap)) {
+ THROW("ConnectNamedPipe() is not pending");
+ }
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING:
+ DBG(0, "Pipe waits for connection");
+ break;
+ case ERROR_PIPE_CONNECTED: {
+ DBG(0, "Pipe already connected");
+ WinConnection *con = new WinConnection(_pipe);
+ NamedPipe::ConnectionInterface &con_interface = _listener_interface.create();
+ con->set_handler(&con_interface);
+ con_interface.bind((NamedPipe::ConnectionRef)con);
+ create_pipe();
+ break;
+ }
+ default:
+ THROW("ConnectNamedPipe() failed %u", GetLastError());
+ }
+}
+