/*
* 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 "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;
}