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