445 lines
11 KiB
C++
445 lines
11 KiB
C++
//
|
|
// 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
|