aboutsummaryrefslogtreecommitdiffstats
path: root/minadbd
diff options
context:
space:
mode:
authorDoug Zongker <dougz@android.com>2012-01-09 15:16:13 -0800
committerDoug Zongker <dougz@android.com>2012-01-10 10:18:17 -0800
commit9270a20a801403c9f60d6a701b39eae70d380403 (patch)
tree5bdb058af5b05a65112297a504018ee356e0ddbb /minadbd
parent210f887382e0fd7e51ec6ce071972374a76f0722 (diff)
downloadbootable_recovery-9270a20a801403c9f60d6a701b39eae70d380403.zip
bootable_recovery-9270a20a801403c9f60d6a701b39eae70d380403.tar.gz
bootable_recovery-9270a20a801403c9f60d6a701b39eae70d380403.tar.bz2
support "sideload over ADB" mode
Rather than depending on the existence of some place to store a file that is accessible to users on an an unbootable device (eg, a physical sdcard, external USB drive, etc.), add support for sideloading packages sent to the device with adb. This change adds a "minimal adbd" which supports nothing but receiving a package over adb (with the "adb sideload" command) and storing it to a fixed filename in the /tmp ramdisk, from where it can be verified and sideloaded in the usual way. This should be leave available even on locked user-build devices. The user can select "apply package from ADB" from the recovery menu, which starts minimal-adb mode (shutting down any real adbd that may be running). Once minimal-adb has received a package it exits (restarting real adbd if appropriate) and then verification and installation of the received package proceeds. Change-Id: I6fe13161ca064a98d06fa32104e1f432826582f5
Diffstat (limited to 'minadbd')
-rw-r--r--minadbd/Android.mk32
-rw-r--r--minadbd/README.txt27
-rw-r--r--minadbd/adb.c1151
-rw-r--r--minadbd/adb.h443
-rw-r--r--minadbd/fdevent.c695
-rw-r--r--minadbd/fdevent.h83
-rw-r--r--minadbd/mutex_list.h26
-rw-r--r--minadbd/services.c161
-rw-r--r--minadbd/sockets.c830
-rw-r--r--minadbd/sysdeps.h495
-rw-r--r--minadbd/transport.c1081
-rw-r--r--minadbd/transport.h26
-rw-r--r--minadbd/transport_usb.c148
-rw-r--r--minadbd/usb_linux_client.c157
-rw-r--r--minadbd/utils.c106
-rw-r--r--minadbd/utils.h68
16 files changed, 5529 insertions, 0 deletions
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
new file mode 100644
index 0000000..5a4de68
--- /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 0000000..0c190d0
--- /dev/null
+++ b/minadbd/README.txt
@@ -0,0 +1,27 @@
+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.
+ - does not setuid/setgid itself (always stays root)
+ - only uses USB transport
+ - references to JDWP removed
+ - main() removed
+
+adb.h
+ - minor changes to match adb.c changes
+
+sockets.c
+ - references to JDWP 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.
diff --git a/minadbd/adb.c b/minadbd/adb.c
new file mode 100644
index 0000000..d1e97b3
--- /dev/null
+++ b/minadbd/adb.c
@@ -0,0 +1,1151 @@
+/*
+ * 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"
+
+#if !ADB_HOST
+#include <private/android_filesystem_config.h>
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#else
+#include "usb_vendors.h"
+#endif
+
+#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);
+#if ADB_HOST
+ /* XXX why sleep here? */
+ // allow the device some time to respond to the connect message
+ adb_sleep_ms(1000);
+#endif
+}
+
+static char *connection_state_name(atransport *t)
+{
+ if (t == NULL) {
+ return "unknown";
+ }
+
+ switch(t->connection_state) {
+ case CS_BOOTLOADER:
+ return "bootloader";
+ case CS_DEVICE:
+ return "device";
+ case CS_OFFLINE:
+ return "offline";
+ default:
+ return "unknown";
+ }
+}
+
+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);
+}
+
+alistener listener_list = {
+ .next = &listener_list,
+ .prev = &listener_list,
+};
+
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l)
+{
+ asocket *s;
+
+ if(ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = adb_socket_accept(_fd, &addr, &alen);
+ if(fd < 0) return;
+
+ adb_socket_setbufsize(fd, CHUNK_SIZE);
+
+ s = create_local_socket(fd);
+ if(s) {
+ connect_to_smartsocket(s);
+ return;
+ }
+
+ adb_close(fd);
+ }
+}
+
+static void listener_event_func(int _fd, unsigned ev, void *_l)
+{
+ alistener *l = _l;
+ asocket *s;
+
+ if(ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = adb_socket_accept(_fd, &addr, &alen);
+ if(fd < 0) return;
+
+ s = create_local_socket(fd);
+ if(s) {
+ s->transport = l->transport;
+ connect_to_remote(s, l->connect_to);
+ return;
+ }
+
+ adb_close(fd);
+ }
+}
+
+static void free_listener(alistener* l)
+{
+ if (l->next) {
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ l->next = l->prev = l;
+ }
+
+ // closes the corresponding fd
+ fdevent_remove(&l->fde);
+
+ if (l->local_name)
+ free((char*)l->local_name);
+
+ if (l->connect_to)
+ free((char*)l->connect_to);
+
+ if (l->transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ }
+ free(l);
+}
+
+static void listener_disconnect(void* _l, atransport* t)
+{
+ alistener* l = _l;
+
+ free_listener(l);
+}
+
+int local_name_to_fd(const char *name)
+{
+ int port;
+
+ if(!strncmp("tcp:", name, 4)){
+ int ret;
+ port = atoi(name + 4);
+ ret = socket_loopback_server(port, SOCK_STREAM);
+ return ret;
+ }
+#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */
+ // It's non-sensical to support the "reserved" space on the adb host side
+ if(!strncmp(name, "local:", 6)) {
+ return socket_local_server(name + 6,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if(!strncmp(name, "localabstract:", 14)) {
+ return socket_local_server(name + 14,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if(!strncmp(name, "localfilesystem:", 16)) {
+ return socket_local_server(name + 16,
+ ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ }
+
+#endif
+ printf("unknown local portname '%s'\n", name);
+ return -1;
+}
+
+static int remove_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+ alistener *l;
+
+ for (l = listener_list.next; l != &listener_list; l = l->next) {
+ if (!strcmp(local_name, l->local_name) &&
+ !strcmp(connect_to, l->connect_to) &&
+ l->transport && l->transport == transport) {
+
+ listener_disconnect(l, transport);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int install_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+ alistener *l;
+
+ //printf("install_listener('%s','%s')\n", local_name, connect_to);
+
+ for(l = listener_list.next; l != &listener_list; l = l->next){
+ if(strcmp(local_name, l->local_name) == 0) {
+ char *cto;
+
+ /* can't repurpose a smartsocket */
+ if(l->connect_to[0] == '*') {
+ return -1;
+ }
+
+ cto = strdup(connect_to);
+ if(cto == 0) {
+ return -1;
+ }
+
+ //printf("rebinding '%s' to '%s'\n", local_name, connect_to);
+ free((void*) l->connect_to);
+ l->connect_to = cto;
+ if (l->transport != transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ l->transport = transport;
+ add_transport_disconnect(l->transport, &l->disconnect);
+ }
+ return 0;
+ }
+ }
+
+ if((l = calloc(1, sizeof(alistener))) == 0) goto nomem;
+ if((l->local_name = strdup(local_name)) == 0) goto nomem;
+ if((l->connect_to = strdup(connect_to)) == 0) goto nomem;
+
+
+ l->fd = local_name_to_fd(local_name);
+ if(l->fd < 0) {
+ free((void*) l->local_name);
+ free((void*) l->connect_to);
+ free(l);
+ printf("cannot bind '%s'\n", local_name);
+ return -2;
+ }
+
+ close_on_exec(l->fd);
+ if(!strcmp(l->connect_to, "*smartsocket*")) {
+ fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
+ } else {
+ fdevent_install(&l->fde, l->fd, listener_event_func, l);
+ }
+ fdevent_set(&l->fde, FDE_READ);
+
+ l->next = &listener_list;
+ l->prev = listener_list.prev;
+ l->next->prev = l;
+ l->prev->next = l;
+ l->transport = transport;
+
+ if (transport) {
+ l->disconnect.opaque = l;
+ l->disconnect.func = listener_disconnect;
+ add_transport_disconnect(transport, &l->disconnect);
+ }
+ return 0;
+
+nomem:
+ fatal("cannot allocate listener");
+ return 0;
+}
+
+#ifdef HAVE_WIN32_PROC
+static BOOL WINAPI ctrlc_handler(DWORD type)
+{
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+#endif
+
+static void adb_cleanup(void)
+{
+ usb_cleanup();
+}
+
+void start_logging(void)
+{
+#ifdef HAVE_WIN32_PROC
+ char temp[ MAX_PATH ];
+ FILE* fnul;
+ FILE* flog;
+
+ GetTempPath( sizeof(temp) - 8, temp );
+ strcat( temp, "adb.log" );
+
+ /* Win32 specific redirections */
+ fnul = fopen( "NUL", "rt" );
+ if (fnul != NULL)
+ stdin[0] = fnul[0];
+
+ flog = fopen( temp, "at" );
+ if (flog == NULL)
+ flog = fnul;
+
+ setvbuf( flog, NULL, _IONBF, 0 );
+
+ stdout[0] = flog[0];
+ stderr[0] = flog[0];
+ fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#else
+ int fd;
+
+ fd = unix_open("/dev/null", O_RDONLY);
+ dup2(fd, 0);
+ adb_close(fd);
+
+ fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
+ if(fd < 0) {
+ fd = unix_open("/dev/null", O_WRONLY);
+ }
+ dup2(fd, 1);
+ dup2(fd, 2);
+ adb_close(fd);
+ fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#endif
+}
+
+#if !ADB_HOST
+void start_device_log(void)
+{
+ int fd;
+ char path[PATH_MAX];
+ struct tm now;
+ time_t t;
+ char value[PROPERTY_VALUE_MAX];
+
+ // read the trace mask from persistent property persist.adb.trace_mask
+ // give up if the property is not set or cannot be parsed
+ property_get("persist.adb.trace_mask", value, "");
+ if (sscanf(value, "%x", &adb_trace_mask) != 1)
+ return;
+
+ adb_mkdir("/data/adb", 0775);
+ tzset();
+ time(&t);
+ localtime_r(&t, &now);
+ strftime(path, sizeof(path),
+ "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt",
+ &now);
+ fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0)
+ return;
+
+ // redirect stdout and stderr to the log file
+ dup2(fd, 1);
+ dup2(fd, 2);
+ fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+ adb_close(fd);
+
+ fd = unix_open("/dev/null", O_RDONLY);
+ dup2(fd, 0);
+ adb_close(fd);
+}
+#endif
+
+#if ADB_HOST
+int launch_server(int server_port)
+{
+#ifdef HAVE_WIN32_PROC
+ /* we need to start the server in the background */
+ /* we create a PIPE that will be used to wait for the server's "OK" */
+ /* message since the pipe handles must be inheritable, we use a */
+ /* security attribute */
+ HANDLE pipe_read, pipe_write;
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+ char program_path[ MAX_PATH ];
+ int ret;
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ /* create pipe, and ensure its read handle isn't inheritable */
+ ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
+ if (!ret) {
+ fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+ return -1;
+ }
+
+ SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+
+ ZeroMemory( &startup, sizeof(startup) );
+ startup.cb = sizeof(startup);
+ startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
+ startup.hStdOutput = pipe_write;
+ startup.hStdError = GetStdHandle( STD_ERROR_HANDLE );
+ startup.dwFlags = STARTF_USESTDHANDLES;
+
+ ZeroMemory( &pinfo, sizeof(pinfo) );
+
+ /* get path of current program */
+ GetModuleFileName( NULL, program_path, sizeof(program_path) );
+
+ ret = CreateProcess(
+ program_path, /* program path */
+ "adb fork-server server",
+ /* the fork-server argument will set the
+ debug = 2 in the child */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ DETACHED_PROCESS, /* the new process doesn't have a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo );
+
+ CloseHandle( pipe_write );
+
+ if (!ret) {
+ fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+ CloseHandle( pipe_read );
+ return -1;
+ }
+
+ CloseHandle( pinfo.hProcess );
+ CloseHandle( pinfo.hThread );
+
+ /* wait for the "OK\n" message */
+ {
+ char temp[3];
+ DWORD count;
+
+ ret = ReadFile( pipe_read, temp, 3, &count, NULL );
+ CloseHandle( pipe_read );
+ if ( !ret ) {
+ fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+ return -1;
+ }
+ if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "ADB server didn't ACK\n" );
+ return -1;
+ }
+ }
+#elif defined(HAVE_FORKEXEC)
+ char path[PATH_MAX];
+ int fd[2];
+
+ // set up a pipe so the child can tell us when it is ready.
+ // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+ if (pipe(fd)) {
+ fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
+ return -1;
+ }
+ get_my_path(path, PATH_MAX);
+ pid_t pid = fork();
+ if(pid < 0) return -1;
+
+ if (pid == 0) {
+ // child side of the fork
+
+ // redirect stderr to the pipe
+ // we use stderr instead of stdout due to stdout's buffering behavior.
+ adb_close(fd[0]);
+ dup2(fd[1], STDERR_FILENO);
+ adb_close(fd[1]);
+
+ // child process
+ int result = execl(path, "adb", "fork-server", "server", NULL);
+ // this should not return
+ fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+ } else {
+ // parent side of the fork
+
+ char temp[3];
+
+ temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+ // wait for the "OK\n" message
+ adb_close(fd[1]);
+ int ret = adb_read(fd[0], temp, 3);
+ int saved_errno = errno;
+ adb_close(fd[0]);
+ if (ret < 0) {
+ fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
+ return -1;
+ }
+ if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "ADB server didn't ACK\n" );
+ return -1;
+ }
+
+ setsid();
+ }
+#else
+#error "cannot implement background server start on this platform"
+#endif
+ return 0;
+}
+#endif
+
+/* Constructs a local name of form tcp:port.
+ * target_str points to the target string, it's content will be overwritten.
+ * target_size is the capacity of the target string.
+ * server_port is the port number to use for the local name.
+ */
+void build_local_name(char* target_str, size_t target_size, int server_port)
+{
+ snprintf(target_str, target_size, "tcp:%d", server_port);
+}
+
+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();
+ }
+
+ D("Event loop starting\n");
+
+ fdevent_loop();
+
+ usb_cleanup();
+
+ return 0;
+}
+
+#if ADB_HOST
+void connect_device(char* host, char* buffer, int buffer_size)
+{
+ int port, fd;
+ char* portstr = strchr(host, ':');
+ char hostbuf[100];
+ char serial[100];
+
+ strncpy(hostbuf, host, sizeof(hostbuf) - 1);
+ if (portstr) {
+ if (portstr - host >= sizeof(hostbuf)) {
+ snprintf(buffer, buffer_size, "bad host name %s", host);
+ return;
+ }
+ // zero terminate the host at the point we found the colon
+ hostbuf[portstr - host] = 0;
+ if (sscanf(portstr + 1, "%d", &port) == 0) {
+ snprintf(buffer, buffer_size, "bad port number %s", portstr);
+ return;
+ }
+ } else {
+ port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ }
+
+ snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+ if (find_transport(serial)) {
+ snprintf(buffer, buffer_size, "already connected to %s", serial);
+ return;
+ }
+
+ fd = socket_network_client(hostbuf, port, SOCK_STREAM);
+ if (fd < 0) {
+ snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
+ return;
+ }
+
+ D("client: connected on remote on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ register_socket_transport(fd, serial, port, 0);
+ snprintf(buffer, buffer_size, "connected to %s", serial);
+}
+
+void connect_emulator(char* port_spec, char* buffer, int buffer_size)
+{
+ char* port_separator = strchr(port_spec, ',');
+ if (!port_separator) {
+ snprintf(buffer, buffer_size,
+ "unable to parse '%s' as <console port>,<adb port>",
+ port_spec);
+ return;
+ }
+
+ // Zero-terminate console port and make port_separator point to 2nd port.
+ *port_separator++ = 0;
+ int console_port = strtol(port_spec, NULL, 0);
+ int adb_port = strtol(port_separator, NULL, 0);
+ if (!(console_port > 0 && adb_port > 0)) {
+ *(port_separator - 1) = ',';
+ snprintf(buffer, buffer_size,
+ "Invalid port numbers: Expected positive numbers, got '%s'",
+ port_spec);
+ return;
+ }
+
+ /* Check if the emulator is already known.
+ * Note: There's a small but harmless race condition here: An emulator not
+ * present just yet could be registered by another invocation right
+ * after doing this check here. However, local_connect protects
+ * against double-registration too. From here, a better error message
+ * can be produced. In the case of the race condition, the very specific
+ * error message won't be shown, but the data doesn't get corrupted. */
+ atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
+ if (known_emulator != NULL) {
+ snprintf(buffer, buffer_size,
+ "Emulator on port %d already registered.", adb_port);
+ return;
+ }
+
+ /* Check if more emulators can be registered. Similar unproblematic
+ * race condition as above. */
+ int candidate_slot = get_available_local_transport_index();
+ if (candidate_slot < 0) {
+ snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+ return;
+ }
+
+ /* Preconditions met, try to connect to the emulator. */
+ if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+ snprintf(buffer, buffer_size,
+ "Connected to emulator on ports %d,%d", console_port, adb_port);
+ } else {
+ snprintf(buffer, buffer_size,
+ "Could not connect to emulator on ports %d,%d",
+ console_port, adb_port);
+ }
+}
+#endif
+
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
+{
+ atransport *transport = NULL;
+ char buf[4096];
+
+ if(!strcmp(service, "kill")) {
+ fprintf(stderr,"adb server killed by remote request\n");
+ fflush(stdout);
+ adb_write(reply_fd, "OKAY", 4);
+ usb_cleanup();
+ exit(0);
+ }
+
+#if ADB_HOST
+ // "transport:" is used for switching transport with a specified serial number
+ // "transport-usb:" is used for switching transport to the only USB transport
+ // "transport-local:" is used for switching transport to the only local transport
+ // "transport-any:" is used for switching transport to the only transport
+ if (!strncmp(service, "transport", strlen("transport"))) {
+ char* error_string = "unknown failure";
+ transport_type type = kTransportAny;
+
+ if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+ type = kTransportUsb;
+ } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
+ type = kTransportLocal;
+ } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
+ type = kTransportAny;
+ } else if (!strncmp(service, "transport:", strlen("transport:"))) {
+ service += strlen("transport:");
+ serial = service;
+ }
+
+ transport = acquire_one_transport(CS_ANY, type, serial, &error_string);
+
+ if (transport) {
+ s->transport = transport;
+ adb_write(reply_fd, "OKAY", 4);
+ } else {
+ sendfailmsg(reply_fd, error_string);
+ }
+ return 1;
+ }
+
+ // return a list of all connected devices
+ if (!strcmp(service, "devices")) {
+ char buffer[4096];
+ memset(buf, 0, sizeof(buf));
+ memset(buffer, 0, sizeof(buffer));
+ D("Getting device list \n");
+ list_transports(buffer, sizeof(buffer));
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+ D("Wrote device list \n");
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // add a new TCP transport, device or emulator
+ if (!strncmp(service, "connect:", 8)) {
+ char buffer[4096];
+ char* host = service + 8;
+ if (!strncmp(host, "emu:", 4)) {
+ connect_emulator(host + 4, buffer, sizeof(buffer));
+ } else {
+ connect_device(host, buffer, sizeof(buffer));
+ }
+ // Send response for emulator and device
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // remove TCP transport
+ if (!strncmp(service, "disconnect:", 11)) {
+ char buffer[4096];
+ memset(buffer, 0, sizeof(buffer));
+ char* serial = service + 11;
+ if (serial[0] == 0) {
+ // disconnect from all TCP devices
+ unregister_all_tcp_transports();
+ } else {
+ char hostbuf[100];
+ // assume port 5555 if no port is specified
+ if (!strchr(serial, ':')) {
+ snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
+ serial = hostbuf;
+ }
+ atransport *t = find_transport(serial);
+
+ if (t) {
+ unregister_transport(t);
+ } else {
+ snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // returns our value for ADB_SERVER_VERSION
+ if (!strcmp(service, "version")) {
+ char version[12];
+ snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
+ snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
+ char *out = "unknown";
+ transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+ if (transport && transport->serial) {
+ out = transport->serial;
+ }
+ snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+ // indicates a new emulator instance has started
+ if (!strncmp(service,"emulator:",9)) {
+ int port = atoi(service+9);
+ local_connect(port);
+ /* we don't even need to send a reply */
+ return 0;
+ }
+#endif // ADB_HOST
+
+ if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) {
+ char *local, *remote, *err;
+ int r;
+ atransport *transport;
+
+ int createForward = strncmp(service,"kill",4);
+
+ local = service + (createForward ? 8 : 12);
+ remote = strchr(local,';');
+ if(remote == 0) {
+ sendfailmsg(reply_fd, "malformed forward spec");
+ return 0;
+ }
+
+ *remote++ = 0;
+ if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
+ sendfailmsg(reply_fd, "malformed forward spec");
+ return 0;
+ }
+
+ transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
+ if (!transport) {
+ sendfailmsg(reply_fd, err);
+ return 0;
+ }
+
+ if (createForward) {
+ r = install_listener(local, remote, transport);
+ } else {
+ r = remove_listener(local, remote, transport);
+ }
+ if(r == 0) {
+ /* 1st OKAY is connect, 2nd OKAY is status */
+ writex(reply_fd, "OKAYOKAY", 8);
+ return 0;
+ }
+
+ if (createForward) {
+ sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket");
+ } else {
+ sendfailmsg(reply_fd, "cannot remove listener");
+ }
+ return 0;
+ }
+
+ if(!strncmp(service,"get-state",strlen("get-state"))) {
+ transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+ char *state = connection_state_name(transport);
+ snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+ return -1;
+}
diff --git a/minadbd/adb.h b/minadbd/adb.h
new file mode 100644
index 0000000..a989edd
--- /dev/null
+++ b/minadbd/adb.h
@@ -0,0 +1,443 @@
+/*
+ * 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 alistener alistener;
+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, listeners, 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;
+};
+
+
+/* A listener is an entity which binds to a local port
+** and, upon receiving a connection on that port, creates
+** an asocket to connect the new local connection to a
+** specific remote service.
+**
+** TODO: some listeners read from the new connection to
+** determine what exact service to connect to on the far
+** side.
+*/
+struct alistener
+{
+ alistener *next;
+ alistener *prev;
+
+ fdevent fde;
+ int fd;
+
+ const char *local_name;
+ const char *connect_to;
+ atransport *transport;
+ adisconnect disconnect;
+};
+
+
+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 0000000..5c374a7
--- /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 0000000..a0ebe2a
--- /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 0000000..652dd73
--- /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 0000000..8fc8b3c
--- /dev/null
+++ b/minadbd/services.c
@@ -0,0 +1,161 @@
+/*
+ * 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) {
+ 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 0000000..9f4cecb
--- /dev/null
+++ b/minadbd/sockets.c
@@ -0,0 +1,830 @@
+/*
+ * 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;
+}
+
+#if ADB_HOST
+static asocket *create_host_service_socket(const char *name, const char* serial)
+{
+ asocket *s;
+
+ s = host_service_to_socket(name, serial);
+
+ if (s != NULL) {
+ D("LS(%d) bound to '%s'\n", s->id, name);
+ return s;
+ }
+
+ return s;
+}
+#endif /* ADB_HOST */
+
+/* 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;
+#if ADB_HOST
+ char *service = NULL;
+ char* serial = NULL;
+ transport_type ttype = kTransportAny;
+#endif
+
+ 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 ADB_HOST
+ service = (char *)p->data + 4;
+ if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
+ char* serial_end;
+ service += strlen("host-serial:");
+
+ // serial number should follow "host:" and could be a host:port string.
+ serial_end = skip_host_serial(service);
+ if (serial_end) {
+ *serial_end = 0; // terminate string
+ serial = service;
+ service = serial_end + 1;
+ }
+ } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+ ttype = kTransportUsb;
+ service += strlen("host-usb:");
+ } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+ ttype = kTransportLocal;
+ service += strlen("host-local:");
+ } else if (!strncmp(service, "host:", strlen("host:"))) {
+ ttype = kTransportAny;
+ service += strlen("host:");
+ } else {
+ service = NULL;
+ }
+
+ if (service) {
+ asocket *s2;
+
+ /* some requests are handled immediately -- in that
+ ** case the handle_host_request() routine has sent
+ ** the OKAY or FAIL message and all we have to do
+ ** is clean up.
+ */
+ if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
+ /* XXX fail message? */
+ D( "SS(%d): handled host service '%s'\n", s->id, service );
+ goto fail;
+ }
+ if (!strncmp(service, "transport", strlen("transport"))) {
+ D( "SS(%d): okay transport\n", s->id );
+ p->len = 0;
+ return 0;
+ }
+
+ /* try to find a local service with this name.
+ ** if no such service exists, we'll fail out
+ ** and tear down here.
+ */
+ s2 = create_host_service_socket(service, serial);
+ if(s2 == 0) {
+ D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+ sendfailmsg(s->peer->fd, "unknown host service");
+ goto fail;
+ }
+
+ /* we've connected to a local host service,
+ ** so we make our peer back into a regular
+ ** local socket and bind it to the new local
+ ** service socket, acknowledge the successful
+ ** connection, and close this smart socket now
+ ** that its work is done.
+ */
+ adb_write(s->peer->fd, "OKAY", 4);
+
+ s->peer->ready = local_socket_ready;
+ s->peer->close = local_socket_close;
+ s->peer->peer = s2;
+ s2->peer = s->peer;
+ s->peer = 0;
+ D( "SS(%d): okay\n", s->id );
+ s->close(s);
+
+ /* initial state is "ready" */
+ s2->ready(s2);
+ return 0;
+ }
+#else /* !ADB_HOST */
+ 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;
+ }
+ }
+#endif
+
+ 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 0000000..b518076
--- /dev/null
+++ b/minadbd/sysdeps.h
@@ -0,0 +1,495 @@
+/*
+ * 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_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_creat(const char* path, int mode)
+{
+ int fd = creat(path, mode);
+
+ if ( fd < 0 )
+ return -1;
+
+ close_on_exec(fd);
+ return fd;
+}
+#undef creat
+#define creat ___xxx_creat
+
+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 0000000..2f7bd27
--- /dev/null
+++ b/minadbd/transport.c
@@ -0,0 +1,1081 @@
+/*
+ * 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;
+
+
+#if ADB_HOST
+static int list_transports_msg(char* buffer, size_t bufferlen)
+{
+ char head[5];
+ int len;
+
+ len = list_transports(buffer+4, bufferlen-4);
+ snprintf(head, sizeof(head), "%04x", len);
+ memcpy(buffer, head, 4);
+ len += 4;
+ return len;
+}
+
+/* this adds support required by the 'track-devices' service.
+ * this is used to send the content of "list_transport" to any
+ * number of client connections that want it through a single
+ * live TCP connection
+ */
+typedef struct device_tracker device_tracker;
+struct device_tracker {
+ asocket socket;
+ int update_needed;
+ device_tracker* next;
+};
+
+/* linked list of all device trackers */
+static device_tracker* device_tracker_list;
+
+static void
+device_tracker_remove( device_tracker* tracker )
+{
+ device_tracker** pnode = &device_tracker_list;
+ device_tracker* node = *pnode;
+
+ adb_mutex_lock( &transport_lock );
+ while (node) {
+ if (node == tracker) {
+ *pnode = node->next;
+ break;
+ }
+ pnode = &node->next;
+ node = *pnode;
+ }
+ adb_mutex_unlock( &transport_lock );
+}
+
+static void
+device_tracker_close( asocket* socket )
+{
+ device_tracker* tracker = (device_tracker*) socket;
+ asocket* peer = socket->peer;
+
+ D( "device tracker %p removed\n", tracker);
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+ device_tracker_remove(tracker);
+ free(tracker);
+}
+
+static int
+device_tracker_enqueue( asocket* socket, apacket* p )
+{
+ /* you can't read from a device tracker, close immediately */
+ put_apacket(p);
+ device_tracker_close(socket);
+ return -1;
+}
+
+static int
+device_tracker_send( device_tracker* tracker,
+ const char* buffer,
+ int len )
+{
+ apacket* p = get_apacket();
+ asocket* peer = tracker->socket.peer;
+
+ memcpy(p->data, buffer, len);
+ p->len = len;
+ return peer->enqueue( peer, p );
+}
+
+
+static void
+device_tracker_ready( asocket* socket )
+{
+ device_tracker* tracker = (device_tracker*) socket;
+
+ /* we want to send the device list when the tracker connects
+ * for the first time, even if no update occured */
+ if (tracker->update_needed > 0) {
+ char buffer[1024];
+ int len;
+
+ tracker->update_needed = 0;
+
+ len = list_transports_msg(buffer, sizeof(buffer));
+ device_tracker_send(tracker, buffer, len);
+ }
+}
+
+
+asocket*
+create_device_tracker(void)
+{
+ device_tracker* tracker = calloc(1,sizeof(*tracker));
+
+ if(tracker == 0) fatal("cannot allocate device tracker");
+
+ D( "device tracker %p created\n", tracker);
+
+ tracker->socket.enqueue = device_tracker_enqueue;
+ tracker->socket.ready = device_tracker_ready;
+ tracker->socket.close = device_tracker_close;
+ tracker->update_needed = 1;
+
+ tracker->next = device_tracker_list;
+ device_tracker_list = tracker;
+
+ return &tracker->socket;
+}
+
+
+/* call this function each time the transport list has changed */
+void update_transports(void)
+{
+ char buffer[1024];
+ int len;
+ device_tracker* tracker;
+
+ len = list_transports_msg(buffer, sizeof(buffer));
+
+ tracker = device_tracker_list;
+ while (tracker != NULL) {
+ device_tracker* next = tracker->next;
+ /* note: this may destroy the tracker if the connection is closed */
+ device_tracker_send(tracker, buffer, len);
+ tracker = next;
+ }
+}
+#else
+void update_transports(void)
+{
+ // nothing to do on the device side
+}
+#endif // ADB_HOST
+
+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;
+}
+
+#if ADB_HOST
+static const char *statename(atransport *t)
+{
+ switch(t->connection_state){
+ case CS_OFFLINE: return "offline";
+ case CS_BOOTLOADER: return "bootloader";
+ case CS_DEVICE: return "device";
+ case CS_HOST: return "host";
+ case CS_RECOVERY: return "recovery";
+ case CS_SIDELOAD: return "sideload";
+ case CS_NOPERM: return "no permissions";
+ default: return "unknown";
+ }
+}
+
+int list_transports(char *buf, size_t bufsize)
+{
+ char* p = buf;
+ char* end = buf + bufsize;
+ int len;
+ atransport *t;
+
+ /* XXX OVERRUN PROBLEMS XXX */
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ const char* serial = t->serial;
+ if (!serial || !serial[0])
+ serial = "????????????";
+ len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t));
+
+ if (p + len >= end) {
+ /* discard last line if buffer is too short */
+ break;
+ }
+ p += len;
+ }
+ p[0] = 0;
+ adb_mutex_unlock(&transport_lock);
+ return p - buf;
+}
+
+
+/* hack for osx */
+void close_usb_devices()
+{
+ atransport *t;
+
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if ( !t->kicked ) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+}
+#endif // ADB_HOST
+
+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);
+}
+
+#if ADB_HOST
+atransport *find_transport(const char *serial)
+{
+ atransport *t;
+
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->serial && !strcmp(serial, t->serial)) {
+ break;
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+
+ if (t != &transport_list)
+ return t;
+ else
+ return 0;
+}
+
+void unregister_transport(atransport *t)
+{
+ adb_mutex_lock(&transport_lock);
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ adb_mutex_unlock(&transport_lock);
+
+ kick_transport(t);
+ transport_unref(t);
+}
+
+// unregisters all non-emulator TCP transports
+void unregister_all_tcp_transports()
+{
+ atransport *t, *next;
+ adb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = next) {
+ next = t->next;
+ if (t->type == kTransportLocal && t->adb_port == 0) {
+ t->next->prev = t->prev;
+ t->prev->next = next;
+ // we cannot call kick_transport when holding transport_lock
+ if (!t->kicked)
+ {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ transport_unref_locked(t);
+ }
+ }
+
+ adb_mutex_unlock(&transport_lock);
+}
+
+#endif
+
+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 0000000..992e052
--- /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 0000000..ee6b637
--- /dev/null
+++ b/minadbd/transport_usb.c
@@ -0,0 +1,148 @@
+/*
+ * 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"
+
+#if ADB_HOST
+#include "usb_vendors.h"
+#endif
+
+#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;
+
+#if ADB_HOST
+ HOST = 1;
+#else
+ HOST = 0;
+#endif
+}
+
+#if ADB_HOST
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+{
+ unsigned i;
+ for (i = 0; i < vendorIdCount; i++) {
+ if (vid == vendorIds[i]) {
+ if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS &&
+ usb_protocol == ADB_PROTOCOL) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c
new file mode 100644
index 0000000..635fa4b
--- /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 0000000..91518ba
--- /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 0000000..f70ecd2
--- /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 */