| /* |
| * 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 <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <windows.h> |
| |
| #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(char addrstr[MAX_ADDRSTR_LEN]) |
| { |
| m_port = GetCurrentProcessId(); |
| make_pipe_name(addrstr, MAX_ADDRSTR_LEN, m_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(const char* addr) |
| { |
| HANDLE pipe; |
| int tries = 10; |
| |
| /* We're going to loop in order to wait for the pipe server to |
| * be setup properly. |
| */ |
| for (; tries > 0; tries--) { |
| pipe = ::CreateFile( |
| addr, // 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(addr, 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; |
| } |