summaryrefslogtreecommitdiffstats
path: root/minadbd/adb.c
diff options
context:
space:
mode:
Diffstat (limited to 'minadbd/adb.c')
-rw-r--r--minadbd/adb.c414
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;
+}