Its purpose is to contact the 'boot-properties' service during the
boot process and to set the list of system properties read from the
emulator program through it.
It must be launched from /system/etc/init.goldfish.rc as root.
It depends on the 'boot-properties' service being implemented by the
emulator (in a later patch). If not available, it will simply timeout.
This also fixes a potential crash in qemud which can happen in the following case:
- client sends a command to a service
- the service responds with one or more message
- the service immediately closes the connection before the client can read the data
what happened is that the FDHandler object was buffering out-going packets to the
client, but the Multiplexer acted on the disconnection immediately. This resulted
in:
- client_free() being called
- fdhandler_shutdown() being called
- the FDHandler being placed on the 'closing' list to be able to send
buffered packets later. And 'receiver_close' being called
- client_fd_close() being called, which would call client_free()
recursively
- the second client_free() call would free the object
- the first client_free() call would try to free the object again
- ==> dlmalloc() detects a double-free and immediately aborts.
the fixes simply avoids the calle to receiver_close() in fdhandler_shutdown()
since this function is called from an explicit shutdown request from the multiplexer,
it doesn't correspond to the case where the client has closed the connection itself.
1717 lines
42 KiB
C
1717 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);
|
|
|
|
/* prevent later fdhandler_close() to
|
|
* call the receiver's close.
|
|
*/
|
|
f->receiver->close = NULL;
|
|
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);
|
|
} else {
|
|
D("%s: NULL client: '%.*s'", __FUNCTION__, p->len, p->data+11);
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* A message that begins with "X00" is a probe sent by
|
|
* the emulator used to detect which version of qemud it runs
|
|
* against (in order to detect 1.0/1.1 system images. Just
|
|
* silently ignore it there instead of printing an error
|
|
* message.
|
|
*/
|
|
if (p->len >= 3 && !memcmp(p->data,"X00",3)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
D("%s: unknown control message (%d bytes): '%.*s'",
|
|
__FUNCTION__, p->len, 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;
|
|
|
|
T("%s: channel=%d '%.*s'", __FUNCTION__, p->channel, p->len, p->data);
|
|
|
|
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;
|
|
}
|