adb: Handle adb connect in a thread
adb connect calls connect() in the event loop. If you pass a wrong ip address or the server is slow to respond, this will block the event loop and you can't even kill the adb server with adb kill-server. Handle connect requests in a service thread instead. Change-Id: I2ee732869a3dc22a6d3b87cf8ac80acaa7790037
This commit is contained in:
parent
db50747c93
commit
1c45ee92e2
114
adb/adb.c
114
adb/adb.c
|
@ -1387,105 +1387,6 @@ int adb_main(int is_daemon, int server_port)
|
|||
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 >= (ptrdiff_t)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;
|
||||
|
@ -1546,21 +1447,6 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r
|
|||
}
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
|
|
@ -289,7 +289,7 @@ void init_usb_transport(atransport *t, usb_handle *usb, int state);
|
|||
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);
|
||||
int 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);
|
||||
|
|
123
adb/services.c
123
adb/services.c
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -429,6 +430,124 @@ static void wait_for_state(int fd, void* cookie)
|
|||
adb_close(fd);
|
||||
D("wait_for_state is done\n");
|
||||
}
|
||||
|
||||
static void connect_device(char* host, char* buffer, int buffer_size)
|
||||
{
|
||||
int port, fd;
|
||||
char* portstr = strchr(host, ':');
|
||||
char hostbuf[100];
|
||||
char serial[100];
|
||||
int ret;
|
||||
|
||||
strncpy(hostbuf, host, sizeof(hostbuf) - 1);
|
||||
if (portstr) {
|
||||
if (portstr - host >= (ptrdiff_t)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);
|
||||
|
||||
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);
|
||||
|
||||
ret = register_socket_transport(fd, serial, port, 0);
|
||||
if (ret < 0) {
|
||||
adb_close(fd);
|
||||
snprintf(buffer, buffer_size, "already connected to %s", serial);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_service(int fd, void* cookie)
|
||||
{
|
||||
char buf[4096];
|
||||
char resp[4096];
|
||||
char *host = cookie;
|
||||
|
||||
if (!strncmp(host, "emu:", 4)) {
|
||||
connect_emulator(host + 4, buf, sizeof(buf));
|
||||
} else {
|
||||
connect_device(host, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
// Send response for emulator and device
|
||||
snprintf(resp, sizeof(resp), "%04x%s",(unsigned)strlen(buf), buf);
|
||||
writex(fd, resp, strlen(resp));
|
||||
adb_close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
|
@ -462,6 +581,10 @@ asocket* host_service_to_socket(const char* name, const char *serial)
|
|||
|
||||
int fd = create_service_thread(wait_for_state, sinfo);
|
||||
return create_local_socket(fd);
|
||||
} else if (!strncmp(name, "connect:", 8)) {
|
||||
const char *host = name + 8;
|
||||
int fd = create_service_thread(connect_service, (void *)host);
|
||||
return create_local_socket(fd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ static atransport transport_list = {
|
|||
.prev = &transport_list,
|
||||
};
|
||||
|
||||
static atransport pending_list = {
|
||||
.next = &pending_list,
|
||||
.prev = &pending_list,
|
||||
};
|
||||
|
||||
ADB_MUTEX_DEFINE( transport_lock );
|
||||
|
||||
#if ADB_TRACE
|
||||
|
@ -645,8 +650,11 @@ static void transport_registration_func(int _fd, unsigned ev, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
/* put us on the master device list */
|
||||
adb_mutex_lock(&transport_lock);
|
||||
/* remove from pending list */
|
||||
t->next->prev = t->prev;
|
||||
t->prev->next = t->next;
|
||||
/* put us on the master device list */
|
||||
t->next = &transport_list;
|
||||
t->prev = transport_list.prev;
|
||||
t->next->prev = t;
|
||||
|
@ -989,9 +997,10 @@ void close_usb_devices()
|
|||
}
|
||||
#endif // ADB_HOST
|
||||
|
||||
void register_socket_transport(int s, const char *serial, int port, int local)
|
||||
int register_socket_transport(int s, const char *serial, int port, int local)
|
||||
{
|
||||
atransport *t = calloc(1, sizeof(atransport));
|
||||
atransport *n;
|
||||
char buff[32];
|
||||
|
||||
if (!serial) {
|
||||
|
@ -999,15 +1008,37 @@ void register_socket_transport(int s, const char *serial, int port, int local)
|
|||
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);
|
||||
if (init_socket_transport(t, s, port, local) < 0) {
|
||||
free(t);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
if(serial) {
|
||||
t->serial = strdup(serial);
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
for (n = pending_list.next; n != &pending_list; n = n->next) {
|
||||
if (n->serial && !strcmp(serial, n->serial)) {
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
free(t);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (n = transport_list.next; n != &transport_list; n = n->next) {
|
||||
if (n->serial && !strcmp(serial, n->serial)) {
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
free(t);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
t->next = &pending_list;
|
||||
t->prev = pending_list.prev;
|
||||
t->next->prev = t;
|
||||
t->prev->next = t;
|
||||
t->serial = strdup(serial);
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
register_transport(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
|
@ -1077,6 +1108,14 @@ void register_usb_transport(usb_handle *usb, const char *serial, const char *dev
|
|||
if(devpath) {
|
||||
t->devpath = strdup(devpath);
|
||||
}
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
t->next = &pending_list;
|
||||
t->prev = pending_list.prev;
|
||||
t->next->prev = t;
|
||||
t->prev->next = t;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
register_transport(t);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue