OSDN Git Service

Merge "Update samples to use new getMotionRanges() API." into honeycomb-mr1
[android-x86/development.git] / simulator / app / LocalBiChannel.cpp
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