| /* |
| * Copyright (C) 2005 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. |
| */ |
| |
| // |
| // Unidirectional pipe. |
| // |
| |
| #include <utils/Pipe.h> |
| #include <utils/Log.h> |
| |
| #if defined(HAVE_WIN32_IPC) |
| # include <windows.h> |
| #else |
| # include <fcntl.h> |
| # include <unistd.h> |
| # include <errno.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <string.h> |
| |
| using namespace android; |
| |
| const unsigned long kInvalidHandle = (unsigned long) -1; |
| |
| |
| /* |
| * Constructor. Do little. |
| */ |
| Pipe::Pipe(void) |
| : mReadNonBlocking(false), mReadHandle(kInvalidHandle), |
| mWriteHandle(kInvalidHandle) |
| { |
| } |
| |
| /* |
| * Destructor. Use the system-appropriate close call. |
| */ |
| Pipe::~Pipe(void) |
| { |
| #if defined(HAVE_WIN32_IPC) |
| if (mReadHandle != kInvalidHandle) { |
| if (!CloseHandle((HANDLE)mReadHandle)) |
| LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", |
| mReadHandle); |
| } |
| if (mWriteHandle != kInvalidHandle) { |
| FlushFileBuffers((HANDLE)mWriteHandle); |
| if (!CloseHandle((HANDLE)mWriteHandle)) |
| LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", |
| mWriteHandle); |
| } |
| #else |
| if (mReadHandle != kInvalidHandle) { |
| if (close((int) mReadHandle) != 0) |
| LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", |
| (int) mReadHandle); |
| } |
| if (mWriteHandle != kInvalidHandle) { |
| if (close((int) mWriteHandle) != 0) |
| LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", |
| (int) mWriteHandle); |
| } |
| #endif |
| } |
| |
| /* |
| * Create the pipe. |
| * |
| * Use the POSIX stuff for everything but Windows. |
| */ |
| bool Pipe::create(void) |
| { |
| assert(mReadHandle == kInvalidHandle); |
| assert(mWriteHandle == kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| /* we use this across processes, so they need to be inheritable */ |
| HANDLE handles[2]; |
| SECURITY_ATTRIBUTES saAttr; |
| |
| saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| saAttr.bInheritHandle = TRUE; |
| saAttr.lpSecurityDescriptor = NULL; |
| |
| if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { |
| LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); |
| return false; |
| } |
| mReadHandle = (unsigned long) handles[0]; |
| mWriteHandle = (unsigned long) handles[1]; |
| return true; |
| #else |
| int fds[2]; |
| |
| if (pipe(fds) != 0) { |
| LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); |
| return false; |
| } |
| mReadHandle = fds[0]; |
| mWriteHandle = fds[1]; |
| return true; |
| #endif |
| } |
| |
| /* |
| * Create a "half pipe". Please, no Segway riding. |
| */ |
| bool Pipe::createReader(unsigned long handle) |
| { |
| mReadHandle = handle; |
| assert(mWriteHandle == kInvalidHandle); |
| return true; |
| } |
| |
| /* |
| * Create a "half pipe" for writing. |
| */ |
| bool Pipe::createWriter(unsigned long handle) |
| { |
| mWriteHandle = handle; |
| assert(mReadHandle == kInvalidHandle); |
| return true; |
| } |
| |
| /* |
| * Return "true" if create() has been called successfully. |
| */ |
| bool Pipe::isCreated(void) |
| { |
| // one or the other should be open |
| return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); |
| } |
| |
| |
| /* |
| * Read data from the pipe. |
| * |
| * For Linux and Darwin, just call read(). For Windows, implement |
| * non-blocking reads by calling PeekNamedPipe first. |
| */ |
| int Pipe::read(void* buf, int count) |
| { |
| assert(mReadHandle != kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| DWORD totalBytesAvail = count; |
| DWORD bytesRead; |
| |
| if (mReadNonBlocking) { |
| // use PeekNamedPipe to adjust read count expectations |
| if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, |
| &totalBytesAvail, NULL)) |
| { |
| LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); |
| return -1; |
| } |
| |
| if (totalBytesAvail == 0) |
| return 0; |
| } |
| |
| if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, |
| NULL)) |
| { |
| DWORD err = GetLastError(); |
| if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) |
| return 0; |
| LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); |
| return -1; |
| } |
| |
| return (int) bytesRead; |
| #else |
| int cc; |
| cc = ::read(mReadHandle, buf, count); |
| if (cc < 0 && errno == EAGAIN) |
| return 0; |
| return cc; |
| #endif |
| } |
| |
| /* |
| * Write data to the pipe. |
| * |
| * POSIX systems are trivial, Windows uses a different call and doesn't |
| * handle non-blocking writes. |
| * |
| * If we add non-blocking support here, we probably want to make it an |
| * all-or-nothing write. |
| * |
| * DO NOT use LOG() here, we could be writing a log message. |
| */ |
| int Pipe::write(const void* buf, int count) |
| { |
| assert(mWriteHandle != kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| DWORD bytesWritten; |
| |
| if (mWriteNonBlocking) { |
| // BUG: can't use PeekNamedPipe() to get the amount of space |
| // left. Looks like we need to use "overlapped I/O" functions. |
| // I just don't care that much. |
| } |
| |
| if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { |
| // can't LOG, use stderr |
| fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); |
| return -1; |
| } |
| |
| return (int) bytesWritten; |
| #else |
| int cc; |
| cc = ::write(mWriteHandle, buf, count); |
| if (cc < 0 && errno == EAGAIN) |
| return 0; |
| return cc; |
| #endif |
| } |
| |
| /* |
| * Figure out if there is data available on the read fd. |
| * |
| * We return "true" on error because we want the caller to try to read |
| * from the pipe. They'll notice the read failure and do something |
| * appropriate. |
| */ |
| bool Pipe::readReady(void) |
| { |
| assert(mReadHandle != kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| DWORD totalBytesAvail; |
| |
| if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, |
| &totalBytesAvail, NULL)) |
| { |
| LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); |
| return true; |
| } |
| |
| return (totalBytesAvail != 0); |
| #else |
| errno = 0; |
| fd_set readfds; |
| struct timeval tv = { 0, 0 }; |
| int cc; |
| |
| FD_ZERO(&readfds); |
| FD_SET(mReadHandle, &readfds); |
| |
| cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); |
| if (cc < 0) { |
| LOG(LOG_ERROR, "pipe", "select() failed\n"); |
| return true; |
| } else if (cc == 0) { |
| /* timed out, nothing available */ |
| return false; |
| } else if (cc == 1) { |
| /* our fd is ready */ |
| return true; |
| } else { |
| LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); |
| return true; |
| } |
| #endif |
| } |
| |
| /* |
| * Enable or disable non-blocking mode for the read descriptor. |
| * |
| * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to |
| * actually be in non-blocking mode. If this matters -- i.e. you're not |
| * using a select() call -- put a call to readReady() in front of the |
| * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for |
| * Darwin. |
| */ |
| bool Pipe::setReadNonBlocking(bool val) |
| { |
| assert(mReadHandle != kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| // nothing to do |
| #else |
| int flags; |
| |
| if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { |
| LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); |
| return false; |
| } |
| if (val) |
| flags |= O_NONBLOCK; |
| else |
| flags &= ~(O_NONBLOCK); |
| if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { |
| LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); |
| return false; |
| } |
| #endif |
| |
| mReadNonBlocking = val; |
| return true; |
| } |
| |
| /* |
| * Enable or disable non-blocking mode for the write descriptor. |
| * |
| * As with setReadNonBlocking(), this does not work on the Mac. |
| */ |
| bool Pipe::setWriteNonBlocking(bool val) |
| { |
| assert(mWriteHandle != kInvalidHandle); |
| |
| #if defined(HAVE_WIN32_IPC) |
| // nothing to do |
| #else |
| int flags; |
| |
| if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { |
| LOG(LOG_WARN, "pipe", |
| "Warning: couldn't get flags for pipe write fd (errno=%d)\n", |
| errno); |
| return false; |
| } |
| if (val) |
| flags |= O_NONBLOCK; |
| else |
| flags &= ~(O_NONBLOCK); |
| if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { |
| LOG(LOG_WARN, "pipe", |
| "Warning: couldn't set flags for pipe write fd (errno=%d)\n", |
| errno); |
| return false; |
| } |
| #endif |
| |
| mWriteNonBlocking = val; |
| return true; |
| } |
| |
| /* |
| * Specify whether a file descriptor can be inherited by a child process. |
| * Under Linux this means setting the close-on-exec flag, under Windows |
| * this is SetHandleInformation(HANDLE_FLAG_INHERIT). |
| */ |
| bool Pipe::disallowReadInherit(void) |
| { |
| if (mReadHandle == kInvalidHandle) |
| return false; |
| |
| #if defined(HAVE_WIN32_IPC) |
| if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) |
| return false; |
| #else |
| if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) |
| return false; |
| #endif |
| return true; |
| } |
| bool Pipe::disallowWriteInherit(void) |
| { |
| if (mWriteHandle == kInvalidHandle) |
| return false; |
| |
| #if defined(HAVE_WIN32_IPC) |
| if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) |
| return false; |
| #else |
| if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) |
| return false; |
| #endif |
| return true; |
| } |
| |
| /* |
| * Close read descriptor. |
| */ |
| bool Pipe::closeRead(void) |
| { |
| if (mReadHandle == kInvalidHandle) |
| return false; |
| |
| #if defined(HAVE_WIN32_IPC) |
| if (mReadHandle != kInvalidHandle) { |
| if (!CloseHandle((HANDLE)mReadHandle)) { |
| LOG(LOG_WARN, "pipe", "failed closing read handle\n"); |
| return false; |
| } |
| } |
| #else |
| if (mReadHandle != kInvalidHandle) { |
| if (close((int) mReadHandle) != 0) { |
| LOG(LOG_WARN, "pipe", "failed closing read fd\n"); |
| return false; |
| } |
| } |
| #endif |
| mReadHandle = kInvalidHandle; |
| return true; |
| } |
| |
| /* |
| * Close write descriptor. |
| */ |
| bool Pipe::closeWrite(void) |
| { |
| if (mWriteHandle == kInvalidHandle) |
| return false; |
| |
| #if defined(HAVE_WIN32_IPC) |
| if (mWriteHandle != kInvalidHandle) { |
| if (!CloseHandle((HANDLE)mWriteHandle)) { |
| LOG(LOG_WARN, "pipe", "failed closing write handle\n"); |
| return false; |
| } |
| } |
| #else |
| if (mWriteHandle != kInvalidHandle) { |
| if (close((int) mWriteHandle) != 0) { |
| LOG(LOG_WARN, "pipe", "failed closing write fd\n"); |
| return false; |
| } |
| } |
| #endif |
| mWriteHandle = kInvalidHandle; |
| return true; |
| } |
| |
| /* |
| * Get the read handle. |
| */ |
| unsigned long Pipe::getReadHandle(void) |
| { |
| assert(mReadHandle != kInvalidHandle); |
| |
| return mReadHandle; |
| } |
| |
| /* |
| * Get the write handle. |
| */ |
| unsigned long Pipe::getWriteHandle(void) |
| { |
| assert(mWriteHandle != kInvalidHandle); |
| |
| return mWriteHandle; |
| } |
| |