//
// Copyright 2005 The Android Open Source Project
//
// Local named bi-directional communication channel.
//
#include "LocalBiChannel.h"
#include "utils/Log.h"
#if defined(HAVE_WIN32_IPC)
# define _WIN32_WINNT 0x0500
# include <windows.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/un.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifndef SUN_LEN
/*
* Our current set of ARM header files don't define this.
*/
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
#endif
using namespace android;
const unsigned long kInvalidHandle = (unsigned long) -1;
/*
* Initialize data fields.
*/
LocalBiChannel::LocalBiChannel(void)
: mFileName(NULL), mIsListener(false), mHandle(kInvalidHandle)
{
}
#if defined(HAVE_WIN32_IPC)
/*
* Implementation for Win32, using named pipes.
*
* Cygwin actually supports UNIX-domain sockets, but we want to stuff
* the file handles into a Pipe, which uses HANDLE under Win32.
*/
const int kPipeSize = 4096;
/*
* Destructor. If we're the server side, we may need to clean up after
* ourselves.
*/
LocalBiChannel::~LocalBiChannel(void)
{
if (mHandle != kInvalidHandle)
CloseHandle((HANDLE)mHandle);
delete[] mFileName;
}
/*
* Construct the full path. The caller must delete[] the return value.
*/
static char* makeFilename(const char* name)
{
static const char* kBasePath = "\\\\.\\pipe\\android-";
char* fileName;
assert(name != NULL && name[0] != '\0');
fileName = new char[strlen(kBasePath) + strlen(name) + 1];
strcpy(fileName, kBasePath);
strcat(fileName, name);
return fileName;
}
/*
* Create a named pipe, so the client has something to connect to.
*/
bool LocalBiChannel::create(const char* name)
{
delete[] mFileName;
mFileName = makeFilename(name);
#if 0
HANDLE hPipe;
hPipe = CreateNamedPipe(
mFileName, // unique pipe name
PIPE_ACCESS_DUPLEX | // open mode
FILE_FLAG_FIRST_PIPE_INSTANCE,
0, // pipe mode (byte, blocking)
1, // max instances
kPipeSize, // output buffer
kPipeSize, // input buffer
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
NULL); // security
if (hPipe == 0) {
LOG(LOG_ERROR, "lbicomm",
"CreateNamedPipe failed (err=%ld)\n", GetLastError());
return false;
}
mHandle = (unsigned long) hPipe;
#endif
return true;
}
/*
* Attach to an existing named pipe.
*/
bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
Pipe** ppWritePipe)
{
HANDLE hPipe, dupHandle;
delete[] mFileName;
mFileName = makeFilename(name);
hPipe = CreateFile(
mFileName, // filename
GENERIC_READ | GENERIC_WRITE, // access
0, // no sharing
NULL, // security
OPEN_EXISTING, // don't create
0, // attributes
NULL); // template
if (hPipe == INVALID_HANDLE_VALUE) {
LOG(LOG_ERROR, "lbicomm",
"CreateFile on pipe '%s' failed (err=%ld)\n", name, GetLastError());
return false;
}
assert(mHandle == kInvalidHandle);
/*
* Set up the pipes. Use the new handle for one, and a duplicate
* of it for the other, in case we decide to only close one side.
*/
*ppReadPipe = new Pipe();
(*ppReadPipe)->createReader((unsigned long) hPipe);
DuplicateHandle(
GetCurrentProcess(),
hPipe,
GetCurrentProcess(),
&dupHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
*ppWritePipe = new Pipe();
(*ppWritePipe)->createWriter((unsigned long) dupHandle);
return true;
}
/*
* Listen for a new connection, discarding any existing connection.
*/
bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
{
BOOL connected;
HANDLE hPipe;
/*
* Create up to 3 instances of the named pipe:
* - currently active connection
* - connection currently being rejected because one is already active
* - a new listener to wait for the next round
*/
hPipe = CreateNamedPipe(
mFileName, // unique pipe name
PIPE_ACCESS_DUPLEX // open mode
/*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
0, // pipe mode (byte, blocking)
3, // max instances
kPipeSize, // output buffer
kPipeSize, // input buffer
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
NULL); // security
if (hPipe == 0) {
LOG(LOG_ERROR, "lbicomm",
"CreateNamedPipe failed (err=%ld)\n", GetLastError());
return false;
}
/*
* If a client is already connected to us, this fails with
* ERROR_PIPE_CONNECTED. It returns success if we had to wait
* a little bit before the connection happens.
*/
connected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (connected) {
/*
* Create the pipes. Give one a duplicated handle so that,
* when one closes, we don't lose both.
*/
HANDLE dupHandle;
*ppReadPipe = new Pipe();
(*ppReadPipe)->createReader((unsigned long) hPipe);
DuplicateHandle(
GetCurrentProcess(),
hPipe,
GetCurrentProcess(),
&dupHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
*ppWritePipe = new Pipe();
(*ppWritePipe)->createWriter((unsigned long) dupHandle);
return true;
} else {
LOG(LOG_WARN, "lbicomm",
"ConnectNamedPipe failed (err=%ld)\n", GetLastError());
#ifdef HAVE_WIN32_THREADS
Sleep(500); /* 500 ms */
#else
usleep(500000); // DEBUG DEBUG
#endif
return false;
}
}
#else
/*
* Implementation for Linux and Darwin, using UNIX-domain sockets.
*/
/*
* Destructor. If we're the server side, blow away the socket file.
*/
LocalBiChannel::~LocalBiChannel(void)
{
if (mHandle != kInvalidHandle)
close((int) mHandle);
if (mIsListener && mFileName != NULL) {
LOG(LOG_DEBUG, "lbicomm", "Removing '%s'\n", mFileName);
(void) unlink(mFileName);
}
delete[] mFileName;
}
/*
* Construct the full path. The caller must delete[] the return value.
*/
static char* makeFilename(const char* name)
{
static const char* kBasePath = "/tmp/android-";
char* fileName;
assert(name != NULL && name[0] != '\0');
fileName = new char[strlen(kBasePath) + strlen(name) + 1];
strcpy(fileName, kBasePath);
strcat(fileName, name);
return fileName;
}
/*
* Create a UNIX domain socket, carefully removing it if it already
* exists.
*/
bool LocalBiChannel::create(const char* name)
{
struct stat sb;
bool result = false;
int sock = -1;
int cc;
delete[] mFileName;
mFileName = makeFilename(name);
cc = stat(mFileName, &sb);
if (cc < 0) {
if (errno != ENOENT) {
LOG(LOG_ERROR, "lbicomm",
"Unable to stat '%s' (errno=%d)\n", mFileName, errno);
goto bail;
}
} else {
/* don't touch it if it's not a socket */
if (!(S_ISSOCK(sb.st_mode))) {
LOG(LOG_ERROR, "lbicomm",
"File '%s' exists and is not a socket\n", mFileName);
goto bail;
}
/* remove the cruft */
if (unlink(mFileName) < 0) {
LOG(LOG_ERROR, "lbicomm",
"Unable to remove '%s' (errno=%d)\n", mFileName, errno);
goto bail;
}
}
struct sockaddr_un addr;
sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
LOG(LOG_ERROR, "lbicomm",
"UNIX domain socket create failed (errno=%d)\n", errno);
goto bail;
}
/* bind the socket; this creates the file on disk */
strcpy(addr.sun_path, mFileName); // max 108 bytes
addr.sun_family = AF_UNIX;
cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
if (cc < 0) {
LOG(LOG_ERROR, "lbicomm",
"AF_UNIX bind failed for '%s' (errno=%d)\n", mFileName, errno);
goto bail;
}
mHandle = (unsigned long) sock;
sock = -1;
mIsListener = true;
result = true;
bail:
if (sock >= 0)
close(sock);
return result;
}
/*
* Attach to an existing UNIX domain socket.
*/
bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
Pipe** ppWritePipe)
{
bool result = false;
int sock = -1;
int cc;
assert(ppReadPipe != NULL);
assert(ppWritePipe != NULL);
delete[] mFileName;
mFileName = makeFilename(name);
struct sockaddr_un addr;
sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
LOG(LOG_ERROR, "lbicomm",
"UNIX domain socket create failed (errno=%d)\n", errno);
goto bail;
}
/* connect to socket; fails if file doesn't exist */
strcpy(addr.sun_path, mFileName); // max 108 bytes
addr.sun_family = AF_UNIX;
cc = ::connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
if (cc < 0) {
// ENOENT means socket file doesn't exist
// ECONNREFUSED means socket exists but nobody is listening
LOG(LOG_ERROR, "lbicomm",
"AF_UNIX connect failed for '%s': %s\n", mFileName,strerror(errno));
goto bail;
}
/*
* Create the two halves. We dup() the sock so that closing one side
* does not hose the other.
*/
*ppReadPipe = new Pipe();
(*ppReadPipe)->createReader(sock);
*ppWritePipe = new Pipe();
(*ppWritePipe)->createWriter(dup(sock));
assert(mHandle == kInvalidHandle);
sock = -1;
mIsListener = false;
result = true;
bail:
if (sock >= 0)
close(sock);
return result;
}
/*
* Listen for a new connection.
*/
bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
{
bool result = false;
struct sockaddr_un from;
socklen_t fromlen;
int sock, lsock;
int cc;
assert(mHandle != kInvalidHandle);
lsock = (int) mHandle;
LOG(LOG_DEBUG, "lbicomm", "AF_UNIX listening\n");
cc = ::listen(lsock, 5);
if (cc < 0) {
LOG(LOG_ERROR, "lbicomm", "AF_UNIX listen failed (errno=%d)\n", errno);
goto bail;
}
fromlen = sizeof(from); // not SUN_LEN()
sock = ::accept(lsock, (struct sockaddr*) &from, &fromlen);
if (sock < 0) {
LOG(LOG_WARN, "lbicomm", "AF_UNIX accept failed (errno=%d)\n", errno);
goto bail;
}
/*
* Create the two halves. We dup() the sock so that closing one side
* does not hose the other.
*/
*ppReadPipe = new Pipe();
(*ppReadPipe)->createReader(sock);
*ppWritePipe = new Pipe();
(*ppWritePipe)->createWriter(dup(sock));
result = true;
bail:
return result;
}
#endif