465 lines
11 KiB
C++
465 lines
11 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
|