// // 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