/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include "common.h"
#include "named_pipe.h"
#include "utils.h"
#include "debug.h"
#define PIPE_TIMEOUT 5000
#define PIPE_MAX_NAME_LEN 256
#define PIPE_PREFIX TEXT("\\\\.\\pipe\\")
PipeBuffer::PipeBuffer(HANDLE pipe, ProcessLoop& process_loop)
: _handler (NULL)
, _pipe (pipe)
, _start (0)
, _end (0)
, _pending (false)
, _process_loop(process_loop)
{
ZeroMemory(&_overlap, sizeof(_overlap));
_overlap.hEvent = this->get_handle();
_process_loop.add_handle(*this);
}
PipeBuffer::~PipeBuffer()
{
_process_loop.remove_handle(*this);
}
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, ProcessLoop& process_loop)
: _pipe (pipe)
, _writer (pipe, process_loop)
, _reader (pipe, process_loop)
{
}
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,
ProcessLoop& process_loop)
: _listener_interface (listener_interface)
, _pipe (0)
, _process_loop (process_loop)
{
_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 = this->get_handle();
_process_loop.add_handle(*this);
create_pipe();
}
WinListener::~WinListener()
{
CancelIo(_pipe);
_process_loop.remove_handle(*this);
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, _process_loop);
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, _process_loop);
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());
}
}