Home | History | Annotate | Download | only in app
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 // Local named bi-directional communication channel.
      5 //
      6 #include "LocalBiChannel.h"
      7 #include "utils/Log.h"
      8 
      9 #if defined(HAVE_WIN32_IPC)
     10 # define _WIN32_WINNT 0x0500
     11 # include <windows.h>
     12 #else
     13 # include <sys/types.h>
     14 # include <sys/socket.h>
     15 # include <sys/stat.h>
     16 # include <sys/un.h>
     17 #endif
     18 
     19 #include <stdlib.h>
     20 #include <unistd.h>
     21 #include <string.h>
     22 #include <errno.h>
     23 #include <assert.h>
     24 
     25 #ifndef SUN_LEN
     26 /*
     27  * Our current set of ARM header files don't define this.
     28  */
     29 # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
     30                       + strlen ((ptr)->sun_path))
     31 #endif
     32 
     33 using namespace android;
     34 
     35 const unsigned long kInvalidHandle = (unsigned long) -1;
     36 
     37 /*
     38  * Initialize data fields.
     39  */
     40 LocalBiChannel::LocalBiChannel(void)
     41     : mFileName(NULL), mIsListener(false), mHandle(kInvalidHandle)
     42 {
     43 }
     44 
     45 #if defined(HAVE_WIN32_IPC)
     46 /*
     47  * Implementation for Win32, using named pipes.
     48  *
     49  * Cygwin actually supports UNIX-domain sockets, but we want to stuff
     50  * the file handles into a Pipe, which uses HANDLE under Win32.
     51  */
     52 
     53 const int kPipeSize = 4096;
     54 
     55 /*
     56  * Destructor.  If we're the server side, we may need to clean up after
     57  * ourselves.
     58  */
     59 LocalBiChannel::~LocalBiChannel(void)
     60 {
     61     if (mHandle != kInvalidHandle)
     62         CloseHandle((HANDLE)mHandle);
     63 
     64     delete[] mFileName;
     65 }
     66 
     67 /*
     68  * Construct the full path.  The caller must delete[] the return value.
     69  */
     70 static char* makeFilename(const char* name)
     71 {
     72     static const char* kBasePath = "\\\\.\\pipe\\android-";
     73     char* fileName;
     74 
     75     assert(name != NULL && name[0] != '\0');
     76 
     77     fileName = new char[strlen(kBasePath) + strlen(name) + 1];
     78     strcpy(fileName, kBasePath);
     79     strcat(fileName, name);
     80 
     81     return fileName;
     82 }
     83 
     84 /*
     85  * Create a named pipe, so the client has something to connect to.
     86  */
     87 bool LocalBiChannel::create(const char* name)
     88 {
     89     delete[] mFileName;
     90     mFileName = makeFilename(name);
     91 
     92 #if 0
     93     HANDLE hPipe;
     94 
     95     hPipe = CreateNamedPipe(
     96                     mFileName,              // unique pipe name
     97                     PIPE_ACCESS_DUPLEX |    // open mode
     98                         FILE_FLAG_FIRST_PIPE_INSTANCE,
     99                     0,                      // pipe mode (byte, blocking)
    100                     1,                      // max instances
    101                     kPipeSize,              // output buffer
    102                     kPipeSize,              // input buffer
    103                     NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
    104                     NULL);                  // security
    105 
    106     if (hPipe == 0) {
    107         LOG(LOG_ERROR, "lbicomm",
    108             "CreateNamedPipe failed (err=%ld)\n", GetLastError());
    109         return false;
    110     }
    111 
    112     mHandle = (unsigned long) hPipe;
    113 #endif
    114 
    115     return true;
    116 }
    117 
    118 /*
    119  * Attach to an existing named pipe.
    120  */
    121 bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
    122     Pipe** ppWritePipe)
    123 {
    124     HANDLE hPipe, dupHandle;
    125 
    126     delete[] mFileName;
    127     mFileName = makeFilename(name);
    128 
    129     hPipe = CreateFile(
    130                 mFileName,                      // filename
    131                 GENERIC_READ | GENERIC_WRITE,   // access
    132                 0,                              // no sharing
    133                 NULL,                           // security
    134                 OPEN_EXISTING,                  // don't create
    135                 0,                              // attributes
    136                 NULL);                          // template
    137     if (hPipe == INVALID_HANDLE_VALUE) {
    138         LOG(LOG_ERROR, "lbicomm",
    139             "CreateFile on pipe '%s' failed (err=%ld)\n", name, GetLastError());
    140         return false;
    141     }
    142 
    143     assert(mHandle == kInvalidHandle);
    144 
    145     /*
    146      * Set up the pipes.  Use the new handle for one, and a duplicate
    147      * of it for the other, in case we decide to only close one side.
    148      */
    149     *ppReadPipe = new Pipe();
    150     (*ppReadPipe)->createReader((unsigned long) hPipe);
    151 
    152     DuplicateHandle(
    153             GetCurrentProcess(),
    154             hPipe,
    155             GetCurrentProcess(),
    156             &dupHandle,
    157             0,
    158             FALSE,
    159             DUPLICATE_SAME_ACCESS);
    160     *ppWritePipe = new Pipe();
    161     (*ppWritePipe)->createWriter((unsigned long) dupHandle);
    162 
    163     return true;
    164 }
    165 
    166 /*
    167  * Listen for a new connection, discarding any existing connection.
    168  */
    169 bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
    170 {
    171     BOOL connected;
    172     HANDLE hPipe;
    173 
    174     /*
    175      * Create up to 3 instances of the named pipe:
    176      * - currently active connection
    177      * - connection currently being rejected because one is already active
    178      * - a new listener to wait for the next round
    179      */
    180     hPipe = CreateNamedPipe(
    181                     mFileName,              // unique pipe name
    182                     PIPE_ACCESS_DUPLEX      // open mode
    183                         /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
    184                     0,                      // pipe mode (byte, blocking)
    185                     3,                      // max instances
    186                     kPipeSize,              // output buffer
    187                     kPipeSize,              // input buffer
    188                     NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
    189                     NULL);                  // security
    190 
    191     if (hPipe == 0) {
    192         LOG(LOG_ERROR, "lbicomm",
    193             "CreateNamedPipe failed (err=%ld)\n", GetLastError());
    194         return false;
    195     }
    196 
    197     /*
    198      * If a client is already connected to us, this fails with
    199      * ERROR_PIPE_CONNECTED.  It returns success if we had to wait
    200      * a little bit before the connection happens.
    201      */
    202     connected = ConnectNamedPipe(hPipe, NULL) ?
    203         TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
    204 
    205     if (connected) {
    206         /*
    207          * Create the pipes.  Give one a duplicated handle so that,
    208          * when one closes, we don't lose both.
    209          */
    210         HANDLE dupHandle;
    211 
    212         *ppReadPipe = new Pipe();
    213         (*ppReadPipe)->createReader((unsigned long) hPipe);
    214 
    215         DuplicateHandle(
    216                 GetCurrentProcess(),
    217                 hPipe,
    218                 GetCurrentProcess(),
    219                 &dupHandle,
    220                 0,
    221                 FALSE,
    222                 DUPLICATE_SAME_ACCESS);
    223         *ppWritePipe = new Pipe();
    224         (*ppWritePipe)->createWriter((unsigned long) dupHandle);
    225 
    226         return true;
    227     } else {
    228         LOG(LOG_WARN, "lbicomm",
    229             "ConnectNamedPipe failed (err=%ld)\n", GetLastError());
    230 #ifdef HAVE_WIN32_THREADS
    231         Sleep(500); /* 500 ms */
    232 #else
    233         usleep(500000);     // DEBUG DEBUG
    234 #endif
    235         return false;
    236     }
    237 }
    238 
    239 #else
    240 
    241 /*
    242  * Implementation for Linux and Darwin, using UNIX-domain sockets.
    243  */
    244 
    245 /*
    246  * Destructor.  If we're the server side, blow away the socket file.
    247  */
    248 LocalBiChannel::~LocalBiChannel(void)
    249 {
    250     if (mHandle != kInvalidHandle)
    251         close((int) mHandle);
    252 
    253     if (mIsListener && mFileName != NULL) {
    254         LOG(LOG_DEBUG, "lbicomm", "Removing '%s'\n", mFileName);
    255         (void) unlink(mFileName);
    256     }
    257     delete[] mFileName;
    258 }
    259 
    260 /*
    261  * Construct the full path.  The caller must delete[] the return value.
    262  */
    263 static char* makeFilename(const char* name)
    264 {
    265     static const char* kBasePath = "/tmp/android-";
    266     char* fileName;
    267 
    268     assert(name != NULL && name[0] != '\0');
    269 
    270     fileName = new char[strlen(kBasePath) + strlen(name) + 1];
    271     strcpy(fileName, kBasePath);
    272     strcat(fileName, name);
    273 
    274     return fileName;
    275 }
    276 
    277 /*
    278  * Create a UNIX domain socket, carefully removing it if it already
    279  * exists.
    280  */
    281 bool LocalBiChannel::create(const char* name)
    282 {
    283     struct stat sb;
    284     bool result = false;
    285     int sock = -1;
    286     int cc;
    287 
    288     delete[] mFileName;
    289     mFileName = makeFilename(name);
    290 
    291     cc = stat(mFileName, &sb);
    292     if (cc < 0) {
    293         if (errno != ENOENT) {
    294             LOG(LOG_ERROR, "lbicomm",
    295                 "Unable to stat '%s' (errno=%d)\n", mFileName, errno);
    296             goto bail;
    297         }
    298     } else {
    299         /* don't touch it if it's not a socket */
    300         if (!(S_ISSOCK(sb.st_mode))) {
    301             LOG(LOG_ERROR, "lbicomm",
    302                 "File '%s' exists and is not a socket\n", mFileName);
    303             goto bail;
    304         }
    305 
    306         /* remove the cruft */
    307         if (unlink(mFileName) < 0) {
    308             LOG(LOG_ERROR, "lbicomm",
    309                 "Unable to remove '%s' (errno=%d)\n", mFileName, errno);
    310             goto bail;
    311         }
    312     }
    313 
    314     struct sockaddr_un addr;
    315 
    316     sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
    317     if (sock < 0) {
    318         LOG(LOG_ERROR, "lbicomm",
    319             "UNIX domain socket create failed (errno=%d)\n", errno);
    320         goto bail;
    321     }
    322 
    323     /* bind the socket; this creates the file on disk */
    324     strcpy(addr.sun_path, mFileName);    // max 108 bytes
    325     addr.sun_family = AF_UNIX;
    326     cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    327     if (cc < 0) {
    328         LOG(LOG_ERROR, "lbicomm",
    329             "AF_UNIX bind failed for '%s' (errno=%d)\n", mFileName, errno);
    330         goto bail;
    331     }
    332 
    333     mHandle = (unsigned long) sock;
    334     sock = -1;
    335     mIsListener = true;
    336     result = true;
    337 
    338 bail:
    339     if (sock >= 0)
    340         close(sock);
    341     return result;
    342 }
    343 
    344 /*
    345  * Attach to an existing UNIX domain socket.
    346  */
    347 bool LocalBiChannel::attach(const char* name, Pipe** ppReadPipe,
    348     Pipe** ppWritePipe)
    349 {
    350     bool result = false;
    351     int sock = -1;
    352     int cc;
    353 
    354     assert(ppReadPipe != NULL);
    355     assert(ppWritePipe != NULL);
    356 
    357     delete[] mFileName;
    358     mFileName = makeFilename(name);
    359 
    360     struct sockaddr_un addr;
    361 
    362     sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
    363     if (sock < 0) {
    364         LOG(LOG_ERROR, "lbicomm",
    365             "UNIX domain socket create failed (errno=%d)\n", errno);
    366         goto bail;
    367     }
    368 
    369     /* connect to socket; fails if file doesn't exist */
    370     strcpy(addr.sun_path, mFileName);    // max 108 bytes
    371     addr.sun_family = AF_UNIX;
    372     cc = ::connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    373     if (cc < 0) {
    374         // ENOENT means socket file doesn't exist
    375         // ECONNREFUSED means socket exists but nobody is listening
    376         LOG(LOG_ERROR, "lbicomm",
    377             "AF_UNIX connect failed for '%s': %s\n", mFileName,strerror(errno));
    378         goto bail;
    379     }
    380 
    381     /*
    382      * Create the two halves.  We dup() the sock so that closing one side
    383      * does not hose the other.
    384      */
    385     *ppReadPipe = new Pipe();
    386     (*ppReadPipe)->createReader(sock);
    387     *ppWritePipe = new Pipe();
    388     (*ppWritePipe)->createWriter(dup(sock));
    389 
    390     assert(mHandle == kInvalidHandle);
    391     sock = -1;
    392     mIsListener = false;
    393 
    394     result = true;
    395 
    396 bail:
    397     if (sock >= 0)
    398         close(sock);
    399     return result;
    400 }
    401 
    402 /*
    403  * Listen for a new connection.
    404  */
    405 bool LocalBiChannel::listen(Pipe** ppReadPipe, Pipe** ppWritePipe)
    406 {
    407     bool result = false;
    408     struct sockaddr_un from;
    409     socklen_t fromlen;
    410     int sock, lsock;
    411     int cc;
    412 
    413     assert(mHandle != kInvalidHandle);
    414     lsock = (int) mHandle;
    415 
    416     LOG(LOG_DEBUG, "lbicomm", "AF_UNIX listening\n");
    417     cc = ::listen(lsock, 5);
    418     if (cc < 0) {
    419         LOG(LOG_ERROR, "lbicomm", "AF_UNIX listen failed (errno=%d)\n", errno);
    420         goto bail;
    421     }
    422 
    423     fromlen = sizeof(from);     // not SUN_LEN()
    424     sock = ::accept(lsock, (struct sockaddr*) &from, &fromlen);
    425     if (sock < 0) {
    426         LOG(LOG_WARN, "lbicomm", "AF_UNIX accept failed (errno=%d)\n", errno);
    427         goto bail;
    428     }
    429 
    430     /*
    431      * Create the two halves.  We dup() the sock so that closing one side
    432      * does not hose the other.
    433      */
    434     *ppReadPipe = new Pipe();
    435     (*ppReadPipe)->createReader(sock);
    436     *ppWritePipe = new Pipe();
    437     (*ppWritePipe)->createWriter(dup(sock));
    438     result = true;
    439 
    440 bail:
    441     return result;
    442 }
    443 
    444 #endif
    445