Files
android_development/emulator/qemud/qemud.c
David Turner d7484a2ff5 AI 144596: am: CL 144595 Fix the AVD configuration code to support "sdcard.path" in config.ini to indicate an explicit SD Card image file (instead of using the one in the content directory)
Note that this also fix a bug where the SD Card image was not properly locked in the previous implementation.
  Allow the http-proxy support code to actually manage to receive chunked encoding data, instead of complaining needlessly.
  Introduce a new CharBuffer object that is used indirectly by "-radio <hostdevice>" and "-gps <hostdevice>" options
  Add new documentation for QEMUD and CharDriverState objects
  Update the Audio documentation with ASCII graphics (because I'm an artist too)
  Original author: digit
  Merged from: //branches/cupcake/...

Automated import of CL 144596
2009-04-05 14:22:25 -07:00

1701 lines
42 KiB
C

#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <termios.h>
#include <cutils/sockets.h>
/*
* the qemud daemon program is only used within Android as a bridge
* between the emulator program and the emulated system. it really works as
* a simple stream multiplexer that works as follows:
*
* - qemud is started by init following instructions in
* /system/etc/init.goldfish.rc (i.e. it is never started on real devices)
*
* - qemud communicates with the emulator program through a single serial
* port, whose name is passed through a kernel boot parameter
* (e.g. android.qemud=ttyS1)
*
* - qemud binds one unix local stream socket (/dev/socket/qemud, created
* by init through /system/etc/init.goldfish.rc).
*
*
* emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1
* |
* +--> client2
*
* - the special channel index 0 is used by the emulator and qemud only.
* other channel numbers correspond to clients. More specifically,
* connection are created like this:
*
* * the client connects to /dev/socket/qemud
*
* * the client sends the service name through the socket, as
* <service-name>
*
* * qemud creates a "Client" object internally, assigns it an
* internal unique channel number > 0, then sends a connection
* initiation request to the emulator (i.e. through channel 0):
*
* connect:<id>:<name>
*
* where <name> is the service name, and <id> is a 2-hexchar
* number corresponding to the channel number.
*
* * in case of success, the emulator responds through channel 0
* with:
*
* ok:connect:<id>
*
* after this, all messages between the client and the emulator
* are passed in pass-through mode.
*
* * if the emulator refuses the service connection, it will
* send the following through channel 0:
*
* ko:connect:<id>:reason-for-failure
*
* * If the client closes the connection, qemud sends the following
* to the emulator:
*
* disconnect:<id>
*
* The same message is the opposite direction if the emulator
* chooses to close the connection.
*
* * any command sent through channel 0 to the emulator that is
* not properly recognized will be answered by:
*
* ko:unknown command
*
*
* Internally, the daemon maintains a "Client" object for each client
* connection (i.e. accepting socket connection).
*/
/* name of the single control socket used by the daemon */
#define CONTROL_SOCKET_NAME "qemud"
#define DEBUG 1
#define T_ACTIVE 0 /* set to 1 to dump traffic */
#if DEBUG
# define LOG_TAG "qemud"
# include <cutils/log.h>
# define D(...) LOGD(__VA_ARGS__)
#else
# define D(...) ((void)0)
# define T(...) ((void)0)
#endif
#if T_ACTIVE
# define T(...) D(__VA_ARGS__)
#else
# define T(...) ((void)0)
#endif
/** UTILITIES
**/
static void
fatal( const char* fmt, ... )
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "PANIC: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n" );
va_end(args);
exit(1);
}
static void*
xalloc( size_t sz )
{
void* p;
if (sz == 0)
return NULL;
p = malloc(sz);
if (p == NULL)
fatal( "not enough memory" );
return p;
}
#define xnew(p) (p) = xalloc(sizeof(*(p)))
static void*
xalloc0( size_t sz )
{
void* p = xalloc(sz);
memset( p, 0, sz );
return p;
}
#define xnew0(p) (p) = xalloc0(sizeof(*(p)))
#define xfree(p) (free((p)), (p) = NULL)
static void*
xrealloc( void* block, size_t size )
{
void* p = realloc( block, size );
if (p == NULL && size > 0)
fatal( "not enough memory" );
return p;
}
#define xrenew(p,count) (p) = xrealloc((p),sizeof(*(p))*(count))
static int
hex2int( const uint8_t* data, int len )
{
int result = 0;
while (len > 0) {
int c = *data++;
unsigned d;
result <<= 4;
do {
d = (unsigned)(c - '0');
if (d < 10)
break;
d = (unsigned)(c - 'a');
if (d < 6) {
d += 10;
break;
}
d = (unsigned)(c - 'A');
if (d < 6) {
d += 10;
break;
}
return -1;
}
while (0);
result |= d;
len -= 1;
}
return result;
}
static void
int2hex( int value, uint8_t* to, int width )
{
int nn = 0;
static const char hexchars[16] = "0123456789abcdef";
for ( --width; width >= 0; width--, nn++ ) {
to[nn] = hexchars[(value >> (width*4)) & 15];
}
}
static int
fd_read(int fd, void* to, int len)
{
int ret;
do {
ret = read(fd, to, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
static int
fd_write(int fd, const void* from, int len)
{
int ret;
do {
ret = write(fd, from, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
static void
fd_setnonblock(int fd)
{
int ret, flags;
do {
flags = fcntl(fd, F_GETFD);
} while (flags < 0 && errno == EINTR);
if (flags < 0) {
fatal( "%s: could not get flags for fd %d: %s",
__FUNCTION__, fd, strerror(errno) );
}
do {
ret = fcntl(fd, F_SETFD, flags | O_NONBLOCK);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
fatal( "%s: could not set fd %d to non-blocking: %s",
__FUNCTION__, fd, strerror(errno) );
}
}
static int
fd_accept(int fd)
{
struct sockaddr from;
socklen_t fromlen = sizeof(from);
int ret;
do {
ret = accept(fd, &from, &fromlen);
} while (ret < 0 && errno == EINTR);
return ret;
}
/** FD EVENT LOOP
**/
/* A Looper object is used to monitor activity on one or more
* file descriptors (e.g sockets).
*
* - call looper_add() to register a function that will be
* called when events happen on the file descriptor.
*
* - call looper_enable() or looper_disable() to enable/disable
* the set of monitored events for a given file descriptor.
*
* - call looper_del() to unregister a file descriptor.
* this does *not* close the file descriptor.
*
* Note that you can only provide a single function to handle
* all events related to a given file descriptor.
* You can call looper_enable/_disable/_del within a function
* callback.
*/
/* the current implementation uses Linux's epoll facility
* the event mask we use are simply combinations of EPOLLIN
* EPOLLOUT, EPOLLHUP and EPOLLERR
*/
#include <sys/epoll.h>
#define MAX_CHANNELS 16
#define MAX_EVENTS (MAX_CHANNELS+1) /* each channel + the serial fd */
/* the event handler function type, 'user' is a user-specific
* opaque pointer passed to looper_add().
*/
typedef void (*EventFunc)( void* user, int events );
/* bit flags for the LoopHook structure.
*
* HOOK_PENDING means that an event happened on the
* corresponding file descriptor.
*
* HOOK_CLOSING is used to delay-close monitored
* file descriptors.
*/
enum {
HOOK_PENDING = (1 << 0),
HOOK_CLOSING = (1 << 1),
};
/* A LoopHook structure is used to monitor a given
* file descriptor and record its event handler.
*/
typedef struct {
int fd;
int wanted; /* events we are monitoring */
int events; /* events that occured */
int state; /* see HOOK_XXX constants */
void* ev_user; /* user-provided handler parameter */
EventFunc ev_func; /* event handler callback */
} LoopHook;
/* Looper is the main object modeling a looper object
*/
typedef struct {
int epoll_fd;
int num_fds;
int max_fds;
struct epoll_event* events;
LoopHook* hooks;
} Looper;
/* initialize a looper object */
static void
looper_init( Looper* l )
{
l->epoll_fd = epoll_create(4);
l->num_fds = 0;
l->max_fds = 0;
l->events = NULL;
l->hooks = NULL;
}
/* finalize a looper object */
static void
looper_done( Looper* l )
{
xfree(l->events);
xfree(l->hooks);
l->max_fds = 0;
l->num_fds = 0;
close(l->epoll_fd);
l->epoll_fd = -1;
}
/* return the LoopHook corresponding to a given
* monitored file descriptor, or NULL if not found
*/
static LoopHook*
looper_find( Looper* l, int fd )
{
LoopHook* hook = l->hooks;
LoopHook* end = hook + l->num_fds;
for ( ; hook < end; hook++ ) {
if (hook->fd == fd)
return hook;
}
return NULL;
}
/* grow the arrays in the looper object */
static void
looper_grow( Looper* l )
{
int old_max = l->max_fds;
int new_max = old_max + (old_max >> 1) + 4;
int n;
xrenew( l->events, new_max );
xrenew( l->hooks, new_max );
l->max_fds = new_max;
/* now change the handles to all events */
for (n = 0; n < l->num_fds; n++) {
struct epoll_event ev;
LoopHook* hook = l->hooks + n;
ev.events = hook->wanted;
ev.data.ptr = hook;
epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, hook->fd, &ev );
}
}
/* register a file descriptor and its event handler.
* no event mask will be enabled
*/
static void
looper_add( Looper* l, int fd, EventFunc func, void* user )
{
struct epoll_event ev;
LoopHook* hook;
if (l->num_fds >= l->max_fds)
looper_grow(l);
hook = l->hooks + l->num_fds;
hook->fd = fd;
hook->ev_user = user;
hook->ev_func = func;
hook->state = 0;
hook->wanted = 0;
hook->events = 0;
fd_setnonblock(fd);
ev.events = 0;
ev.data.ptr = hook;
epoll_ctl( l->epoll_fd, EPOLL_CTL_ADD, fd, &ev );
l->num_fds += 1;
}
/* unregister a file descriptor and its event handler
*/
static void
looper_del( Looper* l, int fd )
{
LoopHook* hook = looper_find( l, fd );
if (!hook) {
D( "%s: invalid fd: %d", __FUNCTION__, fd );
return;
}
/* don't remove the hook yet */
hook->state |= HOOK_CLOSING;
epoll_ctl( l->epoll_fd, EPOLL_CTL_DEL, fd, NULL );
}
/* enable monitoring of certain events for a file
* descriptor. This adds 'events' to the current
* event mask
*/
static void
looper_enable( Looper* l, int fd, int events )
{
LoopHook* hook = looper_find( l, fd );
if (!hook) {
D("%s: invalid fd: %d", __FUNCTION__, fd );
return;
}
if (events & ~hook->wanted) {
struct epoll_event ev;
hook->wanted |= events;
ev.events = hook->wanted;
ev.data.ptr = hook;
epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
}
}
/* disable monitoring of certain events for a file
* descriptor. This ignores events that are not
* currently enabled.
*/
static void
looper_disable( Looper* l, int fd, int events )
{
LoopHook* hook = looper_find( l, fd );
if (!hook) {
D("%s: invalid fd: %d", __FUNCTION__, fd );
return;
}
if (events & hook->wanted) {
struct epoll_event ev;
hook->wanted &= ~events;
ev.events = hook->wanted;
ev.data.ptr = hook;
epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
}
}
/* wait until an event occurs on one of the registered file
* descriptors. Only returns in case of error !!
*/
static void
looper_loop( Looper* l )
{
for (;;) {
int n, count;
do {
count = epoll_wait( l->epoll_fd, l->events, l->num_fds, -1 );
} while (count < 0 && errno == EINTR);
if (count < 0) {
D("%s: error: %s", __FUNCTION__, strerror(errno) );
return;
}
if (count == 0) {
D("%s: huh ? epoll returned count=0", __FUNCTION__);
continue;
}
/* mark all pending hooks */
for (n = 0; n < count; n++) {
LoopHook* hook = l->events[n].data.ptr;
hook->state = HOOK_PENDING;
hook->events = l->events[n].events;
}
/* execute hook callbacks. this may change the 'hooks'
* and 'events' array, as well as l->num_fds, so be careful */
for (n = 0; n < l->num_fds; n++) {
LoopHook* hook = l->hooks + n;
if (hook->state & HOOK_PENDING) {
hook->state &= ~HOOK_PENDING;
hook->ev_func( hook->ev_user, hook->events );
}
}
/* now remove all the hooks that were closed by
* the callbacks */
for (n = 0; n < l->num_fds;) {
LoopHook* hook = l->hooks + n;
if (!(hook->state & HOOK_CLOSING)) {
n++;
continue;
}
hook[0] = l->hooks[l->num_fds-1];
l->num_fds -= 1;
}
}
}
#if T_ACTIVE
char*
quote( const void* data, int len )
{
const char* p = data;
const char* end = p + len;
int count = 0;
int phase = 0;
static char* buff = NULL;
for (phase = 0; phase < 2; phase++) {
if (phase != 0) {
xfree(buff);
buff = xalloc(count+1);
}
count = 0;
for (p = data; p < end; p++) {
int c = *p;
if (c == '\\') {
if (phase != 0) {
buff[count] = buff[count+1] = '\\';
}
count += 2;
continue;
}
if (c >= 32 && c < 127) {
if (phase != 0)
buff[count] = c;
count += 1;
continue;
}
if (c == '\t') {
if (phase != 0) {
memcpy(buff+count, "<TAB>", 5);
}
count += 5;
continue;
}
if (c == '\n') {
if (phase != 0) {
memcpy(buff+count, "<LN>", 4);
}
count += 4;
continue;
}
if (c == '\r') {
if (phase != 0) {
memcpy(buff+count, "<CR>", 4);
}
count += 4;
continue;
}
if (phase != 0) {
buff[count+0] = '\\';
buff[count+1] = 'x';
buff[count+2] = "0123456789abcdef"[(c >> 4) & 15];
buff[count+3] = "0123456789abcdef"[ (c) & 15];
}
count += 4;
}
}
buff[count] = 0;
return buff;
}
#endif /* T_ACTIVE */
/** PACKETS
**
** We need a way to buffer data before it can be sent to the
** corresponding file descriptor. We use linked list of Packet
** objects to do this.
**/
typedef struct Packet Packet;
#define MAX_PAYLOAD 4000
struct Packet {
Packet* next;
int len;
int channel;
uint8_t data[ MAX_PAYLOAD ];
};
/* we expect to alloc/free a lot of packets during
* operations so use a single linked list of free packets
* to keep things speedy and simple.
*/
static Packet* _free_packets;
/* Allocate a packet */
static Packet*
packet_alloc(void)
{
Packet* p = _free_packets;
if (p != NULL) {
_free_packets = p->next;
} else {
xnew(p);
}
p->next = NULL;
p->len = 0;
p->channel = -1;
return p;
}
/* Release a packet. This takes the address of a packet
* pointer that will be set to NULL on exit (avoids
* referencing dangling pointers in case of bugs)
*/
static void
packet_free( Packet* *ppacket )
{
Packet* p = *ppacket;
if (p) {
p->next = _free_packets;
_free_packets = p;
*ppacket = NULL;
}
}
/** PACKET RECEIVER
**
** Simple abstraction for something that can receive a packet
** from a FDHandler (see below) or something else.
**
** Send a packet to it with 'receiver_post'
**
** Call 'receiver_close' to indicate that the corresponding
** packet source was closed.
**/
typedef void (*PostFunc) ( void* user, Packet* p );
typedef void (*CloseFunc)( void* user );
typedef struct {
PostFunc post;
CloseFunc close;
void* user;
} Receiver;
/* post a packet to a receiver. Note that this transfers
* ownership of the packet to the receiver.
*/
static __inline__ void
receiver_post( Receiver* r, Packet* p )
{
if (r->post)
r->post( r->user, p );
else
packet_free(&p);
}
/* tell a receiver the packet source was closed.
* this will also prevent further posting to the
* receiver.
*/
static __inline__ void
receiver_close( Receiver* r )
{
if (r->close) {
r->close( r->user );
r->close = NULL;
}
r->post = NULL;
}
/** FD HANDLERS
**
** these are smart listeners that send incoming packets to a receiver
** and can queue one or more outgoing packets and send them when
** possible to the FD.
**
** note that we support clean shutdown of file descriptors,
** i.e. we try to send all outgoing packets before destroying
** the FDHandler.
**/
typedef struct FDHandler FDHandler;
typedef struct FDHandlerList FDHandlerList;
struct FDHandler {
int fd;
FDHandlerList* list;
char closing;
Receiver receiver[1];
/* queue of outgoing packets */
int out_pos;
Packet* out_first;
Packet** out_ptail;
FDHandler* next;
FDHandler** pref;
};
struct FDHandlerList {
/* the looper that manages the fds */
Looper* looper;
/* list of active FDHandler objects */
FDHandler* active;
/* list of closing FDHandler objects.
* these are waiting to push their
* queued packets to the fd before
* freeing themselves.
*/
FDHandler* closing;
};
/* remove a FDHandler from its current list */
static void
fdhandler_remove( FDHandler* f )
{
f->pref[0] = f->next;
if (f->next)
f->next->pref = f->pref;
}
/* add a FDHandler to a given list */
static void
fdhandler_prepend( FDHandler* f, FDHandler** list )
{
f->next = list[0];
f->pref = list;
list[0] = f;
if (f->next)
f->next->pref = &f->next;
}
/* initialize a FDHandler list */
static void
fdhandler_list_init( FDHandlerList* list, Looper* looper )
{
list->looper = looper;
list->active = NULL;
list->closing = NULL;
}
/* close a FDHandler (and free it). Note that this will not
* perform a graceful shutdown, i.e. all packets in the
* outgoing queue will be immediately free.
*
* this *will* notify the receiver that the file descriptor
* was closed.
*
* you should call fdhandler_shutdown() if you want to
* notify the FDHandler that its packet source is closed.
*/
static void
fdhandler_close( FDHandler* f )
{
/* notify receiver */
receiver_close(f->receiver);
/* remove the handler from its list */
fdhandler_remove(f);
/* get rid of outgoing packet queue */
if (f->out_first != NULL) {
Packet* p;
while ((p = f->out_first) != NULL) {
f->out_first = p->next;
packet_free(&p);
}
}
/* get rid of file descriptor */
if (f->fd >= 0) {
looper_del( f->list->looper, f->fd );
close(f->fd);
f->fd = -1;
}
f->list = NULL;
xfree(f);
}
/* Ask the FDHandler to cleanly shutdown the connection,
* i.e. send any pending outgoing packets then auto-free
* itself.
*/
static void
fdhandler_shutdown( FDHandler* f )
{
if (f->out_first != NULL && !f->closing)
{
/* move the handler to the 'closing' list */
f->closing = 1;
fdhandler_remove(f);
fdhandler_prepend(f, &f->list->closing);
/* notify the receiver that we're closing */
receiver_close(f->receiver);
return;
}
fdhandler_close(f);
}
/* Enqueue a new packet that the FDHandler will
* send through its file descriptor.
*/
static void
fdhandler_enqueue( FDHandler* f, Packet* p )
{
Packet* first = f->out_first;
p->next = NULL;
f->out_ptail[0] = p;
f->out_ptail = &p->next;
if (first == NULL) {
f->out_pos = 0;
looper_enable( f->list->looper, f->fd, EPOLLOUT );
}
}
/* FDHandler file descriptor event callback for read/write ops */
static void
fdhandler_event( FDHandler* f, int events )
{
int len;
/* in certain cases, it's possible to have both EPOLLIN and
* EPOLLHUP at the same time. This indicates that there is incoming
* data to read, but that the connection was nonetheless closed
* by the sender. Be sure to read the data before closing
* the receiver to avoid packet loss.
*/
if (events & EPOLLIN) {
Packet* p = packet_alloc();
int len;
if ((len = fd_read(f->fd, p->data, MAX_PAYLOAD)) < 0) {
D("%s: can't recv: %s", __FUNCTION__, strerror(errno));
packet_free(&p);
} else if (len > 0) {
p->len = len;
p->channel = -101; /* special debug value, not used */
receiver_post( f->receiver, p );
}
}
if (events & (EPOLLHUP|EPOLLERR)) {
/* disconnection */
D("%s: disconnect on fd %d", __FUNCTION__, f->fd);
fdhandler_close(f);
return;
}
if (events & EPOLLOUT && f->out_first) {
Packet* p = f->out_first;
int avail, len;
avail = p->len - f->out_pos;
if ((len = fd_write(f->fd, p->data + f->out_pos, avail)) < 0) {
D("%s: can't send: %s", __FUNCTION__, strerror(errno));
} else {
f->out_pos += len;
if (f->out_pos >= p->len) {
f->out_pos = 0;
f->out_first = p->next;
packet_free(&p);
if (f->out_first == NULL) {
f->out_ptail = &f->out_first;
looper_disable( f->list->looper, f->fd, EPOLLOUT );
}
}
}
}
}
/* Create a new FDHandler that monitors read/writes */
static FDHandler*
fdhandler_new( int fd,
FDHandlerList* list,
Receiver* receiver )
{
FDHandler* f = xalloc0(sizeof(*f));
f->fd = fd;
f->list = list;
f->receiver[0] = receiver[0];
f->out_first = NULL;
f->out_ptail = &f->out_first;
f->out_pos = 0;
fdhandler_prepend(f, &list->active);
looper_add( list->looper, fd, (EventFunc) fdhandler_event, f );
looper_enable( list->looper, fd, EPOLLIN );
return f;
}
/* event callback function to monitor accepts() on server sockets.
* the convention used here is that the receiver will receive a
* dummy packet with the new client socket in p->channel
*/
static void
fdhandler_accept_event( FDHandler* f, int events )
{
if (events & EPOLLIN) {
/* this is an accept - send a dummy packet to the receiver */
Packet* p = packet_alloc();
D("%s: accepting on fd %d", __FUNCTION__, f->fd);
p->data[0] = 1;
p->len = 1;
p->channel = fd_accept(f->fd);
if (p->channel < 0) {
D("%s: accept failed ?: %s", __FUNCTION__, strerror(errno));
packet_free(&p);
return;
}
receiver_post( f->receiver, p );
}
if (events & (EPOLLHUP|EPOLLERR)) {
/* disconnecting !! */
D("%s: closing accept fd %d", __FUNCTION__, f->fd);
fdhandler_close(f);
return;
}
}
/* Create a new FDHandler used to monitor new connections on a
* server socket. The receiver must expect the new connection
* fd in the 'channel' field of a dummy packet.
*/
static FDHandler*
fdhandler_new_accept( int fd,
FDHandlerList* list,
Receiver* receiver )
{
FDHandler* f = xalloc0(sizeof(*f));
f->fd = fd;
f->list = list;
f->receiver[0] = receiver[0];
fdhandler_prepend(f, &list->active);
looper_add( list->looper, fd, (EventFunc) fdhandler_accept_event, f );
looper_enable( list->looper, fd, EPOLLIN );
listen( fd, 5 );
return f;
}
/** SERIAL CONNECTION STATE
**
** The following is used to handle the framing protocol
** used on the serial port connection.
**/
/* each packet is made of a 6 byte header followed by a payload
* the header looks like:
*
* offset size description
* 0 2 a 2-byte hex string for the channel number
* 4 4 a 4-char hex string for the size of the payload
* 6 n the payload itself
*/
#define HEADER_SIZE 6
#define CHANNEL_OFFSET 0
#define LENGTH_OFFSET 2
#define CHANNEL_SIZE 2
#define LENGTH_SIZE 4
#define CHANNEL_CONTROL 0
/* The Serial object receives data from the serial port,
* extracts the payload size and channel index, then sends
* the resulting messages as a packet to a generic receiver.
*
* You can also use serial_send to send a packet through
* the serial port.
*/
typedef struct Serial {
FDHandler* fdhandler; /* used to monitor serial port fd */
Receiver receiver[1]; /* send payload there */
int in_len; /* current bytes in input packet */
int in_datalen; /* payload size, or 0 when reading header */
int in_channel; /* extracted channel number */
Packet* in_packet; /* used to read incoming packets */
} Serial;
/* a callback called when the serial port's fd is closed */
static void
serial_fd_close( Serial* s )
{
fatal("unexpected serial port close !!");
}
static void
serial_dump( Packet* p, const char* funcname )
{
T("%s: %03d bytes: '%s'",
funcname, p->len, quote(p->data, p->len));
}
/* a callback called when a packet arrives from the serial port's FDHandler.
*
* This will essentially parse the header, extract the channel number and
* the payload size and store them in 'in_datalen' and 'in_channel'.
*
* After that, the payload is sent to the receiver once completed.
*/
static void
serial_fd_receive( Serial* s, Packet* p )
{
int rpos = 0, rcount = p->len;
Packet* inp = s->in_packet;
int inpos = s->in_len;
serial_dump( p, __FUNCTION__ );
while (rpos < rcount)
{
int avail = rcount - rpos;
/* first, try to read the header */
if (s->in_datalen == 0) {
int wanted = HEADER_SIZE - inpos;
if (avail > wanted)
avail = wanted;
memcpy( inp->data + inpos, p->data + rpos, avail );
inpos += avail;
rpos += avail;
if (inpos == HEADER_SIZE) {
s->in_datalen = hex2int( inp->data + LENGTH_OFFSET, LENGTH_SIZE );
s->in_channel = hex2int( inp->data + CHANNEL_OFFSET, CHANNEL_SIZE );
if (s->in_datalen <= 0) {
D("ignoring %s packet from serial port",
s->in_datalen ? "empty" : "malformed");
s->in_datalen = 0;
}
//D("received %d bytes packet for channel %d", s->in_datalen, s->in_channel);
inpos = 0;
}
}
else /* then, populate the packet itself */
{
int wanted = s->in_datalen - inpos;
if (avail > wanted)
avail = wanted;
memcpy( inp->data + inpos, p->data + rpos, avail );
inpos += avail;
rpos += avail;
if (inpos == s->in_datalen) {
if (s->in_channel < 0) {
D("ignoring %d bytes addressed to channel %d",
inpos, s->in_channel);
} else {
inp->len = inpos;
inp->channel = s->in_channel;
receiver_post( s->receiver, inp );
s->in_packet = inp = packet_alloc();
}
s->in_datalen = 0;
inpos = 0;
}
}
}
s->in_len = inpos;
packet_free(&p);
}
/* send a packet to the serial port.
* this assumes that p->len and p->channel contain the payload's
* size and channel and will add the appropriate header.
*/
static void
serial_send( Serial* s, Packet* p )
{
Packet* h = packet_alloc();
//D("sending to serial %d bytes from channel %d: '%.*s'", p->len, p->channel, p->len, p->data);
/* insert a small header before this packet */
h->len = HEADER_SIZE;
int2hex( p->len, h->data + LENGTH_OFFSET, LENGTH_SIZE );
int2hex( p->channel, h->data + CHANNEL_OFFSET, CHANNEL_SIZE );
serial_dump( h, __FUNCTION__ );
serial_dump( p, __FUNCTION__ );
fdhandler_enqueue( s->fdhandler, h );
fdhandler_enqueue( s->fdhandler, p );
}
/* initialize serial reader */
static void
serial_init( Serial* s,
int fd,
FDHandlerList* list,
Receiver* receiver )
{
Receiver recv;
recv.user = s;
recv.post = (PostFunc) serial_fd_receive;
recv.close = (CloseFunc) serial_fd_close;
s->receiver[0] = receiver[0];
s->fdhandler = fdhandler_new( fd, list, &recv );
s->in_len = 0;
s->in_datalen = 0;
s->in_channel = 0;
s->in_packet = packet_alloc();
}
/** CLIENTS
**/
typedef struct Client Client;
typedef struct Multiplexer Multiplexer;
/* A Client object models a single qemud client socket
* connection in the emulated system.
*
* the client first sends the name of the system service
* it wants to contact (no framing), then waits for a 2
* byte answer from qemud.
*
* the answer is either "OK" or "KO" to indicate
* success or failure.
*
* In case of success, the client can send messages
* to the service.
*
* In case of failure, it can disconnect or try sending
* the name of another service.
*/
struct Client {
Client* next;
Client** pref;
int channel;
char registered;
FDHandler* fdhandler;
Multiplexer* multiplexer;
};
struct Multiplexer {
Client* clients;
int last_channel;
Serial serial[1];
Looper looper[1];
FDHandlerList fdhandlers[1];
};
static int multiplexer_open_channel( Multiplexer* mult, Packet* p );
static void multiplexer_close_channel( Multiplexer* mult, int channel );
static void multiplexer_serial_send( Multiplexer* mult, int channel, Packet* p );
static void
client_dump( Client* c, Packet* p, const char* funcname )
{
T("%s: client %p (%d): %3d bytes: '%s'",
funcname, c, c->fdhandler->fd,
p->len, quote(p->data, p->len));
}
/* destroy a client */
static void
client_free( Client* c )
{
/* remove from list */
c->pref[0] = c->next;
if (c->next)
c->next->pref = c->pref;
c->channel = -1;
c->registered = 0;
/* gently ask the FDHandler to shutdown to
* avoid losing queued outgoing packets */
if (c->fdhandler != NULL) {
fdhandler_shutdown(c->fdhandler);
c->fdhandler = NULL;
}
xfree(c);
}
/* a function called when a client socket receives data */
static void
client_fd_receive( Client* c, Packet* p )
{
client_dump(c, p, __FUNCTION__);
if (c->registered) {
/* the client is registered, just send the
* data through the serial port
*/
multiplexer_serial_send(c->multiplexer, c->channel, p);
return;
}
if (c->channel > 0) {
/* the client is waiting registration results.
* this should not happen because the client
* should wait for our 'ok' or 'ko'.
* close the connection.
*/
D("%s: bad client sending data before end of registration",
__FUNCTION__);
BAD_CLIENT:
packet_free(&p);
client_free(c);
return;
}
/* the client hasn't registered a service yet,
* so this must be the name of a service, call
* the multiplexer to start registration for
* it.
*/
D("%s: attempting registration for service '%.*s'",
__FUNCTION__, p->len, p->data);
c->channel = multiplexer_open_channel(c->multiplexer, p);
if (c->channel < 0) {
D("%s: service name too long", __FUNCTION__);
goto BAD_CLIENT;
}
D("%s: -> received channel id %d", __FUNCTION__, c->channel);
packet_free(&p);
}
/* a function called when the client socket is closed. */
static void
client_fd_close( Client* c )
{
T("%s: client %p (%d)", __FUNCTION__, c, c->fdhandler->fd);
/* no need to shutdown the FDHandler */
c->fdhandler = NULL;
/* tell the emulator we're out */
if (c->channel > 0)
multiplexer_close_channel(c->multiplexer, c->channel);
/* free the client */
client_free(c);
}
/* a function called when the multiplexer received a registration
* response from the emulator for a given client.
*/
static void
client_registration( Client* c, int registered )
{
Packet* p = packet_alloc();
/* sends registration status to client */
if (!registered) {
D("%s: registration failed for client %d", __FUNCTION__, c->channel);
memcpy( p->data, "KO", 2 );
p->len = 2;
} else {
D("%s: registration succeeded for client %d", __FUNCTION__, c->channel);
memcpy( p->data, "OK", 2 );
p->len = 2;
}
client_dump(c, p, __FUNCTION__);
fdhandler_enqueue(c->fdhandler, p);
/* now save registration state
*/
c->registered = registered;
if (!registered) {
/* allow the client to try registering another service */
c->channel = -1;
}
}
/* send data to a client */
static void
client_send( Client* c, Packet* p )
{
client_dump(c, p, __FUNCTION__);
fdhandler_enqueue(c->fdhandler, p);
}
/* Create new client socket handler */
static Client*
client_new( Multiplexer* mult,
int fd,
FDHandlerList* pfdhandlers,
Client** pclients )
{
Client* c;
Receiver recv;
xnew(c);
c->multiplexer = mult;
c->next = NULL;
c->pref = &c->next;
c->channel = -1;
c->registered = 0;
recv.user = c;
recv.post = (PostFunc) client_fd_receive;
recv.close = (CloseFunc) client_fd_close;
c->fdhandler = fdhandler_new( fd, pfdhandlers, &recv );
/* add to client list */
c->next = *pclients;
c->pref = pclients;
*pclients = c;
if (c->next)
c->next->pref = &c->next;
return c;
}
/** GLOBAL MULTIPLEXER
**/
/* find a client by its channel */
static Client*
multiplexer_find_client( Multiplexer* mult, int channel )
{
Client* c = mult->clients;
for ( ; c != NULL; c = c->next ) {
if (c->channel == channel)
return c;
}
return NULL;
}
/* handle control messages coming from the serial port
* on CONTROL_CHANNEL.
*/
static void
multiplexer_handle_control( Multiplexer* mult, Packet* p )
{
/* connection registration success */
if (p->len == 13 && !memcmp(p->data, "ok:connect:", 11)) {
int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
/* note that 'client' can be NULL if the corresponding
* socket was closed before the emulator response arrived.
*/
if (client != NULL) {
client_registration(client, 1);
}
goto EXIT;
}
/* connection registration failure */
if (p->len >= 13 && !memcmp(p->data, "ko:connect:",11)) {
int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
if (client != NULL)
client_registration(client, 0);
goto EXIT;
}
/* emulator-induced client disconnection */
if (p->len == 13 && !memcmp(p->data, "disconnect:",11)) {
int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
if (client != NULL)
client_free(client);
goto EXIT;
}
D("%s: unknown control message: '%.*s'",
__FUNCTION__, p->len, p->data);
EXIT:
packet_free(&p);
}
/* a function called when an incoming packet comes from the serial port */
static void
multiplexer_serial_receive( Multiplexer* mult, Packet* p )
{
Client* client;
if (p->channel == CHANNEL_CONTROL) {
multiplexer_handle_control(mult, p);
return;
}
client = multiplexer_find_client(mult, p->channel);
if (client != NULL) {
client_send(client, p);
return;
}
D("%s: discarding packet for unknown channel %d", __FUNCTION__, p->channel);
packet_free(&p);
}
/* a function called when the serial reader closes */
static void
multiplexer_serial_close( Multiplexer* mult )
{
fatal("unexpected close of serial reader");
}
/* a function called to send a packet to the serial port */
static void
multiplexer_serial_send( Multiplexer* mult, int channel, Packet* p )
{
p->channel = channel;
serial_send( mult->serial, p );
}
/* a function used by a client to allocate a new channel id and
* ask the emulator to open it. 'service' must be a packet containing
* the name of the service in its payload.
*
* returns -1 if the service name is too long.
*
* notice that client_registration() will be called later when
* the answer arrives.
*/
static int
multiplexer_open_channel( Multiplexer* mult, Packet* service )
{
Packet* p = packet_alloc();
int len, channel;
/* find a free channel number, assume we don't have many
* clients here. */
{
Client* c;
TRY_AGAIN:
channel = (++mult->last_channel) & 0xff;
for (c = mult->clients; c != NULL; c = c->next)
if (c->channel == channel)
goto TRY_AGAIN;
}
len = snprintf((char*)p->data, sizeof p->data, "connect:%.*s:%02x", service->len, service->data, channel);
if (len >= (int)sizeof(p->data)) {
D("%s: weird, service name too long (%d > %d)", __FUNCTION__, len, sizeof(p->data));
packet_free(&p);
return -1;
}
p->channel = CHANNEL_CONTROL;
p->len = len;
serial_send(mult->serial, p);
return channel;
}
/* used to tell the emulator a channel was closed by a client */
static void
multiplexer_close_channel( Multiplexer* mult, int channel )
{
Packet* p = packet_alloc();
int len = snprintf((char*)p->data, sizeof(p->data), "disconnect:%02x", channel);
if (len > (int)sizeof(p->data)) {
/* should not happen */
return;
}
p->channel = CHANNEL_CONTROL;
p->len = len;
serial_send(mult->serial, p);
}
/* this function is used when a new connection happens on the control
* socket.
*/
static void
multiplexer_control_accept( Multiplexer* m, Packet* p )
{
/* the file descriptor for the new socket connection is
* in p->channel. See fdhandler_accept_event() */
int fd = p->channel;
Client* client = client_new( m, fd, m->fdhandlers, &m->clients );
D("created client %p listening on fd %d", client, fd);
/* free dummy packet */
packet_free(&p);
}
static void
multiplexer_control_close( Multiplexer* m )
{
fatal("unexpected multiplexer control close");
}
static void
multiplexer_init( Multiplexer* m, const char* serial_dev )
{
int fd, control_fd;
Receiver recv;
/* initialize looper and fdhandlers list */
looper_init( m->looper );
fdhandler_list_init( m->fdhandlers, m->looper );
/* open the serial port */
do {
fd = open(serial_dev, O_RDWR);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
fatal( "%s: could not open '%s': %s", __FUNCTION__, serial_dev,
strerror(errno) );
}
// disable echo on serial lines
if ( !memcmp( serial_dev, "/dev/ttyS", 9 ) ) {
struct termios ios;
tcgetattr( fd, &ios );
ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
tcsetattr( fd, TCSANOW, &ios );
}
/* initialize the serial reader/writer */
recv.user = m;
recv.post = (PostFunc) multiplexer_serial_receive;
recv.close = (CloseFunc) multiplexer_serial_close;
serial_init( m->serial, fd, m->fdhandlers, &recv );
/* open the qemud control socket */
recv.user = m;
recv.post = (PostFunc) multiplexer_control_accept;
recv.close = (CloseFunc) multiplexer_control_close;
fd = android_get_control_socket(CONTROL_SOCKET_NAME);
if (fd < 0) {
fatal("couldn't get fd for control socket '%s'", CONTROL_SOCKET_NAME);
}
fdhandler_new_accept( fd, m->fdhandlers, &recv );
/* initialize clients list */
m->clients = NULL;
}
/** MAIN LOOP
**/
static Multiplexer _multiplexer[1];
int main( void )
{
Multiplexer* m = _multiplexer;
/* extract the name of our serial device from the kernel
* boot options that are stored in /proc/cmdline
*/
#define KERNEL_OPTION "android.qemud="
{
char buff[1024];
int fd, len;
char* p;
char* q;
fd = open( "/proc/cmdline", O_RDONLY );
if (fd < 0) {
D("%s: can't open /proc/cmdline !!: %s", __FUNCTION__,
strerror(errno));
exit(1);
}
len = fd_read( fd, buff, sizeof(buff)-1 );
close(fd);
if (len < 0) {
D("%s: can't read /proc/cmdline: %s", __FUNCTION__,
strerror(errno));
exit(1);
}
buff[len] = 0;
p = strstr( buff, KERNEL_OPTION );
if (p == NULL) {
D("%s: can't find '%s' in /proc/cmdline",
__FUNCTION__, KERNEL_OPTION );
exit(1);
}
p += sizeof(KERNEL_OPTION)-1; /* skip option */
q = p;
while ( *q && *q != ' ' && *q != '\t' )
q += 1;
snprintf( buff, sizeof(buff), "/dev/%.*s", q-p, p );
multiplexer_init( m, buff );
}
D( "entering main loop");
looper_loop( m->looper );
D( "unexpected termination !!" );
return 0;
}