diff options
Diffstat (limited to 'minadbd')
-rw-r--r-- | minadbd/Android.mk | 32 | ||||
-rw-r--r-- | minadbd/README.txt | 39 | ||||
-rw-r--r-- | minadbd/adb.c | 414 | ||||
-rw-r--r-- | minadbd/adb.h | 418 | ||||
-rw-r--r-- | minadbd/fdevent.c | 695 | ||||
-rw-r--r-- | minadbd/fdevent.h | 83 | ||||
-rw-r--r-- | minadbd/mutex_list.h | 26 | ||||
-rw-r--r-- | minadbd/services.c | 162 | ||||
-rw-r--r-- | minadbd/sockets.c | 730 | ||||
-rw-r--r-- | minadbd/sysdeps.h | 494 | ||||
-rw-r--r-- | minadbd/transport.c | 824 | ||||
-rw-r--r-- | minadbd/transport.h | 26 | ||||
-rw-r--r-- | minadbd/transport_usb.c | 121 | ||||
-rw-r--r-- | minadbd/usb_linux_client.c | 157 | ||||
-rw-r--r-- | minadbd/utils.c | 106 | ||||
-rw-r--r-- | minadbd/utils.h | 68 |
16 files changed, 4395 insertions, 0 deletions
diff --git a/minadbd/Android.mk b/minadbd/Android.mk new file mode 100644 index 000000000..5a4de6828 --- /dev/null +++ b/minadbd/Android.mk @@ -0,0 +1,32 @@ +# Copyright 2005 The Android Open Source Project +# +# Android.mk for adb +# + +LOCAL_PATH:= $(call my-dir) + +# minadbd library +# ========================================================= + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb.c \ + fdevent.c \ + transport.c \ + transport_usb.c \ + sockets.c \ + services.c \ + usb_linux_client.c \ + utils.c + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +LOCAL_MODULE := libminadbd + +LOCAL_STATIC_LIBRARIES := libcutils libc +include $(BUILD_STATIC_LIBRARY) + + + diff --git a/minadbd/README.txt b/minadbd/README.txt new file mode 100644 index 000000000..c9df484c3 --- /dev/null +++ b/minadbd/README.txt @@ -0,0 +1,39 @@ +The contents of this directory are copied from system/core/adb, with +the following changes: + +adb.c + - much support for host mode and non-linux OS's stripped out; this + version only runs as adbd on the device. + - always setuid/setgid's itself to the shell user + - only uses USB transport + - references to JDWP removed + - main() removed + - all ADB_HOST and win32 code removed + - removed listeners, logging code, background server (for host) + +adb.h + - minor changes to match adb.c changes + +sockets.c + - references to JDWP removed + - ADB_HOST code removed + +services.c + - all services except echo_service (which is commented out) removed + - all host mode support removed + - sideload_service() added; this is the only service supported. It + receives a single blob of data, writes it to a fixed filename, and + makes the process exit. + +Android.mk + - only builds in adbd mode; builds as static library instead of a + standalone executable. + +sysdeps.h + - changes adb_creat() to use O_NOFOLLOW + +transport.c + - removed ADB_HOST code + +transport_usb.c + - removed ADB_HOST code diff --git a/minadbd/adb.c b/minadbd/adb.c new file mode 100644 index 000000000..0e8fd2a7e --- /dev/null +++ b/minadbd/adb.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TRACE_TAG TRACE_ADB + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> + +#include "sysdeps.h" +#include "adb.h" + +#include <private/android_filesystem_config.h> +#include <linux/capability.h> +#include <linux/prctl.h> + +#if ADB_TRACE +ADB_MUTEX_DEFINE( D_lock ); +#endif + +int HOST = 0; + +static const char *adb_device_banner = "sideload"; + +void fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +void fatal_errno(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: %s: ", strerror(errno)); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +int adb_trace_mask; + +/* read a comma/space/colum/semi-column separated list of tags + * from the ADB_TRACE environment variable and build the trace + * mask from it. note that '1' and 'all' are special cases to + * enable all tracing + */ +void adb_trace_init(void) +{ + const char* p = getenv("ADB_TRACE"); + const char* q; + + static const struct { + const char* tag; + int flag; + } tags[] = { + { "1", 0 }, + { "all", 0 }, + { "adb", TRACE_ADB }, + { "sockets", TRACE_SOCKETS }, + { "packets", TRACE_PACKETS }, + { "rwx", TRACE_RWX }, + { "usb", TRACE_USB }, + { "sync", TRACE_SYNC }, + { "sysdeps", TRACE_SYSDEPS }, + { "transport", TRACE_TRANSPORT }, + { "jdwp", TRACE_JDWP }, + { "services", TRACE_SERVICES }, + { NULL, 0 } + }; + + if (p == NULL) + return; + + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; + + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; + + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); + + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } +} + + +apacket *get_apacket(void) +{ + apacket *p = malloc(sizeof(apacket)); + if(p == 0) fatal("failed to allocate an apacket"); + memset(p, 0, sizeof(apacket) - MAX_PAYLOAD); + return p; +} + +void put_apacket(apacket *p) +{ + free(p); +} + +void handle_online(void) +{ + D("adb: online\n"); +} + +void handle_offline(atransport *t) +{ + D("adb: offline\n"); + //Close the associated usb + run_transport_disconnects(t); +} + +#if TRACE_PACKETS +#define DUMPMAX 32 +void print_packet(const char *label, apacket *p) +{ + char *tag; + char *x; + unsigned count; + + switch(p->msg.command){ + case A_SYNC: tag = "SYNC"; break; + case A_CNXN: tag = "CNXN" ; break; + case A_OPEN: tag = "OPEN"; break; + case A_OKAY: tag = "OKAY"; break; + case A_CLSE: tag = "CLSE"; break; + case A_WRTE: tag = "WRTE"; break; + default: tag = "????"; break; + } + + fprintf(stderr, "%s: %s %08x %08x %04x \"", + label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length); + count = p->msg.data_length; + x = (char*) p->data; + if(count > DUMPMAX) { + count = DUMPMAX; + tag = "\n"; + } else { + tag = "\"\n"; + } + while(count-- > 0){ + if((*x >= ' ') && (*x < 127)) { + fputc(*x, stderr); + } else { + fputc('.', stderr); + } + x++; + } + fprintf(stderr, tag); +} +#endif + +static void send_ready(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_ready \n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_close(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_close \n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_connect(atransport *t) +{ + D("Calling send_connect \n"); + apacket *cp = get_apacket(); + cp->msg.command = A_CNXN; + cp->msg.arg0 = A_VERSION; + cp->msg.arg1 = MAX_PAYLOAD; + snprintf((char*) cp->data, sizeof cp->data, "%s::", + HOST ? "host" : adb_device_banner); + cp->msg.data_length = strlen((char*) cp->data) + 1; + send_packet(cp, t); +} + +void parse_banner(char *banner, atransport *t) +{ + char *type, *product, *end; + + D("parse_banner: %s\n", banner); + type = banner; + product = strchr(type, ':'); + if(product) { + *product++ = 0; + } else { + product = ""; + } + + /* remove trailing ':' */ + end = strchr(product, ':'); + if(end) *end = 0; + + /* save product name in device structure */ + if (t->product == NULL) { + t->product = strdup(product); + } else if (strcmp(product, t->product) != 0) { + free(t->product); + t->product = strdup(product); + } + + if(!strcmp(type, "bootloader")){ + D("setting connection_state to CS_BOOTLOADER\n"); + t->connection_state = CS_BOOTLOADER; + update_transports(); + return; + } + + if(!strcmp(type, "device")) { + D("setting connection_state to CS_DEVICE\n"); + t->connection_state = CS_DEVICE; + update_transports(); + return; + } + + if(!strcmp(type, "recovery")) { + D("setting connection_state to CS_RECOVERY\n"); + t->connection_state = CS_RECOVERY; + update_transports(); + return; + } + + if(!strcmp(type, "sideload")) { + D("setting connection_state to CS_SIDELOAD\n"); + t->connection_state = CS_SIDELOAD; + update_transports(); + return; + } + + t->connection_state = CS_HOST; +} + +void handle_packet(apacket *p, atransport *t) +{ + asocket *s; + + D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], + ((char*) (&(p->msg.command)))[1], + ((char*) (&(p->msg.command)))[2], + ((char*) (&(p->msg.command)))[3]); + print_packet("recv", p); + + switch(p->msg.command){ + case A_SYNC: + if(p->msg.arg0){ + send_packet(p, t); + if(HOST) send_connect(t); + } else { + t->connection_state = CS_OFFLINE; + handle_offline(t); + send_packet(p, t); + } + return; + + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + /* XXX verify version, etc */ + if(t->connection_state != CS_OFFLINE) { + t->connection_state = CS_OFFLINE; + handle_offline(t); + } + parse_banner((char*) p->data, t); + handle_online(); + if(!HOST) send_connect(t); + break; + + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + if(t->connection_state != CS_OFFLINE) { + char *name = (char*) p->data; + name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; + s = create_local_service_socket(name); + if(s == 0) { + send_close(0, p->msg.arg0, t); + } else { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + send_ready(s->id, s->peer->id, t); + s->ready(s); + } + } + break; + + case A_OKAY: /* READY(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + if(s->peer == 0) { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + } + s->ready(s); + } + } + break; + + case A_CLSE: /* CLOSE(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + s->close(s); + } + } + break; + + case A_WRTE: + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + unsigned rid = p->msg.arg0; + p->len = p->msg.data_length; + + if(s->enqueue(s, p) == 0) { + D("Enqueue the socket\n"); + send_ready(s->id, rid, t); + } + return; + } + } + break; + + default: + printf("handle_packet: what is %08x?!\n", p->msg.command); + } + + put_apacket(p); +} + +static void adb_cleanup(void) +{ + usb_cleanup(); +} + +int adb_main() +{ + atexit(adb_cleanup); +#if defined(HAVE_FORKEXEC) + // No SIGCHLD. Let the service subproc handle its children. + signal(SIGPIPE, SIG_IGN); +#endif + + init_transport_registration(); + + // The minimal version of adbd only uses USB. + if (access("/dev/android_adb", F_OK) == 0) { + // listen on USB + usb_init(); + } + + if (setgid(AID_SHELL) != 0) { + fprintf(stderr, "failed to setgid to shell\n"); + exit(1); + } + if (setuid(AID_SHELL) != 0) { + fprintf(stderr, "failed to setuid to shell\n"); + exit(1); + } + fprintf(stderr, "userid is %d\n", getuid()); + + D("Event loop starting\n"); + + fdevent_loop(); + + usb_cleanup(); + + return 0; +} diff --git a/minadbd/adb.h b/minadbd/adb.h new file mode 100644 index 000000000..98fa5972e --- /dev/null +++ b/minadbd/adb.h @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ADB_H +#define __ADB_H + +#include <limits.h> + +#include "transport.h" /* readx(), writex() */ +#include "fdevent.h" + +#define MAX_PAYLOAD 4096 + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 + +#define A_VERSION 0x01000000 // ADB protocol version + +#define ADB_VERSION_MAJOR 1 // Used for help/version information +#define ADB_VERSION_MINOR 0 // Used for help/version information + +#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server + +typedef struct amessage amessage; +typedef struct apacket apacket; +typedef struct asocket asocket; +typedef struct aservice aservice; +typedef struct atransport atransport; +typedef struct adisconnect adisconnect; +typedef struct usb_handle usb_handle; + +struct amessage { + unsigned command; /* command identifier constant */ + unsigned arg0; /* first argument */ + unsigned arg1; /* second argument */ + unsigned data_length; /* length of payload (0 is allowed) */ + unsigned data_check; /* checksum of data payload */ + unsigned magic; /* command ^ 0xffffffff */ +}; + +struct apacket +{ + apacket *next; + + unsigned len; + unsigned char *ptr; + + amessage msg; + unsigned char data[MAX_PAYLOAD]; +}; + +/* An asocket represents one half of a connection between a local and +** remote entity. A local asocket is bound to a file descriptor. A +** remote asocket is bound to the protocol engine. +*/ +struct asocket { + /* chain pointers for the local/remote list of + ** asockets that this asocket lives in + */ + asocket *next; + asocket *prev; + + /* the unique identifier for this asocket + */ + unsigned id; + + /* flag: set when the socket's peer has closed + ** but packets are still queued for delivery + */ + int closing; + + /* the asocket we are connected to + */ + + asocket *peer; + + /* For local asockets, the fde is used to bind + ** us to our fd event system. For remote asockets + ** these fields are not used. + */ + fdevent fde; + int fd; + + /* queue of apackets waiting to be written + */ + apacket *pkt_first; + apacket *pkt_last; + + /* enqueue is called by our peer when it has data + ** for us. It should return 0 if we can accept more + ** data or 1 if not. If we return 1, we must call + ** peer->ready() when we once again are ready to + ** receive data. + */ + int (*enqueue)(asocket *s, apacket *pkt); + + /* ready is called by the peer when it is ready for + ** us to send data via enqueue again + */ + void (*ready)(asocket *s); + + /* close is called by the peer when it has gone away. + ** we are not allowed to make any further calls on the + ** peer once our close method is called. + */ + void (*close)(asocket *s); + + /* socket-type-specific extradata */ + void *extra; + + /* A socket is bound to atransport */ + atransport *transport; +}; + + +/* the adisconnect structure is used to record a callback that +** will be called whenever a transport is disconnected (e.g. by the user) +** this should be used to cleanup objects that depend on the +** transport (e.g. remote sockets, etc...) +*/ +struct adisconnect +{ + void (*func)(void* opaque, atransport* t); + void* opaque; + adisconnect* next; + adisconnect* prev; +}; + + +/* a transport object models the connection to a remote device or emulator +** there is one transport per connected device/emulator. a "local transport" +** connects through TCP (for the emulator), while a "usb transport" through +** USB (for real devices) +** +** note that kTransportHost doesn't really correspond to a real transport +** object, it's a special value used to indicate that a client wants to +** connect to a service implemented within the ADB server itself. +*/ +typedef enum transport_type { + kTransportUsb, + kTransportLocal, + kTransportAny, + kTransportHost, +} transport_type; + +struct atransport +{ + atransport *next; + atransport *prev; + + int (*read_from_remote)(apacket *p, atransport *t); + int (*write_to_remote)(apacket *p, atransport *t); + void (*close)(atransport *t); + void (*kick)(atransport *t); + + int fd; + int transport_socket; + fdevent transport_fde; + int ref_count; + unsigned sync_token; + int connection_state; + transport_type type; + + /* usb handle or socket fd as needed */ + usb_handle *usb; + int sfd; + + /* used to identify transports for clients */ + char *serial; + char *product; + int adb_port; // Use for emulators (local transport) + + /* a list of adisconnect callbacks called when the transport is kicked */ + int kicked; + adisconnect disconnects; +}; + + +void print_packet(const char *label, apacket *p); + +asocket *find_local_socket(unsigned id); +void install_local_socket(asocket *s); +void remove_socket(asocket *s); +void close_all_sockets(atransport *t); + +#define LOCAL_CLIENT_PREFIX "emulator-" + +asocket *create_local_socket(int fd); +asocket *create_local_service_socket(const char *destination); + +asocket *create_remote_socket(unsigned id, atransport *t); +void connect_to_remote(asocket *s, const char *destination); +void connect_to_smartsocket(asocket *s); + +void fatal(const char *fmt, ...); +void fatal_errno(const char *fmt, ...); + +void handle_packet(apacket *p, atransport *t); +void send_packet(apacket *p, atransport *t); + +void get_my_path(char *s, size_t maxLen); +int launch_server(int server_port); +int adb_main(); + + +/* transports are ref-counted +** get_device_transport does an acquire on your behalf before returning +*/ +void init_transport_registration(void); +int list_transports(char *buf, size_t bufsize); +void update_transports(void); + +asocket* create_device_tracker(void); + +/* Obtain a transport from the available transports. +** If state is != CS_ANY, only transports in that state are considered. +** If serial is non-NULL then only the device with that serial will be chosen. +** If no suitable transport is found, error is set. +*/ +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out); +void add_transport_disconnect( atransport* t, adisconnect* dis ); +void remove_transport_disconnect( atransport* t, adisconnect* dis ); +void run_transport_disconnects( atransport* t ); +void kick_transport( atransport* t ); + +/* initialize a transport object's func pointers and state */ +#if ADB_HOST +int get_available_local_transport_index(); +#endif +int init_socket_transport(atransport *t, int s, int port, int local); +void init_usb_transport(atransport *t, usb_handle *usb, int state); + +/* for MacOS X cleanup */ +void close_usb_devices(); + +/* cause new transports to be init'd and added to the list */ +void register_socket_transport(int s, const char *serial, int port, int local); + +/* these should only be used for the "adb disconnect" command */ +void unregister_transport(atransport *t); +void unregister_all_tcp_transports(); + +void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); + +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb); + +atransport *find_transport(const char *serial); +#if ADB_HOST +atransport* find_emulator_transport_by_adb_port(int adb_port); +#endif + +int service_to_fd(const char *name); +#if ADB_HOST +asocket *host_service_to_socket(const char* name, const char *serial); +#endif + +#if !ADB_HOST +typedef enum { + BACKUP, + RESTORE +} BackupOperation; +int backup_service(BackupOperation operation, char* args); +void framebuffer_service(int fd, void *cookie); +void log_service(int fd, void *cookie); +void remount_service(int fd, void *cookie); +char * get_log_file_path(const char * log_name); +#endif + +/* packet allocator */ +apacket *get_apacket(void); +void put_apacket(apacket *p); + +int check_header(apacket *p); +int check_data(apacket *p); + +/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ + +#define ADB_TRACE 1 + +/* IMPORTANT: if you change the following list, don't + * forget to update the corresponding 'tags' table in + * the adb_trace_init() function implemented in adb.c + */ +typedef enum { + TRACE_ADB = 0, /* 0x001 */ + TRACE_SOCKETS, + TRACE_PACKETS, + TRACE_TRANSPORT, + TRACE_RWX, /* 0x010 */ + TRACE_USB, + TRACE_SYNC, + TRACE_SYSDEPS, + TRACE_JDWP, /* 0x100 */ + TRACE_SERVICES, +} AdbTrace; + +#if ADB_TRACE + + extern int adb_trace_mask; + extern unsigned char adb_trace_output_count; + void adb_trace_init(void); + +# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) + + /* you must define TRACE_TAG before using this macro */ +# define D(...) \ + do { \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + fprintf(stderr, "%s::%s():", \ + __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ + } while (0) +# define DR(...) \ + do { \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ + } while (0) +#else +# define D(...) ((void)0) +# define DR(...) ((void)0) +# define ADB_TRACING 0 +#endif + + +#if !TRACE_PACKETS +#define print_packet(tag,p) do {} while (0) +#endif + +#if ADB_HOST_ON_TARGET +/* adb and adbd are coexisting on the target, so use 5038 for adb + * to avoid conflicting with adbd's usage of 5037 + */ +# define DEFAULT_ADB_PORT 5038 +#else +# define DEFAULT_ADB_PORT 5037 +#endif + +#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555 + +#define ADB_CLASS 0xff +#define ADB_SUBCLASS 0x42 +#define ADB_PROTOCOL 0x1 + + +void local_init(int port); +int local_connect(int port); +int local_connect_arbitrary_ports(int console_port, int adb_port); + +/* usb host/client interface */ +void usb_init(); +void usb_cleanup(); +int usb_write(usb_handle *h, const void *data, int len); +int usb_read(usb_handle *h, void *data, int len); +int usb_close(usb_handle *h); +void usb_kick(usb_handle *h); + +/* used for USB device detection */ +#if ADB_HOST +int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol); +#endif + +unsigned host_to_le32(unsigned n); +int adb_commandline(int argc, char **argv); + +int connection_state(atransport *t); + +#define CS_ANY -1 +#define CS_OFFLINE 0 +#define CS_BOOTLOADER 1 +#define CS_DEVICE 2 +#define CS_HOST 3 +#define CS_RECOVERY 4 +#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ +#define CS_SIDELOAD 6 + +extern int HOST; +extern int SHELL_EXIT_NOTIFY_FD; + +#define CHUNK_SIZE (64*1024) + +int sendfailmsg(int fd, const char *reason); +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); + +#define ADB_SIDELOAD_FILENAME "/tmp/update.zip" + +#endif diff --git a/minadbd/fdevent.c b/minadbd/fdevent.c new file mode 100644 index 000000000..5c374a71b --- /dev/null +++ b/minadbd/fdevent.c @@ -0,0 +1,695 @@ +/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c +** +** Copyright 2006, Brian Swetland <swetland@frotz.net> +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <sys/ioctl.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <fcntl.h> + +#include <stdarg.h> +#include <stddef.h> + +#include "fdevent.h" +#include "transport.h" +#include "sysdeps.h" + + +/* !!! Do not enable DEBUG for the adb that will run as the server: +** both stdout and stderr are used to communicate between the client +** and server. Any extra output will cause failures. +*/ +#define DEBUG 0 /* non-0 will break adb server */ + +// This socket is used when a subproc shell service exists. +// It wakes up the fdevent_loop() and cause the correct handling +// of the shell's pseudo-tty master. I.e. force close it. +int SHELL_EXIT_NOTIFY_FD = -1; + +static void fatal(const char *fn, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:", fn); + vfprintf(stderr, fmt, ap); + va_end(ap); + abort(); +} + +#define FATAL(x...) fatal(__FUNCTION__, x) + +#if DEBUG +#define D(...) \ + do { \ + adb_mutex_lock(&D_lock); \ + int save_errno = errno; \ + fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } while(0) +static void dump_fde(fdevent *fde, const char *info) +{ + adb_mutex_lock(&D_lock); + fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd, + fde->state & FDE_READ ? 'R' : ' ', + fde->state & FDE_WRITE ? 'W' : ' ', + fde->state & FDE_ERROR ? 'E' : ' ', + info); + adb_mutex_unlock(&D_lock); +} +#else +#define D(...) ((void)0) +#define dump_fde(fde, info) do { } while(0) +#endif + +#define FDE_EVENTMASK 0x00ff +#define FDE_STATEMASK 0xff00 + +#define FDE_ACTIVE 0x0100 +#define FDE_PENDING 0x0200 +#define FDE_CREATED 0x0400 + +static void fdevent_plist_enqueue(fdevent *node); +static void fdevent_plist_remove(fdevent *node); +static fdevent *fdevent_plist_dequeue(void); +static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata); + +static fdevent list_pending = { + .next = &list_pending, + .prev = &list_pending, +}; + +static fdevent **fd_table = 0; +static int fd_table_max = 0; + +#ifdef CRAPTASTIC +//HAVE_EPOLL + +#include <sys/epoll.h> + +static int epoll_fd = -1; + +static void fdevent_init() +{ + /* XXX: what's a good size for the passed in hint? */ + epoll_fd = epoll_create(256); + + if(epoll_fd < 0) { + perror("epoll_create() failed"); + exit(1); + } + + /* mark for close-on-exec */ + fcntl(epoll_fd, F_SETFD, FD_CLOEXEC); +} + +static void fdevent_connect(fdevent *fde) +{ + struct epoll_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.events = 0; + ev.data.ptr = fde; + +#if 0 + if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) { + perror("epoll_ctl() failed\n"); + exit(1); + } +#endif +} + +static void fdevent_disconnect(fdevent *fde) +{ + struct epoll_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.events = 0; + ev.data.ptr = fde; + + /* technically we only need to delete if we + ** were actively monitoring events, but let's + ** be aggressive and do it anyway, just in case + ** something's out of sync + */ + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev); +} + +static void fdevent_update(fdevent *fde, unsigned events) +{ + struct epoll_event ev; + int active; + + active = (fde->state & FDE_EVENTMASK) != 0; + + memset(&ev, 0, sizeof(ev)); + ev.events = 0; + ev.data.ptr = fde; + + if(events & FDE_READ) ev.events |= EPOLLIN; + if(events & FDE_WRITE) ev.events |= EPOLLOUT; + if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP); + + fde->state = (fde->state & FDE_STATEMASK) | events; + + if(active) { + /* we're already active. if we're changing to *no* + ** events being monitored, we need to delete, otherwise + ** we need to just modify + */ + if(ev.events) { + if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) { + perror("epoll_ctl() failed\n"); + exit(1); + } + } else { + if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) { + perror("epoll_ctl() failed\n"); + exit(1); + } + } + } else { + /* we're not active. if we're watching events, we need + ** to add, otherwise we can just do nothing + */ + if(ev.events) { + if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) { + perror("epoll_ctl() failed\n"); + exit(1); + } + } + } +} + +static void fdevent_process() +{ + struct epoll_event events[256]; + fdevent *fde; + int i, n; + + n = epoll_wait(epoll_fd, events, 256, -1); + + if(n < 0) { + if(errno == EINTR) return; + perror("epoll_wait"); + exit(1); + } + + for(i = 0; i < n; i++) { + struct epoll_event *ev = events + i; + fde = ev->data.ptr; + + if(ev->events & EPOLLIN) { + fde->events |= FDE_READ; + } + if(ev->events & EPOLLOUT) { + fde->events |= FDE_WRITE; + } + if(ev->events & (EPOLLERR | EPOLLHUP)) { + fde->events |= FDE_ERROR; + } + if(fde->events) { + if(fde->state & FDE_PENDING) continue; + fde->state |= FDE_PENDING; + fdevent_plist_enqueue(fde); + } + } +} + +#else /* USE_SELECT */ + +#ifdef HAVE_WINSOCK +#include <winsock2.h> +#else +#include <sys/select.h> +#endif + +static fd_set read_fds; +static fd_set write_fds; +static fd_set error_fds; + +static int select_n = 0; + +static void fdevent_init(void) +{ + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + FD_ZERO(&error_fds); +} + +static void fdevent_connect(fdevent *fde) +{ + if(fde->fd >= select_n) { + select_n = fde->fd + 1; + } +} + +static void fdevent_disconnect(fdevent *fde) +{ + int i, n; + + FD_CLR(fde->fd, &read_fds); + FD_CLR(fde->fd, &write_fds); + FD_CLR(fde->fd, &error_fds); + + for(n = 0, i = 0; i < select_n; i++) { + if(fd_table[i] != 0) n = i; + } + select_n = n + 1; +} + +static void fdevent_update(fdevent *fde, unsigned events) +{ + if(events & FDE_READ) { + FD_SET(fde->fd, &read_fds); + } else { + FD_CLR(fde->fd, &read_fds); + } + if(events & FDE_WRITE) { + FD_SET(fde->fd, &write_fds); + } else { + FD_CLR(fde->fd, &write_fds); + } + if(events & FDE_ERROR) { + FD_SET(fde->fd, &error_fds); + } else { + FD_CLR(fde->fd, &error_fds); + } + + fde->state = (fde->state & FDE_STATEMASK) | events; +} + +/* Looks at fd_table[] for bad FDs and sets bit in fds. +** Returns the number of bad FDs. +*/ +static int fdevent_fd_check(fd_set *fds) +{ + int i, n = 0; + fdevent *fde; + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + if(fde == 0) continue; + if(fcntl(i, F_GETFL, NULL) < 0) { + FD_SET(i, fds); + n++; + // fde->state |= FDE_DONT_CLOSE; + + } + } + return n; +} + +#if !DEBUG +static inline void dump_all_fds(const char *extra_msg) {} +#else +static void dump_all_fds(const char *extra_msg) +{ +int i; + fdevent *fde; + // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank + char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff; + size_t max_chars = FD_SETSIZE * 6 + 1; + int printed_out; +#define SAFE_SPRINTF(...) \ + do { \ + printed_out = snprintf(pb, max_chars, __VA_ARGS__); \ + if (printed_out <= 0) { \ + D("... snprintf failed.\n"); \ + return; \ + } \ + if (max_chars < (unsigned int)printed_out) { \ + D("... snprintf out of space.\n"); \ + return; \ + } \ + pb += printed_out; \ + max_chars -= printed_out; \ + } while(0) + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + SAFE_SPRINTF("%d", i); + if(fde == 0) { + SAFE_SPRINTF("? "); + continue; + } + if(fcntl(i, F_GETFL, NULL) < 0) { + SAFE_SPRINTF("b"); + } + SAFE_SPRINTF(" "); + } + D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff); +} +#endif + +static void fdevent_process() +{ + int i, n; + fdevent *fde; + unsigned events; + fd_set rfd, wfd, efd; + + memcpy(&rfd, &read_fds, sizeof(fd_set)); + memcpy(&wfd, &write_fds, sizeof(fd_set)); + memcpy(&efd, &error_fds, sizeof(fd_set)); + + dump_all_fds("pre select()"); + + n = select(select_n, &rfd, &wfd, &efd, NULL); + int saved_errno = errno; + D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0); + + dump_all_fds("post select()"); + + if(n < 0) { + switch(saved_errno) { + case EINTR: return; + case EBADF: + // Can't trust the FD sets after an error. + FD_ZERO(&wfd); + FD_ZERO(&efd); + FD_ZERO(&rfd); + break; + default: + D("Unexpected select() error=%d\n", saved_errno); + return; + } + } + if(n <= 0) { + // We fake a read, as the rest of the code assumes + // that errors will be detected at that point. + n = fdevent_fd_check(&rfd); + } + + for(i = 0; (i < select_n) && (n > 0); i++) { + events = 0; + if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; } + if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; } + if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; } + + if(events) { + fde = fd_table[i]; + if(fde == 0) + FATAL("missing fde for fd %d\n", i); + + fde->events |= events; + + D("got events fde->fd=%d events=%04x, state=%04x\n", + fde->fd, fde->events, fde->state); + if(fde->state & FDE_PENDING) continue; + fde->state |= FDE_PENDING; + fdevent_plist_enqueue(fde); + } + } +} + +#endif + +static void fdevent_register(fdevent *fde) +{ + if(fde->fd < 0) { + FATAL("bogus negative fd (%d)\n", fde->fd); + } + + if(fde->fd >= fd_table_max) { + int oldmax = fd_table_max; + if(fde->fd > 32000) { + FATAL("bogus huuuuge fd (%d)\n", fde->fd); + } + if(fd_table_max == 0) { + fdevent_init(); + fd_table_max = 256; + } + while(fd_table_max <= fde->fd) { + fd_table_max *= 2; + } + fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max); + if(fd_table == 0) { + FATAL("could not expand fd_table to %d entries\n", fd_table_max); + } + memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax)); + } + + fd_table[fde->fd] = fde; +} + +static void fdevent_unregister(fdevent *fde) +{ + if((fde->fd < 0) || (fde->fd >= fd_table_max)) { + FATAL("fd out of range (%d)\n", fde->fd); + } + + if(fd_table[fde->fd] != fde) { + FATAL("fd_table out of sync [%d]\n", fde->fd); + } + + fd_table[fde->fd] = 0; + + if(!(fde->state & FDE_DONT_CLOSE)) { + dump_fde(fde, "close"); + adb_close(fde->fd); + } +} + +static void fdevent_plist_enqueue(fdevent *node) +{ + fdevent *list = &list_pending; + + node->next = list; + node->prev = list->prev; + node->prev->next = node; + list->prev = node; +} + +static void fdevent_plist_remove(fdevent *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; +} + +static fdevent *fdevent_plist_dequeue(void) +{ + fdevent *list = &list_pending; + fdevent *node = list->next; + + if(node == list) return 0; + + list->next = node->next; + list->next->prev = list; + node->next = 0; + node->prev = 0; + + return node; +} + +static void fdevent_call_fdfunc(fdevent* fde) +{ + unsigned events = fde->events; + fde->events = 0; + if(!(fde->state & FDE_PENDING)) return; + fde->state &= (~FDE_PENDING); + dump_fde(fde, "callback"); + fde->func(fde->fd, events, fde->arg); +} + +static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata) +{ + + D("subproc handling on fd=%d ev=%04x\n", fd, ev); + + // Hook oneself back into the fde's suitable for select() on read. + if((fd < 0) || (fd >= fd_table_max)) { + FATAL("fd %d out of range for fd_table \n", fd); + } + fdevent *fde = fd_table[fd]; + fdevent_add(fde, FDE_READ); + + if(ev & FDE_READ){ + int subproc_fd; + + if(readx(fd, &subproc_fd, sizeof(subproc_fd))) { + FATAL("Failed to read the subproc's fd from fd=%d\n", fd); + } + if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) { + D("subproc_fd %d out of range 0, fd_table_max=%d\n", + subproc_fd, fd_table_max); + return; + } + fdevent *subproc_fde = fd_table[subproc_fd]; + if(!subproc_fde) { + D("subproc_fd %d cleared from fd_table\n", subproc_fd); + return; + } + if(subproc_fde->fd != subproc_fd) { + // Already reallocated? + D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd); + return; + } + + subproc_fde->force_eof = 1; + + int rcount = 0; + ioctl(subproc_fd, FIONREAD, &rcount); + D("subproc with fd=%d has rcount=%d err=%d\n", + subproc_fd, rcount, errno); + + if(rcount) { + // If there is data left, it will show up in the select(). + // This works because there is no other thread reading that + // data when in this fd_func(). + return; + } + + D("subproc_fde.state=%04x\n", subproc_fde->state); + subproc_fde->events |= FDE_READ; + if(subproc_fde->state & FDE_PENDING) { + return; + } + subproc_fde->state |= FDE_PENDING; + fdevent_call_fdfunc(subproc_fde); + } +} + +fdevent *fdevent_create(int fd, fd_func func, void *arg) +{ + fdevent *fde = (fdevent*) malloc(sizeof(fdevent)); + if(fde == 0) return 0; + fdevent_install(fde, fd, func, arg); + fde->state |= FDE_CREATED; + return fde; +} + +void fdevent_destroy(fdevent *fde) +{ + if(fde == 0) return; + if(!(fde->state & FDE_CREATED)) { + FATAL("fde %p not created by fdevent_create()\n", fde); + } + fdevent_remove(fde); +} + +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) +{ + memset(fde, 0, sizeof(fdevent)); + fde->state = FDE_ACTIVE; + fde->fd = fd; + fde->force_eof = 0; + fde->func = func; + fde->arg = arg; + +#ifndef HAVE_WINSOCK + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + fdevent_register(fde); + dump_fde(fde, "connect"); + fdevent_connect(fde); + fde->state |= FDE_ACTIVE; +} + +void fdevent_remove(fdevent *fde) +{ + if(fde->state & FDE_PENDING) { + fdevent_plist_remove(fde); + } + + if(fde->state & FDE_ACTIVE) { + fdevent_disconnect(fde); + dump_fde(fde, "disconnect"); + fdevent_unregister(fde); + } + + fde->state = 0; + fde->events = 0; +} + + +void fdevent_set(fdevent *fde, unsigned events) +{ + events &= FDE_EVENTMASK; + + if((fde->state & FDE_EVENTMASK) == events) return; + + if(fde->state & FDE_ACTIVE) { + fdevent_update(fde, events); + dump_fde(fde, "update"); + } + + fde->state = (fde->state & FDE_STATEMASK) | events; + + if(fde->state & FDE_PENDING) { + /* if we're pending, make sure + ** we don't signal an event that + ** is no longer wanted. + */ + fde->events &= (~events); + if(fde->events == 0) { + fdevent_plist_remove(fde); + fde->state &= (~FDE_PENDING); + } + } +} + +void fdevent_add(fdevent *fde, unsigned events) +{ + fdevent_set( + fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK)); +} + +void fdevent_del(fdevent *fde, unsigned events) +{ + fdevent_set( + fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK))); +} + +void fdevent_subproc_setup() +{ + int s[2]; + + if(adb_socketpair(s)) { + FATAL("cannot create shell-exit socket-pair\n"); + } + SHELL_EXIT_NOTIFY_FD = s[0]; + fdevent *fde; + fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL); + if(!fde) + FATAL("cannot create fdevent for shell-exit handler\n"); + fdevent_add(fde, FDE_READ); +} + +void fdevent_loop() +{ + fdevent *fde; + fdevent_subproc_setup(); + + for(;;) { + D("--- ---- waiting for events\n"); + + fdevent_process(); + + while((fde = fdevent_plist_dequeue())) { + fdevent_call_fdfunc(fde); + } + } +} diff --git a/minadbd/fdevent.h b/minadbd/fdevent.h new file mode 100644 index 000000000..a0ebe2a7e --- /dev/null +++ b/minadbd/fdevent.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FDEVENT_H +#define __FDEVENT_H + +#include <stdint.h> /* for int64_t */ + +/* events that may be observed */ +#define FDE_READ 0x0001 +#define FDE_WRITE 0x0002 +#define FDE_ERROR 0x0004 +#define FDE_TIMEOUT 0x0008 + +/* features that may be set (via the events set/add/del interface) */ +#define FDE_DONT_CLOSE 0x0080 + +typedef struct fdevent fdevent; + +typedef void (*fd_func)(int fd, unsigned events, void *userdata); + +/* Allocate and initialize a new fdevent object + * Note: use FD_TIMER as 'fd' to create a fd-less object + * (used to implement timers). +*/ +fdevent *fdevent_create(int fd, fd_func func, void *arg); + +/* Uninitialize and deallocate an fdevent object that was +** created by fdevent_create() +*/ +void fdevent_destroy(fdevent *fde); + +/* Initialize an fdevent object that was externally allocated +*/ +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg); + +/* Uninitialize an fdevent object that was initialized by +** fdevent_install() +*/ +void fdevent_remove(fdevent *item); + +/* Change which events should cause notifications +*/ +void fdevent_set(fdevent *fde, unsigned events); +void fdevent_add(fdevent *fde, unsigned events); +void fdevent_del(fdevent *fde, unsigned events); + +void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms); + +/* loop forever, handling events. +*/ +void fdevent_loop(); + +struct fdevent +{ + fdevent *next; + fdevent *prev; + + int fd; + int force_eof; + + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + + +#endif diff --git a/minadbd/mutex_list.h b/minadbd/mutex_list.h new file mode 100644 index 000000000..652dd7341 --- /dev/null +++ b/minadbd/mutex_list.h @@ -0,0 +1,26 @@ +/* the list of mutexes used by adb */ +/* #ifndef __MUTEX_LIST_H + * Do not use an include-guard. This file is included once to declare the locks + * and once in win32 to actually do the runtime initialization. + */ +#ifndef ADB_MUTEX +#error ADB_MUTEX not defined when including this file +#endif +ADB_MUTEX(dns_lock) +ADB_MUTEX(socket_list_lock) +ADB_MUTEX(transport_lock) +#if ADB_HOST +ADB_MUTEX(local_transports_lock) +#endif +ADB_MUTEX(usb_lock) + +// Sadly logging to /data/adb/adb-... is not thread safe. +// After modifying adb.h::D() to count invocations: +// DEBUG(jpa):0:Handling main() +// DEBUG(jpa):1:[ usb_init - starting thread ] +// (Oopsies, no :2:, and matching message is also gone.) +// DEBUG(jpa):3:[ usb_thread - opening device ] +// DEBUG(jpa):4:jdwp control socket started (10) +ADB_MUTEX(D_lock) + +#undef ADB_MUTEX diff --git a/minadbd/services.c b/minadbd/services.c new file mode 100644 index 000000000..aef37f7e4 --- /dev/null +++ b/minadbd/services.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" +#include "fdevent.h" + +#define TRACE_TAG TRACE_SERVICES +#include "adb.h" + +typedef struct stinfo stinfo; + +struct stinfo { + void (*func)(int fd, void *cookie); + int fd; + void *cookie; +}; + + +void *service_bootstrap_func(void *x) +{ + stinfo *sti = x; + sti->func(sti->fd, sti->cookie); + free(sti); + return 0; +} + +static void sideload_service(int s, void *cookie) +{ + unsigned char buf[4096]; + unsigned count = (unsigned) cookie; + int fd; + + fprintf(stderr, "sideload_service invoked\n"); + + fd = adb_creat(ADB_SIDELOAD_FILENAME, 0644); + if(fd < 0) { + fprintf(stderr, "failed to create %s\n", ADB_SIDELOAD_FILENAME); + adb_close(s); + return; + } + + while(count > 0) { + unsigned xfer = (count > 4096) ? 4096 : count; + if(readx(s, buf, xfer)) break; + if(writex(fd, buf, xfer)) break; + count -= xfer; + } + + if(count == 0) { + writex(s, "OKAY", 4); + } else { + writex(s, "FAIL", 4); + } + adb_close(fd); + adb_close(s); + + if (count == 0) { + fprintf(stderr, "adbd exiting after successful sideload\n"); + sleep(1); + exit(0); + } +} + + +#if 0 +static void echo_service(int fd, void *cookie) +{ + char buf[4096]; + int r; + char *p; + int c; + + for(;;) { + r = read(fd, buf, 4096); + if(r == 0) goto done; + if(r < 0) { + if(errno == EINTR) continue; + else goto done; + } + + c = r; + p = buf; + while(c > 0) { + r = write(fd, p, c); + if(r > 0) { + c -= r; + p += r; + continue; + } + if((r < 0) && (errno == EINTR)) continue; + goto done; + } + } +done: + close(fd); +} +#endif + +static int create_service_thread(void (*func)(int, void *), void *cookie) +{ + stinfo *sti; + adb_thread_t t; + int s[2]; + + if(adb_socketpair(s)) { + printf("cannot create service socket pair\n"); + return -1; + } + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = func; + sti->cookie = cookie; + sti->fd = s[1]; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(s[0]); + adb_close(s[1]); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, %d:%d\n",s[0], s[1]); + return s[0]; +} + +int service_to_fd(const char *name) +{ + int ret = -1; + + if (!strncmp(name, "sideload:", 9)) { + ret = create_service_thread(sideload_service, (void*) atoi(name + 9)); +#if 0 + } else if(!strncmp(name, "echo:", 5)){ + ret = create_service_thread(echo_service, 0); +#endif + } + if (ret >= 0) { + close_on_exec(ret); + } + return ret; +} diff --git a/minadbd/sockets.c b/minadbd/sockets.c new file mode 100644 index 000000000..2dd646159 --- /dev/null +++ b/minadbd/sockets.c @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SOCKETS +#include "adb.h" + +ADB_MUTEX_DEFINE( socket_list_lock ); + +static void local_socket_close_locked(asocket *s); + +int sendfailmsg(int fd, const char *reason) +{ + char buf[9]; + int len; + len = strlen(reason); + if(len > 0xffff) len = 0xffff; + snprintf(buf, sizeof buf, "FAIL%04x", len); + if(writex(fd, buf, 8)) return -1; + return writex(fd, reason, len); +} + +//extern int online; + +static unsigned local_socket_next_id = 1; + +static asocket local_socket_list = { + .next = &local_socket_list, + .prev = &local_socket_list, +}; + +/* the the list of currently closing local sockets. +** these have no peer anymore, but still packets to +** write to their fd. +*/ +static asocket local_socket_closing_list = { + .next = &local_socket_closing_list, + .prev = &local_socket_closing_list, +}; + +asocket *find_local_socket(unsigned id) +{ + asocket *s; + asocket *result = NULL; + + adb_mutex_lock(&socket_list_lock); + for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { + if (s->id == id) { + result = s; + break; + } + } + adb_mutex_unlock(&socket_list_lock); + + return result; +} + +static void +insert_local_socket(asocket* s, asocket* list) +{ + s->next = list; + s->prev = s->next->prev; + s->prev->next = s; + s->next->prev = s; +} + + +void install_local_socket(asocket *s) +{ + adb_mutex_lock(&socket_list_lock); + + s->id = local_socket_next_id++; + insert_local_socket(s, &local_socket_list); + + adb_mutex_unlock(&socket_list_lock); +} + +void remove_socket(asocket *s) +{ + // socket_list_lock should already be held + if (s->prev && s->next) + { + s->prev->next = s->next; + s->next->prev = s->prev; + s->next = 0; + s->prev = 0; + s->id = 0; + } +} + +void close_all_sockets(atransport *t) +{ + asocket *s; + + /* this is a little gross, but since s->close() *will* modify + ** the list out from under you, your options are limited. + */ + adb_mutex_lock(&socket_list_lock); +restart: + for(s = local_socket_list.next; s != &local_socket_list; s = s->next){ + if(s->transport == t || (s->peer && s->peer->transport == t)) { + local_socket_close_locked(s); + goto restart; + } + } + adb_mutex_unlock(&socket_list_lock); +} + +static int local_socket_enqueue(asocket *s, apacket *p) +{ + D("LS(%d): enqueue %d\n", s->id, p->len); + + p->ptr = p->data; + + /* if there is already data queue'd, we will receive + ** events when it's time to write. just add this to + ** the tail + */ + if(s->pkt_first) { + goto enqueue; + } + + /* write as much as we can, until we + ** would block or there is an error/eof + */ + while(p->len > 0) { + int r = adb_write(s->fd, p->ptr, p->len); + if(r > 0) { + p->len -= r; + p->ptr += r; + continue; + } + if((r == 0) || (errno != EAGAIN)) { + D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) ); + s->close(s); + return 1; /* not ready (error) */ + } else { + break; + } + } + + if(p->len == 0) { + put_apacket(p); + return 0; /* ready for more data */ + } + +enqueue: + p->next = 0; + if(s->pkt_first) { + s->pkt_last->next = p; + } else { + s->pkt_first = p; + } + s->pkt_last = p; + + /* make sure we are notified when we can drain the queue */ + fdevent_add(&s->fde, FDE_WRITE); + + return 1; /* not ready (backlog) */ +} + +static void local_socket_ready(asocket *s) +{ + /* far side is ready for data, pay attention to + readable events */ + fdevent_add(&s->fde, FDE_READ); +// D("LS(%d): ready()\n", s->id); +} + +static void local_socket_close(asocket *s) +{ + adb_mutex_lock(&socket_list_lock); + local_socket_close_locked(s); + adb_mutex_unlock(&socket_list_lock); +} + +// be sure to hold the socket list lock when calling this +static void local_socket_destroy(asocket *s) +{ + apacket *p, *n; + D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd); + + /* IMPORTANT: the remove closes the fd + ** that belongs to this socket + */ + fdevent_remove(&s->fde); + + /* dispose of any unwritten data */ + for(p = s->pkt_first; p; p = n) { + D("LS(%d): discarding %d bytes\n", s->id, p->len); + n = p->next; + put_apacket(p); + } + remove_socket(s); + free(s); +} + + +static void local_socket_close_locked(asocket *s) +{ + D("entered. LS(%d) fd=%d\n", s->id, s->fd); + if(s->peer) { + D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); + s->peer->peer = 0; + // tweak to avoid deadlock + if (s->peer->close == local_socket_close) { + local_socket_close_locked(s->peer); + } else { + s->peer->close(s->peer); + } + s->peer = 0; + } + + /* If we are already closing, or if there are no + ** pending packets, destroy immediately + */ + if (s->closing || s->pkt_first == NULL) { + int id = s->id; + local_socket_destroy(s); + D("LS(%d): closed\n", id); + return; + } + + /* otherwise, put on the closing list + */ + D("LS(%d): closing\n", s->id); + s->closing = 1; + fdevent_del(&s->fde, FDE_READ); + remove_socket(s); + D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd); + insert_local_socket(s, &local_socket_closing_list); +} + +static void local_socket_event_func(int fd, unsigned ev, void *_s) +{ + asocket *s = _s; + + D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); + + /* put the FDE_WRITE processing before the FDE_READ + ** in order to simplify the code. + */ + if(ev & FDE_WRITE){ + apacket *p; + + while((p = s->pkt_first) != 0) { + while(p->len > 0) { + int r = adb_write(fd, p->ptr, p->len); + if(r > 0) { + p->ptr += r; + p->len -= r; + continue; + } + if(r < 0) { + /* returning here is ok because FDE_READ will + ** be processed in the next iteration loop + */ + if(errno == EAGAIN) return; + if(errno == EINTR) continue; + } + D(" closing after write because r=%d and errno is %d\n", r, errno); + s->close(s); + return; + } + + if(p->len == 0) { + s->pkt_first = p->next; + if(s->pkt_first == 0) s->pkt_last = 0; + put_apacket(p); + } + } + + /* if we sent the last packet of a closing socket, + ** we can now destroy it. + */ + if (s->closing) { + D(" closing because 'closing' is set after write\n"); + s->close(s); + return; + } + + /* no more packets queued, so we can ignore + ** writable events again and tell our peer + ** to resume writing + */ + fdevent_del(&s->fde, FDE_WRITE); + s->peer->ready(s->peer); + } + + + if(ev & FDE_READ){ + apacket *p = get_apacket(); + unsigned char *x = p->data; + size_t avail = MAX_PAYLOAD; + int r; + int is_eof = 0; + + while(avail > 0) { + r = adb_read(fd, x, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); + if(r > 0) { + avail -= r; + x += r; + continue; + } + if(r < 0) { + if(errno == EAGAIN) break; + if(errno == EINTR) continue; + } + + /* r = 0 or unhandled error */ + is_eof = 1; + break; + } + D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n", + s->id, s->fd, r, is_eof, s->fde.force_eof); + if((avail == MAX_PAYLOAD) || (s->peer == 0)) { + put_apacket(p); + } else { + p->len = MAX_PAYLOAD - avail; + + r = s->peer->enqueue(s->peer, p); + D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r); + + if(r < 0) { + /* error return means they closed us as a side-effect + ** and we must return immediately. + ** + ** note that if we still have buffered packets, the + ** socket will be placed on the closing socket list. + ** this handler function will be called again + ** to process FDE_WRITE events. + */ + return; + } + + if(r > 0) { + /* if the remote cannot accept further events, + ** we disable notification of READs. They'll + ** be enabled again when we get a call to ready() + */ + fdevent_del(&s->fde, FDE_READ); + } + } + /* Don't allow a forced eof if data is still there */ + if((s->fde.force_eof && !r) || is_eof) { + D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof); + s->close(s); + } + } + + if(ev & FDE_ERROR){ + /* this should be caught be the next read or write + ** catching it here means we may skip the last few + ** bytes of readable data. + */ +// s->close(s); + D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); + + return; + } +} + +asocket *create_local_socket(int fd) +{ + asocket *s = calloc(1, sizeof(asocket)); + if (s == NULL) fatal("cannot allocate socket"); + s->fd = fd; + s->enqueue = local_socket_enqueue; + s->ready = local_socket_ready; + s->close = local_socket_close; + install_local_socket(s); + + fdevent_install(&s->fde, fd, local_socket_event_func, s); +/* fdevent_add(&s->fde, FDE_ERROR); */ + //fprintf(stderr, "Created local socket in create_local_socket \n"); + D("LS(%d): created (fd=%d)\n", s->id, s->fd); + return s; +} + +asocket *create_local_service_socket(const char *name) +{ + asocket *s; + int fd; + + fd = service_to_fd(name); + if(fd < 0) return 0; + + s = create_local_socket(fd); + D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); + return s; +} + +/* a Remote socket is used to send/receive data to/from a given transport object +** it needs to be closed when the transport is forcibly destroyed by the user +*/ +typedef struct aremotesocket { + asocket socket; + adisconnect disconnect; +} aremotesocket; + +static int remote_socket_enqueue(asocket *s, apacket *p) +{ + D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); + p->msg.command = A_WRTE; + p->msg.arg0 = s->peer->id; + p->msg.arg1 = s->id; + p->msg.data_length = p->len; + send_packet(p, s->transport); + return 1; +} + +static void remote_socket_ready(asocket *s) +{ + D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = s->peer->id; + p->msg.arg1 = s->id; + send_packet(p, s->transport); +} + +static void remote_socket_close(asocket *s) +{ + D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + s->id, s->fd, s->peer?s->peer->fd:-1); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + if(s->peer) { + p->msg.arg0 = s->peer->id; + s->peer->peer = 0; + D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); + s->peer->close(s->peer); + } + p->msg.arg1 = s->id; + send_packet(p, s->transport); + D("RS(%d): closed\n", s->id); + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +static void remote_socket_disconnect(void* _s, atransport* t) +{ + asocket* s = _s; + asocket* peer = s->peer; + + D("remote_socket_disconnect RS(%d)\n", s->id); + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +asocket *create_remote_socket(unsigned id, atransport *t) +{ + asocket *s = calloc(1, sizeof(aremotesocket)); + adisconnect* dis = &((aremotesocket*)s)->disconnect; + + if (s == NULL) fatal("cannot allocate socket"); + s->id = id; + s->enqueue = remote_socket_enqueue; + s->ready = remote_socket_ready; + s->close = remote_socket_close; + s->transport = t; + + dis->func = remote_socket_disconnect; + dis->opaque = s; + add_transport_disconnect( t, dis ); + D("RS(%d): created\n", s->id); + return s; +} + +void connect_to_remote(asocket *s, const char *destination) +{ + D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd); + apacket *p = get_apacket(); + int len = strlen(destination) + 1; + + if(len > (MAX_PAYLOAD-1)) { + fatal("destination oversized"); + } + + D("LS(%d): connect('%s')\n", s->id, destination); + p->msg.command = A_OPEN; + p->msg.arg0 = s->id; + p->msg.data_length = len; + strcpy((char*) p->data, destination); + send_packet(p, s->transport); +} + + +/* this is used by magic sockets to rig local sockets to + send the go-ahead message when they connect */ +static void local_socket_ready_notify(asocket *s) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + adb_write(s->fd, "OKAY", 4); + s->ready(s); +} + +/* this is used by magic sockets to rig local sockets to + send the failure message if they are closed before + connected (to avoid closing them without a status message) */ +static void local_socket_close_notify(asocket *s) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + sendfailmsg(s->fd, "closed"); + s->close(s); +} + +unsigned unhex(unsigned char *s, int len) +{ + unsigned n = 0, c; + + while(len-- > 0) { + switch((c = *s++)) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + c -= '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + c = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + c = c - 'A' + 10; + break; + default: + return 0xffffffff; + } + + n = (n << 4) | c; + } + + return n; +} + +/* skip_host_serial return the position in a string + skipping over the 'serial' parameter in the ADB protocol, + where parameter string may be a host:port string containing + the protocol delimiter (colon). */ +char *skip_host_serial(char *service) { + char *first_colon, *serial_end; + + first_colon = strchr(service, ':'); + if (!first_colon) { + /* No colon in service string. */ + return NULL; + } + serial_end = first_colon; + if (isdigit(serial_end[1])) { + serial_end++; + while ((*serial_end) && isdigit(*serial_end)) { + serial_end++; + } + if ((*serial_end) != ':') { + // Something other than numbers was found, reset the end. + serial_end = first_colon; + } + } + return serial_end; +} + +static int smart_socket_enqueue(asocket *s, apacket *p) +{ + unsigned len; + + D("SS(%d): enqueue %d\n", s->id, p->len); + + if(s->pkt_first == 0) { + s->pkt_first = p; + s->pkt_last = p; + } else { + if((s->pkt_first->len + p->len) > MAX_PAYLOAD) { + D("SS(%d): overflow\n", s->id); + put_apacket(p); + goto fail; + } + + memcpy(s->pkt_first->data + s->pkt_first->len, + p->data, p->len); + s->pkt_first->len += p->len; + put_apacket(p); + + p = s->pkt_first; + } + + /* don't bother if we can't decode the length */ + if(p->len < 4) return 0; + + len = unhex(p->data, 4); + if((len < 1) || (len > 1024)) { + D("SS(%d): bad size (%d)\n", s->id, len); + goto fail; + } + + D("SS(%d): len is %d\n", s->id, len ); + /* can't do anything until we have the full header */ + if((len + 4) > p->len) { + D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len); + return 0; + } + + p->data[len + 4] = 0; + + D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4)); + + if (s->transport == NULL) { + char* error_string = "unknown failure"; + s->transport = acquire_one_transport (CS_ANY, + kTransportAny, NULL, &error_string); + + if (s->transport == NULL) { + sendfailmsg(s->peer->fd, error_string); + goto fail; + } + } + + if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) { + /* if there's no remote we fail the connection + ** right here and terminate it + */ + sendfailmsg(s->peer->fd, "device offline (x)"); + goto fail; + } + + + /* instrument our peer to pass the success or fail + ** message back once it connects or closes, then + ** detach from it, request the connection, and + ** tear down + */ + s->peer->ready = local_socket_ready_notify; + s->peer->close = local_socket_close_notify; + s->peer->peer = 0; + /* give him our transport and upref it */ + s->peer->transport = s->transport; + + connect_to_remote(s->peer, (char*) (p->data + 4)); + s->peer = 0; + s->close(s); + return 1; + +fail: + /* we're going to close our peer as a side-effect, so + ** return -1 to signal that state to the local socket + ** who is enqueueing against us + */ + s->close(s); + return -1; +} + +static void smart_socket_ready(asocket *s) +{ + D("SS(%d): ready\n", s->id); +} + +static void smart_socket_close(asocket *s) +{ + D("SS(%d): closed\n", s->id); + if(s->pkt_first){ + put_apacket(s->pkt_first); + } + if(s->peer) { + s->peer->peer = 0; + s->peer->close(s->peer); + s->peer = 0; + } + free(s); +} + +asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act)) +{ + D("Creating smart socket \n"); + asocket *s = calloc(1, sizeof(asocket)); + if (s == NULL) fatal("cannot allocate socket"); + s->enqueue = smart_socket_enqueue; + s->ready = smart_socket_ready; + s->close = smart_socket_close; + s->extra = action_cb; + + D("SS(%d): created %p\n", s->id, action_cb); + return s; +} + +void smart_socket_action(asocket *s, const char *act) +{ + +} + +void connect_to_smartsocket(asocket *s) +{ + D("Connecting to smart socket \n"); + asocket *ss = create_smart_socket(smart_socket_action); + s->peer = ss; + ss->peer = s; + s->ready(s); +} diff --git a/minadbd/sysdeps.h b/minadbd/sysdeps.h new file mode 100644 index 000000000..800ddb753 --- /dev/null +++ b/minadbd/sysdeps.h @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* this file contains system-dependent definitions used by ADB + * they're related to threads, sockets and file descriptors + */ +#ifndef _ADB_SYSDEPS_H +#define _ADB_SYSDEPS_H + +#ifdef __CYGWIN__ +# undef _WIN32 +#endif + +#ifdef _WIN32 + +#include <windows.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#include <process.h> +#include <fcntl.h> +#include <io.h> +#include <sys/stat.h> +#include <errno.h> +#include <ctype.h> + +#define OS_PATH_SEPARATOR '\\' +#define OS_PATH_SEPARATOR_STR "\\" + +typedef CRITICAL_SECTION adb_mutex_t; + +#define ADB_MUTEX_DEFINE(x) adb_mutex_t x + +/* declare all mutexes */ +/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */ +#define ADB_MUTEX(x) extern adb_mutex_t x; +#include "mutex_list.h" + +extern void adb_sysdeps_init(void); + +static __inline__ void adb_mutex_lock( adb_mutex_t* lock ) +{ + EnterCriticalSection( lock ); +} + +static __inline__ void adb_mutex_unlock( adb_mutex_t* lock ) +{ + LeaveCriticalSection( lock ); +} + +typedef struct { unsigned tid; } adb_thread_t; + +typedef void* (*adb_thread_func_t)(void* arg); + +typedef void (*win_thread_func_t)(void* arg); + +static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg) +{ + thread->tid = _beginthread( (win_thread_func_t)func, 0, arg ); + if (thread->tid == (unsigned)-1L) { + return -1; + } + return 0; +} + +static __inline__ void close_on_exec(int fd) +{ + /* nothing really */ +} + +extern void disable_tcp_nagle(int fd); + +#define lstat stat /* no symlinks on Win32 */ + +#define S_ISLNK(m) 0 /* no symlinks on Win32 */ + +static __inline__ int adb_unlink(const char* path) +{ + int rc = unlink(path); + + if (rc == -1 && errno == EACCES) { + /* unlink returns EACCES when the file is read-only, so we first */ + /* try to make it writable, then unlink again... */ + rc = chmod(path, _S_IREAD|_S_IWRITE ); + if (rc == 0) + rc = unlink(path); + } + return rc; +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return _mkdir(path); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +extern int adb_open(const char* path, int options); +extern int adb_creat(const char* path, int mode); +extern int adb_read(int fd, void* buf, int len); +extern int adb_write(int fd, const void* buf, int len); +extern int adb_lseek(int fd, int pos, int where); +extern int adb_shutdown(int fd); +extern int adb_close(int fd); + +static __inline__ int unix_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + +static __inline__ int unix_read(int fd, void* buf, size_t len) +{ + return read(fd, buf, len); +} +#undef read +#define read ___xxx_read + +static __inline__ int unix_write(int fd, const void* buf, size_t len) +{ + return write(fd, buf, len); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_open_mode(const char* path, int options, int mode) +{ + return adb_open(path, options); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return open(path, options); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return open(path, options, mode); + } +} +#define open ___xxx_unix_open + + +/* normally provided by <cutils/misc.h> */ +extern void* load_file(const char* pathname, unsigned* psize); + +/* normally provided by <cutils/sockets.h> */ +extern int socket_loopback_client(int port, int type); +extern int socket_network_client(const char *host, int port, int type); +extern int socket_loopback_server(int port, int type); +extern int socket_inaddr_any_server(int port, int type); + +/* normally provided by "fdevent.h" */ + +#define FDE_READ 0x0001 +#define FDE_WRITE 0x0002 +#define FDE_ERROR 0x0004 +#define FDE_DONT_CLOSE 0x0080 + +typedef struct fdevent fdevent; + +typedef void (*fd_func)(int fd, unsigned events, void *userdata); + +fdevent *fdevent_create(int fd, fd_func func, void *arg); +void fdevent_destroy(fdevent *fde); +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg); +void fdevent_remove(fdevent *item); +void fdevent_set(fdevent *fde, unsigned events); +void fdevent_add(fdevent *fde, unsigned events); +void fdevent_del(fdevent *fde, unsigned events); +void fdevent_loop(); + +struct fdevent { + fdevent *next; + fdevent *prev; + + int fd; + int force_eof; + + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + Sleep( mseconds ); +} + +extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen); + +#undef accept +#define accept ___xxx_accept + +static __inline__ int adb_socket_setbufsize( int fd, int bufsize ) +{ + int opt = bufsize; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt)); +} + +extern int adb_socketpair( int sv[2] ); + +static __inline__ char* adb_dirstart( const char* path ) +{ + char* p = strchr(path, '/'); + char* p2 = strchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ char* adb_dirstop( const char* path ) +{ + char* p = strrchr(path, '/'); + char* p2 = strrchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; +} + +#else /* !_WIN32 a.k.a. Unix */ + +#include "fdevent.h" +#include <cutils/sockets.h> +#include <cutils/properties.h> +#include <cutils/misc.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <pthread.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <string.h> + +#define OS_PATH_SEPARATOR '/' +#define OS_PATH_SEPARATOR_STR "/" + +typedef pthread_mutex_t adb_mutex_t; + +#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define adb_mutex_init pthread_mutex_init +#define adb_mutex_lock pthread_mutex_lock +#define adb_mutex_unlock pthread_mutex_unlock +#define adb_mutex_destroy pthread_mutex_destroy + +#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER + +#define adb_cond_t pthread_cond_t +#define adb_cond_init pthread_cond_init +#define adb_cond_wait pthread_cond_wait +#define adb_cond_broadcast pthread_cond_broadcast +#define adb_cond_signal pthread_cond_signal +#define adb_cond_destroy pthread_cond_destroy + +/* declare all mutexes */ +#define ADB_MUTEX(x) extern adb_mutex_t x; +#include "mutex_list.h" + +static __inline__ void close_on_exec(int fd) +{ + fcntl( fd, F_SETFD, FD_CLOEXEC ); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return open(path, options); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return open(path, options, mode); + } +} + +static __inline__ int adb_open_mode( const char* pathname, int options, int mode ) +{ + return open( pathname, options, mode ); +} + +static __inline__ int adb_creat(const char* path, int mode) +{ + int fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode); + + if ( fd < 0 ) + return -1; + + close_on_exec(fd); + return fd; +} +#undef creat +#define creat ___xxx_creat + +static __inline__ int adb_open( const char* pathname, int options ) +{ + int fd = open( pathname, options ); + if (fd < 0) + return -1; + close_on_exec( fd ); + return fd; +} +#undef open +#define open ___xxx_open + +static __inline__ int adb_shutdown(int fd) +{ + return shutdown(fd, SHUT_RDWR); +} +#undef shutdown +#define shutdown ____xxx_shutdown + +static __inline__ int adb_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + + +static __inline__ int adb_read(int fd, void* buf, size_t len) +{ + return read(fd, buf, len); +} + +#undef read +#define read ___xxx_read + +static __inline__ int adb_write(int fd, const void* buf, size_t len) +{ + return write(fd, buf, len); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_lseek(int fd, int pos, int where) +{ + return lseek(fd, pos, where); +} +#undef lseek +#define lseek ___xxx_lseek + +static __inline__ int adb_unlink(const char* path) +{ + return unlink(path); +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) +{ + int fd; + + fd = accept(serverfd, addr, addrlen); + if (fd >= 0) + close_on_exec(fd); + + return fd; +} + +#undef accept +#define accept ___xxx_accept + +#define unix_read adb_read +#define unix_write adb_write +#define unix_close adb_close + +typedef pthread_t adb_thread_t; + +typedef void* (*adb_thread_func_t)( void* arg ); + +static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg ) +{ + pthread_attr_t attr; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + return pthread_create( pthread, &attr, start, arg ); +} + +static __inline__ int adb_socket_setbufsize( int fd, int bufsize ) +{ + int opt = bufsize; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); +} + +static __inline__ void disable_tcp_nagle(int fd) +{ + int on = 1; + setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) ); +} + + +static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] ) +{ + return socketpair( d, type, protocol, sv ); +} + +static __inline__ int adb_socketpair( int sv[2] ) +{ + int rc; + + rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); + if (rc < 0) + return -1; + + close_on_exec( sv[0] ); + close_on_exec( sv[1] ); + return 0; +} + +#undef socketpair +#define socketpair ___xxx_socketpair + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + usleep( mseconds*1000 ); +} + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return mkdir(path, mode); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +static __inline__ void adb_sysdeps_init(void) +{ +} + +static __inline__ char* adb_dirstart(const char* path) +{ + return strchr(path, '/'); +} + +static __inline__ char* adb_dirstop(const char* path) +{ + return strrchr(path, '/'); +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return path[0] == '/'; +} + +#endif /* !_WIN32 */ + +#endif /* _ADB_SYSDEPS_H */ diff --git a/minadbd/transport.c b/minadbd/transport.c new file mode 100644 index 000000000..ff2004932 --- /dev/null +++ b/minadbd/transport.c @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +static void transport_unref(atransport *t); + +static atransport transport_list = { + .next = &transport_list, + .prev = &transport_list, +}; + +ADB_MUTEX_DEFINE( transport_lock ); + +#if ADB_TRACE +#define MAX_DUMP_HEX_LEN 16 +static void dump_hex( const unsigned char* ptr, size_t len ) +{ + int nn, len2 = len; + // Build a string instead of logging each character. + // MAX chars in 2 digit hex, one space, MAX chars, one '\0'. + char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer; + + if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN; + + for (nn = 0; nn < len2; nn++) { + sprintf(pb, "%02x", ptr[nn]); + pb += 2; + } + sprintf(pb++, " "); + + for (nn = 0; nn < len2; nn++) { + int c = ptr[nn]; + if (c < 32 || c > 127) + c = '.'; + *pb++ = c; + } + *pb++ = '\0'; + DR("%s\n", buffer); +} +#endif + +void +kick_transport(atransport* t) +{ + if (t && !t->kicked) + { + int kicked; + + adb_mutex_lock(&transport_lock); + kicked = t->kicked; + if (!kicked) + t->kicked = 1; + adb_mutex_unlock(&transport_lock); + + if (!kicked) + t->kick(t); + } +} + +void +run_transport_disconnects(atransport* t) +{ + adisconnect* dis = t->disconnects.next; + + D("%s: run_transport_disconnects\n", t->serial); + while (dis != &t->disconnects) { + adisconnect* next = dis->next; + dis->func( dis->opaque, t ); + dis = next; + } +} + +#if ADB_TRACE +static void +dump_packet(const char* name, const char* func, apacket* p) +{ + unsigned command = p->msg.command; + int len = p->msg.data_length; + char cmd[9]; + char arg0[12], arg1[12]; + int n; + + for (n = 0; n < 4; n++) { + int b = (command >> (n*8)) & 255; + if (b < 32 || b >= 127) + break; + cmd[n] = (char)b; + } + if (n == 4) { + cmd[4] = 0; + } else { + /* There is some non-ASCII name in the command, so dump + * the hexadecimal value instead */ + snprintf(cmd, sizeof cmd, "%08x", command); + } + + if (p->msg.arg0 < 256U) + snprintf(arg0, sizeof arg0, "%d", p->msg.arg0); + else + snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0); + + if (p->msg.arg1 < 256U) + snprintf(arg1, sizeof arg1, "%d", p->msg.arg1); + else + snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1); + + D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", + name, func, cmd, arg0, arg1, len); + dump_hex(p->data, len); +} +#endif /* ADB_TRACE */ + +static int +read_packet(int fd, const char* name, apacket** ppacket) +{ + char *p = (char*)ppacket; /* really read a packet address */ + int r; + int len = sizeof(*ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno)); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + +#if ADB_TRACE + if (ADB_TRACING) { + dump_packet(name, "from remote", *ppacket); + } +#endif + return 0; +} + +static int +write_packet(int fd, const char* name, apacket** ppacket) +{ + char *p = (char*) ppacket; /* we really write the packet address */ + int r, len = sizeof(ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } + +#if ADB_TRACE + if (ADB_TRACING) { + dump_packet(name, "to remote", *ppacket); + } +#endif + len = sizeof(ppacket); + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno)); + if((r < 0) && (errno == EINTR)) continue; + return -1; + } + } + return 0; +} + +static void transport_socket_events(int fd, unsigned events, void *_t) +{ + atransport *t = _t; + D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events); + if(events & FDE_READ){ + apacket *p = 0; + if(read_packet(fd, t->serial, &p)){ + D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd); + } else { + handle_packet(p, (atransport *) _t); + } + } +} + +void send_packet(apacket *p, atransport *t) +{ + unsigned char *x; + unsigned sum; + unsigned count; + + p->msg.magic = p->msg.command ^ 0xffffffff; + + count = p->msg.data_length; + x = (unsigned char *) p->data; + sum = 0; + while(count-- > 0){ + sum += *x++; + } + p->msg.data_check = sum; + + print_packet("send", p); + + if (t == NULL) { + D("Transport is null \n"); + // Zap errno because print_packet() and other stuff have errno effect. + errno = 0; + fatal_errno("Transport is null"); + } + + if(write_packet(t->transport_socket, t->serial, &p)){ + fatal_errno("cannot enqueue packet on transport socket"); + } +} + +/* The transport is opened by transport_register_func before +** the input and output threads are started. +** +** The output thread issues a SYNC(1, token) message to let +** the input thread know to start things up. In the event +** of transport IO failure, the output thread will post a +** SYNC(0,0) message to ensure shutdown. +** +** The transport will not actually be closed until both +** threads exit, but the input thread will kick the transport +** on its way out to disconnect the underlying device. +*/ + +static void *output_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + + D("%s: starting transport output thread on fd %d, SYNC online (%d)\n", + t->serial, t->fd, t->sync_token + 1); + p = get_apacket(); + p->msg.command = A_SYNC; + p->msg.arg0 = 1; + p->msg.arg1 = ++(t->sync_token); + p->msg.magic = A_SYNC ^ 0xffffffff; + if(write_packet(t->fd, t->serial, &p)) { + put_apacket(p); + D("%s: failed to write SYNC packet\n", t->serial); + goto oops; + } + + D("%s: data pump started\n", t->serial); + for(;;) { + p = get_apacket(); + + if(t->read_from_remote(p, t) == 0){ + D("%s: received remote packet, sending to transport\n", + t->serial); + if(write_packet(t->fd, t->serial, &p)){ + put_apacket(p); + D("%s: failed to write apacket to transport\n", t->serial); + goto oops; + } + } else { + D("%s: remote read failed for transport\n", t->serial); + put_apacket(p); + break; + } + } + + D("%s: SYNC offline for transport\n", t->serial); + p = get_apacket(); + p->msg.command = A_SYNC; + p->msg.arg0 = 0; + p->msg.arg1 = 0; + p->msg.magic = A_SYNC ^ 0xffffffff; + if(write_packet(t->fd, t->serial, &p)) { + put_apacket(p); + D("%s: failed to write SYNC apacket to transport", t->serial); + } + +oops: + D("%s: transport output thread is exiting\n", t->serial); + kick_transport(t); + transport_unref(t); + return 0; +} + +static void *input_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + int active = 0; + + D("%s: starting transport input thread, reading from fd %d\n", + t->serial, t->fd); + + for(;;){ + if(read_packet(t->fd, t->serial, &p)) { + D("%s: failed to read apacket from transport on fd %d\n", + t->serial, t->fd ); + break; + } + if(p->msg.command == A_SYNC){ + if(p->msg.arg0 == 0) { + D("%s: transport SYNC offline\n", t->serial); + put_apacket(p); + break; + } else { + if(p->msg.arg1 == t->sync_token) { + D("%s: transport SYNC online\n", t->serial); + active = 1; + } else { + D("%s: transport ignoring SYNC %d != %d\n", + t->serial, p->msg.arg1, t->sync_token); + } + } + } else { + if(active) { + D("%s: transport got packet, sending to remote\n", t->serial); + t->write_to_remote(p, t); + } else { + D("%s: transport ignoring packet while offline\n", t->serial); + } + } + + put_apacket(p); + } + + // this is necessary to avoid a race condition that occured when a transport closes + // while a client socket is still active. + close_all_sockets(t); + + D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd); + kick_transport(t); + transport_unref(t); + return 0; +} + + +static int transport_registration_send = -1; +static int transport_registration_recv = -1; +static fdevent transport_registration_fde; + +void update_transports(void) +{ + // nothing to do on the device side +} + +typedef struct tmsg tmsg; +struct tmsg +{ + atransport *transport; + int action; +}; + +static int +transport_read_action(int fd, struct tmsg* m) +{ + char *p = (char*)m; + int len = sizeof(*m); + int r; + + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if((r < 0) && (errno == EINTR)) continue; + D("transport_read_action: on fd %d, error %d: %s\n", + fd, errno, strerror(errno)); + return -1; + } + } + return 0; +} + +static int +transport_write_action(int fd, struct tmsg* m) +{ + char *p = (char*)m; + int len = sizeof(*m); + int r; + + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if((r < 0) && (errno == EINTR)) continue; + D("transport_write_action: on fd %d, error %d: %s\n", + fd, errno, strerror(errno)); + return -1; + } + } + return 0; +} + +static void transport_registration_func(int _fd, unsigned ev, void *data) +{ + tmsg m; + adb_thread_t output_thread_ptr; + adb_thread_t input_thread_ptr; + int s[2]; + atransport *t; + + if(!(ev & FDE_READ)) { + return; + } + + if(transport_read_action(_fd, &m)) { + fatal_errno("cannot read transport registration socket"); + } + + t = m.transport; + + if(m.action == 0){ + D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket); + + /* IMPORTANT: the remove closes one half of the + ** socket pair. The close closes the other half. + */ + fdevent_remove(&(t->transport_fde)); + adb_close(t->fd); + + adb_mutex_lock(&transport_lock); + t->next->prev = t->prev; + t->prev->next = t->next; + adb_mutex_unlock(&transport_lock); + + run_transport_disconnects(t); + + if (t->product) + free(t->product); + if (t->serial) + free(t->serial); + + memset(t,0xee,sizeof(atransport)); + free(t); + + update_transports(); + return; + } + + /* don't create transport threads for inaccessible devices */ + if (t->connection_state != CS_NOPERM) { + /* initial references are the two threads */ + t->ref_count = 2; + + if(adb_socketpair(s)) { + fatal_errno("cannot open transport socketpair"); + } + + D("transport: %s (%d,%d) starting\n", t->serial, s[0], s[1]); + + t->transport_socket = s[0]; + t->fd = s[1]; + + fdevent_install(&(t->transport_fde), + t->transport_socket, + transport_socket_events, + t); + + fdevent_set(&(t->transport_fde), FDE_READ); + + if(adb_thread_create(&input_thread_ptr, input_thread, t)){ + fatal_errno("cannot create input thread"); + } + + if(adb_thread_create(&output_thread_ptr, output_thread, t)){ + fatal_errno("cannot create output thread"); + } + } + + /* put us on the master device list */ + adb_mutex_lock(&transport_lock); + t->next = &transport_list; + t->prev = transport_list.prev; + t->next->prev = t; + t->prev->next = t; + adb_mutex_unlock(&transport_lock); + + t->disconnects.next = t->disconnects.prev = &t->disconnects; + + update_transports(); +} + +void init_transport_registration(void) +{ + int s[2]; + + if(adb_socketpair(s)){ + fatal_errno("cannot open transport registration socketpair"); + } + + transport_registration_send = s[0]; + transport_registration_recv = s[1]; + + fdevent_install(&transport_registration_fde, + transport_registration_recv, + transport_registration_func, + 0); + + fdevent_set(&transport_registration_fde, FDE_READ); +} + +/* the fdevent select pump is single threaded */ +static void register_transport(atransport *transport) +{ + tmsg m; + m.transport = transport; + m.action = 1; + D("transport: %s registered\n", transport->serial); + if(transport_write_action(transport_registration_send, &m)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + +static void remove_transport(atransport *transport) +{ + tmsg m; + m.transport = transport; + m.action = 0; + D("transport: %s removed\n", transport->serial); + if(transport_write_action(transport_registration_send, &m)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + + +static void transport_unref_locked(atransport *t) +{ + t->ref_count--; + if (t->ref_count == 0) { + D("transport: %s unref (kicking and closing)\n", t->serial); + if (!t->kicked) { + t->kicked = 1; + t->kick(t); + } + t->close(t); + remove_transport(t); + } else { + D("transport: %s unref (count=%d)\n", t->serial, t->ref_count); + } +} + +static void transport_unref(atransport *t) +{ + if (t) { + adb_mutex_lock(&transport_lock); + transport_unref_locked(t); + adb_mutex_unlock(&transport_lock); + } +} + +void add_transport_disconnect(atransport* t, adisconnect* dis) +{ + adb_mutex_lock(&transport_lock); + dis->next = &t->disconnects; + dis->prev = dis->next->prev; + dis->prev->next = dis; + dis->next->prev = dis; + adb_mutex_unlock(&transport_lock); +} + +void remove_transport_disconnect(atransport* t, adisconnect* dis) +{ + dis->prev->next = dis->next; + dis->next->prev = dis->prev; + dis->next = dis->prev = dis; +} + + +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out) +{ + atransport *t; + atransport *result = NULL; + int ambiguous = 0; + +retry: + if (error_out) + *error_out = "device not found"; + + adb_mutex_lock(&transport_lock); + for (t = transport_list.next; t != &transport_list; t = t->next) { + if (t->connection_state == CS_NOPERM) { + if (error_out) + *error_out = "insufficient permissions for device"; + continue; + } + + /* check for matching serial number */ + if (serial) { + if (t->serial && !strcmp(serial, t->serial)) { + result = t; + break; + } + } else { + if (ttype == kTransportUsb && t->type == kTransportUsb) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportLocal && t->type == kTransportLocal) { + if (result) { + if (error_out) + *error_out = "more than one emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportAny) { + if (result) { + if (error_out) + *error_out = "more than one device and emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } + } + } + adb_mutex_unlock(&transport_lock); + + if (result) { + /* offline devices are ignored -- they are either being born or dying */ + if (result && result->connection_state == CS_OFFLINE) { + if (error_out) + *error_out = "device offline"; + result = NULL; + } + /* check for required connection state */ + if (result && state != CS_ANY && result->connection_state != state) { + if (error_out) + *error_out = "invalid device state"; + result = NULL; + } + } + + if (result) { + /* found one that we can take */ + if (error_out) + *error_out = NULL; + } else if (state != CS_ANY && (serial || !ambiguous)) { + adb_sleep_ms(1000); + goto retry; + } + + return result; +} + +void register_socket_transport(int s, const char *serial, int port, int local) +{ + atransport *t = calloc(1, sizeof(atransport)); + char buff[32]; + + if (!serial) { + snprintf(buff, sizeof buff, "T-%p", t); + serial = buff; + } + D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port); + if ( init_socket_transport(t, s, port, local) < 0 ) { + adb_close(s); + free(t); + return; + } + if(serial) { + t->serial = strdup(serial); + } + register_transport(t); +} + +void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) +{ + atransport *t = calloc(1, sizeof(atransport)); + D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, + serial ? serial : ""); + init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM)); + if(serial) { + t->serial = strdup(serial); + } + register_transport(t); +} + +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb) +{ + atransport *t; + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if (t->usb == usb && t->connection_state == CS_NOPERM) { + t->next->prev = t->prev; + t->prev->next = t->next; + break; + } + } + adb_mutex_unlock(&transport_lock); +} + +#undef TRACE_TAG +#define TRACE_TAG TRACE_RWX + +int readx(int fd, void *ptr, size_t len) +{ + char *p = ptr; + int r; +#if ADB_TRACE + int len0 = len; +#endif + D("readx: fd=%d wanted=%d\n", fd, (int)len); + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if (r < 0) { + D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("readx: fd=%d disconnected\n", fd); + } + return -1; + } + } + +#if ADB_TRACE + D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len); + dump_hex( ptr, len0 ); +#endif + return 0; +} + +int writex(int fd, const void *ptr, size_t len) +{ + char *p = (char*) ptr; + int r; + +#if ADB_TRACE + D("writex: fd=%d len=%d: ", fd, (int)len); + dump_hex( ptr, len ); +#endif + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if (r < 0) { + D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("writex: fd=%d disconnected\n", fd); + } + return -1; + } + } + return 0; +} + +int check_header(apacket *p) +{ + if(p->msg.magic != (p->msg.command ^ 0xffffffff)) { + D("check_header(): invalid magic\n"); + return -1; + } + + if(p->msg.data_length > MAX_PAYLOAD) { + D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length); + return -1; + } + + return 0; +} + +int check_data(apacket *p) +{ + unsigned count, sum; + unsigned char *x; + + count = p->msg.data_length; + x = p->data; + sum = 0; + while(count-- > 0) { + sum += *x++; + } + + if(sum != p->msg.data_check) { + return -1; + } else { + return 0; + } +} diff --git a/minadbd/transport.h b/minadbd/transport.h new file mode 100644 index 000000000..992e05285 --- /dev/null +++ b/minadbd/transport.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TRANSPORT_H +#define __TRANSPORT_H + +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); +#endif /* __TRANSPORT_H */ diff --git a/minadbd/transport_usb.c b/minadbd/transport_usb.c new file mode 100644 index 000000000..91cbf6151 --- /dev/null +++ b/minadbd/transport_usb.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sysdeps.h> + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +#ifdef HAVE_BIG_ENDIAN +#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) +static inline void fix_endians(apacket *p) +{ + p->msg.command = H4(p->msg.command); + p->msg.arg0 = H4(p->msg.arg0); + p->msg.arg1 = H4(p->msg.arg1); + p->msg.data_length = H4(p->msg.data_length); + p->msg.data_check = H4(p->msg.data_check); + p->msg.magic = H4(p->msg.magic); +} +unsigned host_to_le32(unsigned n) +{ + return H4(n); +} +#else +#define fix_endians(p) do {} while (0) +unsigned host_to_le32(unsigned n) +{ + return n; +} +#endif + +static int remote_read(apacket *p, atransport *t) +{ + if(usb_read(t->usb, &p->msg, sizeof(amessage))){ + D("remote usb: read terminated (message)\n"); + return -1; + } + + fix_endians(p); + + if(check_header(p)) { + D("remote usb: check_header failed\n"); + return -1; + } + + if(p->msg.data_length) { + if(usb_read(t->usb, p->data, p->msg.data_length)){ + D("remote usb: terminated (data)\n"); + return -1; + } + } + + if(check_data(p)) { + D("remote usb: check_data failed\n"); + return -1; + } + + return 0; +} + +static int remote_write(apacket *p, atransport *t) +{ + unsigned size = p->msg.data_length; + + fix_endians(p); + + if(usb_write(t->usb, &p->msg, sizeof(amessage))) { + D("remote usb: 1 - write terminated\n"); + return -1; + } + if(p->msg.data_length == 0) return 0; + if(usb_write(t->usb, &p->data, size)) { + D("remote usb: 2 - write terminated\n"); + return -1; + } + + return 0; +} + +static void remote_close(atransport *t) +{ + usb_close(t->usb); + t->usb = 0; +} + +static void remote_kick(atransport *t) +{ + usb_kick(t->usb); +} + +void init_usb_transport(atransport *t, usb_handle *h, int state) +{ + D("transport: usb\n"); + t->close = remote_close; + t->kick = remote_kick; + t->read_from_remote = remote_read; + t->write_to_remote = remote_write; + t->sync_token = 1; + t->connection_state = state; + t->type = kTransportUsb; + t->usb = h; + + HOST = 0; +} diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c new file mode 100644 index 000000000..635fa4bbb --- /dev/null +++ b/minadbd/usb_linux_client.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + + +struct usb_handle +{ + int fd; + adb_cond_t notify; + adb_mutex_t lock; +}; + +void usb_cleanup() +{ + // nothing to do here +} + +static void *usb_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + int fd; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->fd != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + D("[ usb_thread - opening device ]\n"); + do { + /* XXX use inotify? */ + fd = unix_open("/dev/android_adb", O_RDWR); + if (fd < 0) { + // to support older kernels + fd = unix_open("/dev/android", O_RDWR); + } + if (fd < 0) { + adb_sleep_ms(1000); + } + } while (fd < 0); + D("[ opening device succeeded ]\n"); + + close_on_exec(fd); + usb->fd = fd; + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0, 1); + } + + // never gets here + return 0; +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->fd, len); + n = adb_write(h->fd, data, len); + if(n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->fd); + return 0; +} + +int usb_read(usb_handle *h, void *data, int len) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->fd, len); + n = adb_read(h->fd, data, len); + if(n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->fd); + return 0; +} + +void usb_init() +{ + usb_handle *h; + adb_thread_t tid; + int fd; + + h = calloc(1, sizeof(usb_handle)); + h->fd = -1; + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + // Open the file /dev/android_adb_enable to trigger + // the enabling of the adb USB function in the kernel. + // We never touch this file again - just leave it open + // indefinitely so the kernel will know when we are running + // and when we are not. + fd = unix_open("/dev/android_adb_enable", O_RDWR); + if (fd < 0) { + D("failed to open /dev/android_adb_enable\n"); + } else { + close_on_exec(fd); + } + + D("[ usb_init - starting thread ]\n"); + if(adb_thread_create(&tid, usb_open_thread, h)){ + fatal_errno("cannot create usb thread"); + } +} + +void usb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +int usb_close(usb_handle *h) +{ + // nothing to do here + return 0; +} diff --git a/minadbd/utils.c b/minadbd/utils.c new file mode 100644 index 000000000..91518bab6 --- /dev/null +++ b/minadbd/utils.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "utils.h" +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +char* +buff_addc (char* buff, char* buffEnd, int c) +{ + int avail = buffEnd - buff; + + if (avail <= 0) /* already in overflow mode */ + return buff; + + if (avail == 1) { /* overflowing, the last byte is reserved for zero */ + buff[0] = 0; + return buff + 1; + } + + buff[0] = (char) c; /* add char and terminating zero */ + buff[1] = 0; + return buff + 1; +} + +char* +buff_adds (char* buff, char* buffEnd, const char* s) +{ + int slen = strlen(s); + + return buff_addb(buff, buffEnd, s, slen); +} + +char* +buff_addb (char* buff, char* buffEnd, const void* data, int len) +{ + int avail = (buffEnd - buff); + + if (avail <= 0 || len <= 0) /* already overflowing */ + return buff; + + if (len > avail) + len = avail; + + memcpy(buff, data, len); + + buff += len; + + /* ensure there is a terminating zero */ + if (buff >= buffEnd) { /* overflow */ + buff[-1] = 0; + } else + buff[0] = 0; + + return buff; +} + +char* +buff_add (char* buff, char* buffEnd, const char* format, ... ) +{ + int avail; + + avail = (buffEnd - buff); + + if (avail > 0) { + va_list args; + int nn; + + va_start(args, format); + nn = vsnprintf( buff, avail, format, args); + va_end(args); + + if (nn < 0) { + /* some C libraries return -1 in case of overflow, + * but they will also do that if the format spec is + * invalid. We assume ADB is not buggy enough to + * trigger that last case. */ + nn = avail; + } + else if (nn > avail) { + nn = avail; + } + + buff += nn; + + /* ensure that there is a terminating zero */ + if (buff >= buffEnd) + buff[-1] = 0; + else + buff[0] = 0; + } + return buff; +} diff --git a/minadbd/utils.h b/minadbd/utils.h new file mode 100644 index 000000000..f70ecd24d --- /dev/null +++ b/minadbd/utils.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _ADB_UTILS_H +#define _ADB_UTILS_H + +/* bounded buffer functions */ + +/* all these functions are used to append data to a bounded buffer. + * + * after each operation, the buffer is guaranteed to be zero-terminated, + * even in the case of an overflow. they all return the new buffer position + * which allows one to use them in succession, only checking for overflows + * at the end. For example: + * + * BUFF_DECL(temp,p,end,1024); + * char* p; + * + * p = buff_addc(temp, end, '"'); + * p = buff_adds(temp, end, string); + * p = buff_addc(temp, end, '"'); + * + * if (p >= end) { + * overflow detected. note that 'temp' is + * zero-terminated for safety. + * } + * return strdup(temp); + */ + +/* tries to add a character to the buffer, in case of overflow + * this will only write a terminating zero and return buffEnd. + */ +char* buff_addc (char* buff, char* buffEnd, int c); + +/* tries to add a string to the buffer */ +char* buff_adds (char* buff, char* buffEnd, const char* s); + +/* tries to add a bytes to the buffer. the input can contain zero bytes, + * but a terminating zero will always be appended at the end anyway + */ +char* buff_addb (char* buff, char* buffEnd, const void* data, int len); + +/* tries to add a formatted string to a bounded buffer */ +char* buff_add (char* buff, char* buffEnd, const char* format, ... ); + +/* convenience macro used to define a bounded buffer, as well as + * a 'cursor' and 'end' variables all in one go. + * + * note: this doesn't place an initial terminating zero in the buffer, + * you need to use one of the buff_ functions for this. or simply + * do _cursor[0] = 0 manually. + */ +#define BUFF_DECL(_buff,_cursor,_end,_size) \ + char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size) + +#endif /* _ADB_UTILS_H */ |