From e83b7cf8f1bad5b334b6352d3b850d8555530a9a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 9 Jan 2012 15:16:13 -0800 Subject: 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. always initialize usb product, vendor, etc. for adb in recovery Set these values even on non-debuggable builds, so that the mini-adb now in recovery can work. --- minadbd/adb.c | 1151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1151 insertions(+) create mode 100644 minadbd/adb.c (limited to 'minadbd/adb.c') diff --git a/minadbd/adb.c b/minadbd/adb.c new file mode 100644 index 000000000..d1e97b31f --- /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 +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" +#include "adb.h" + +#if !ADB_HOST +#include +#include +#include +#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 ,", + 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; +} -- cgit v1.2.3