/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Win32PipeStream.h" #include #include #include #include #include #include #ifndef _WIN32 #error ONLY BUILD THIS SOURCE FILE FOR WINDOWS! #endif /* The official documentation states that the name of a given named * pipe cannot be more than 256 characters long. */ #define NAMED_PIPE_MAX 256 Win32PipeStream::Win32PipeStream(size_t bufSize) : SocketStream(bufSize), m_pipe(INVALID_HANDLE_VALUE) { } Win32PipeStream::Win32PipeStream(HANDLE pipe, size_t bufSize) : SocketStream(-1, bufSize), m_pipe(pipe) { } Win32PipeStream::~Win32PipeStream() { if (m_pipe != INVALID_HANDLE_VALUE) { CloseHandle(m_pipe); m_pipe = INVALID_HANDLE_VALUE; } } /* Initialize the pipe name corresponding to a given port */ static void make_pipe_name(char *path, size_t pathlen, int port_number) { snprintf(path, pathlen, "\\\\.\\pipe\\qemu-gles-%d", port_number); } /* Technical note: Named pipes work differently from BSD Sockets. * One does not create/bind a pipe, and collect a new handle each * time a client connects with accept(). * * Instead, the server creates a new pipe instance each time it wants * to get a new client connection, then calls ConnectNamedPipe() to * wait for a connection. * * So listen() is a no-op, and accept() really creates the pipe handle. * * Also, connect() must create a pipe handle with CreateFile() and * wait for a server instance with WaitNamedPipe() */ int Win32PipeStream::listen(unsigned short port) { // just save the port number for accept() m_port = port; return 0; } SocketStream * Win32PipeStream::accept() { char path[NAMED_PIPE_MAX+1]; SocketStream* clientStream; HANDLE pipe; make_pipe_name(path, sizeof(path), m_port); pipe = ::CreateNamedPipe( path, // pipe name PIPE_ACCESS_DUPLEX, // read-write access PIPE_TYPE_BYTE | // byte-oriented writes PIPE_READMODE_BYTE | // byte-oriented reads PIPE_WAIT, // blocking operations PIPE_UNLIMITED_INSTANCES, // no limit on clients 4096, // input buffer size 4096, // output buffer size 0, // client time-out NULL); // default security attributes if (pipe == INVALID_HANDLE_VALUE) { ERR("%s: CreateNamedPipe failed %d\n", __FUNCTION__, (int)GetLastError()); return NULL; } // Stupid Win32 API design: If a client is already connected, then // ConnectNamedPipe will return 0, and GetLastError() will return // ERROR_PIPE_CONNECTED. This is not an error! It just means that the // function didn't have to wait. // if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) { ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError()); CloseHandle(pipe); return NULL; } clientStream = new Win32PipeStream(pipe, m_bufsize); return clientStream; } int Win32PipeStream::connect(unsigned short port) { char path[NAMED_PIPE_MAX+1]; HANDLE pipe; int tries = 10; make_pipe_name(path, sizeof(path), port); /* We're going to loop in order to wait for the pipe server to * be setup properly. */ for (; tries > 0; tries--) { pipe = ::CreateFile( path, // pipe name GENERIC_READ | GENERIC_WRITE, // read & write 0, // no sharing NULL, // default security attrs OPEN_EXISTING, // open existing pipe 0, // default attributes NULL); // no template file /* If we have a valid pipe handle, break from the loop */ if (pipe != INVALID_HANDLE_VALUE) { break; } /* We can get here if the pipe is busy, i.e. if the server hasn't * create a new pipe instance to service our request. In which case * GetLastError() will return ERROR_PIPE_BUSY. * * If so, then use WaitNamedPipe() to wait for a decent time * to try again. */ if (GetLastError() != ERROR_PIPE_BUSY) { /* Not ERROR_PIPE_BUSY */ ERR("%s: CreateFile failed: %d\n", __FUNCTION__, (int)GetLastError()); errno = EINVAL; return -1; } /* Wait for 5 seconds */ if ( !WaitNamedPipe(path, 5000) ) { ERR("%s: WaitNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError()); errno = EINVAL; return -1; } } m_pipe = pipe; return 0; } /* Special buffer methods, since we can't use socket functions here */ int Win32PipeStream::commitBuffer(size_t size) { if (m_pipe == INVALID_HANDLE_VALUE) return -1; size_t res = size; int retval = 0; while (res > 0) { DWORD written; if (! ::WriteFile(m_pipe, (const char *)m_buf + (size - res), res, &written, NULL)) { retval = -1; ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError()); break; } res -= written; } return retval; } const unsigned char *Win32PipeStream::readFully(void *buf, size_t len) { const unsigned char* ret = NULL; if (m_pipe == INVALID_HANDLE_VALUE) return NULL; if (!buf) { return NULL; // do not allow NULL buf in that implementation } size_t res = len; while (res > 0) { DWORD readcount = 0; if (! ::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) { errno = (int)GetLastError(); return NULL; } res -= readcount; } return (const unsigned char *)buf; } const unsigned char *Win32PipeStream::read( void *buf, size_t *inout_len) { size_t len = *inout_len; DWORD readcount; if (m_pipe == INVALID_HANDLE_VALUE) return NULL; if (!buf) { return NULL; // do not allow NULL buf in that implementation } if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) { errno = (int)GetLastError(); return NULL; } *inout_len = (size_t)readcount; return (const unsigned char *)buf; }