diff options
Diffstat (limited to 'minadbd/adb.c')
-rw-r--r-- | minadbd/adb.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/minadbd/adb.c b/minadbd/adb.c new file mode 100644 index 000000000..0e8fd2a7e --- /dev/null +++ b/minadbd/adb.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TRACE_TAG TRACE_ADB + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> + +#include "sysdeps.h" +#include "adb.h" + +#include <private/android_filesystem_config.h> +#include <linux/capability.h> +#include <linux/prctl.h> + +#if ADB_TRACE +ADB_MUTEX_DEFINE( D_lock ); +#endif + +int HOST = 0; + +static const char *adb_device_banner = "sideload"; + +void fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +void fatal_errno(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: %s: ", strerror(errno)); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +int adb_trace_mask; + +/* read a comma/space/colum/semi-column separated list of tags + * from the ADB_TRACE environment variable and build the trace + * mask from it. note that '1' and 'all' are special cases to + * enable all tracing + */ +void adb_trace_init(void) +{ + const char* p = getenv("ADB_TRACE"); + const char* q; + + static const struct { + const char* tag; + int flag; + } tags[] = { + { "1", 0 }, + { "all", 0 }, + { "adb", TRACE_ADB }, + { "sockets", TRACE_SOCKETS }, + { "packets", TRACE_PACKETS }, + { "rwx", TRACE_RWX }, + { "usb", TRACE_USB }, + { "sync", TRACE_SYNC }, + { "sysdeps", TRACE_SYSDEPS }, + { "transport", TRACE_TRANSPORT }, + { "jdwp", TRACE_JDWP }, + { "services", TRACE_SERVICES }, + { NULL, 0 } + }; + + if (p == NULL) + return; + + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; + + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; + + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); + + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } +} + + +apacket *get_apacket(void) +{ + apacket *p = malloc(sizeof(apacket)); + if(p == 0) fatal("failed to allocate an apacket"); + memset(p, 0, sizeof(apacket) - MAX_PAYLOAD); + return p; +} + +void put_apacket(apacket *p) +{ + free(p); +} + +void handle_online(void) +{ + D("adb: online\n"); +} + +void handle_offline(atransport *t) +{ + D("adb: offline\n"); + //Close the associated usb + run_transport_disconnects(t); +} + +#if TRACE_PACKETS +#define DUMPMAX 32 +void print_packet(const char *label, apacket *p) +{ + char *tag; + char *x; + unsigned count; + + switch(p->msg.command){ + case A_SYNC: tag = "SYNC"; break; + case A_CNXN: tag = "CNXN" ; break; + case A_OPEN: tag = "OPEN"; break; + case A_OKAY: tag = "OKAY"; break; + case A_CLSE: tag = "CLSE"; break; + case A_WRTE: tag = "WRTE"; break; + default: tag = "????"; break; + } + + fprintf(stderr, "%s: %s %08x %08x %04x \"", + label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length); + count = p->msg.data_length; + x = (char*) p->data; + if(count > DUMPMAX) { + count = DUMPMAX; + tag = "\n"; + } else { + tag = "\"\n"; + } + while(count-- > 0){ + if((*x >= ' ') && (*x < 127)) { + fputc(*x, stderr); + } else { + fputc('.', stderr); + } + x++; + } + fprintf(stderr, tag); +} +#endif + +static void send_ready(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_ready \n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_close(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_close \n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_connect(atransport *t) +{ + D("Calling send_connect \n"); + apacket *cp = get_apacket(); + cp->msg.command = A_CNXN; + cp->msg.arg0 = A_VERSION; + cp->msg.arg1 = MAX_PAYLOAD; + snprintf((char*) cp->data, sizeof cp->data, "%s::", + HOST ? "host" : adb_device_banner); + cp->msg.data_length = strlen((char*) cp->data) + 1; + send_packet(cp, t); +} + +void parse_banner(char *banner, atransport *t) +{ + char *type, *product, *end; + + D("parse_banner: %s\n", banner); + type = banner; + product = strchr(type, ':'); + if(product) { + *product++ = 0; + } else { + product = ""; + } + + /* remove trailing ':' */ + end = strchr(product, ':'); + if(end) *end = 0; + + /* save product name in device structure */ + if (t->product == NULL) { + t->product = strdup(product); + } else if (strcmp(product, t->product) != 0) { + free(t->product); + t->product = strdup(product); + } + + if(!strcmp(type, "bootloader")){ + D("setting connection_state to CS_BOOTLOADER\n"); + t->connection_state = CS_BOOTLOADER; + update_transports(); + return; + } + + if(!strcmp(type, "device")) { + D("setting connection_state to CS_DEVICE\n"); + t->connection_state = CS_DEVICE; + update_transports(); + return; + } + + if(!strcmp(type, "recovery")) { + D("setting connection_state to CS_RECOVERY\n"); + t->connection_state = CS_RECOVERY; + update_transports(); + return; + } + + if(!strcmp(type, "sideload")) { + D("setting connection_state to CS_SIDELOAD\n"); + t->connection_state = CS_SIDELOAD; + update_transports(); + return; + } + + t->connection_state = CS_HOST; +} + +void handle_packet(apacket *p, atransport *t) +{ + asocket *s; + + D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], + ((char*) (&(p->msg.command)))[1], + ((char*) (&(p->msg.command)))[2], + ((char*) (&(p->msg.command)))[3]); + print_packet("recv", p); + + switch(p->msg.command){ + case A_SYNC: + if(p->msg.arg0){ + send_packet(p, t); + if(HOST) send_connect(t); + } else { + t->connection_state = CS_OFFLINE; + handle_offline(t); + send_packet(p, t); + } + return; + + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + /* XXX verify version, etc */ + if(t->connection_state != CS_OFFLINE) { + t->connection_state = CS_OFFLINE; + handle_offline(t); + } + parse_banner((char*) p->data, t); + handle_online(); + if(!HOST) send_connect(t); + break; + + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + if(t->connection_state != CS_OFFLINE) { + char *name = (char*) p->data; + name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; + s = create_local_service_socket(name); + if(s == 0) { + send_close(0, p->msg.arg0, t); + } else { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + send_ready(s->id, s->peer->id, t); + s->ready(s); + } + } + break; + + case A_OKAY: /* READY(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + if(s->peer == 0) { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + } + s->ready(s); + } + } + break; + + case A_CLSE: /* CLOSE(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + s->close(s); + } + } + break; + + case A_WRTE: + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + unsigned rid = p->msg.arg0; + p->len = p->msg.data_length; + + if(s->enqueue(s, p) == 0) { + D("Enqueue the socket\n"); + send_ready(s->id, rid, t); + } + return; + } + } + break; + + default: + printf("handle_packet: what is %08x?!\n", p->msg.command); + } + + put_apacket(p); +} + +static void adb_cleanup(void) +{ + usb_cleanup(); +} + +int adb_main() +{ + atexit(adb_cleanup); +#if defined(HAVE_FORKEXEC) + // No SIGCHLD. Let the service subproc handle its children. + signal(SIGPIPE, SIG_IGN); +#endif + + init_transport_registration(); + + // The minimal version of adbd only uses USB. + if (access("/dev/android_adb", F_OK) == 0) { + // listen on USB + usb_init(); + } + + if (setgid(AID_SHELL) != 0) { + fprintf(stderr, "failed to setgid to shell\n"); + exit(1); + } + if (setuid(AID_SHELL) != 0) { + fprintf(stderr, "failed to setuid to shell\n"); + exit(1); + } + fprintf(stderr, "userid is %d\n", getuid()); + + D("Event loop starting\n"); + + fdevent_loop(); + + usb_cleanup(); + + return 0; +} |