diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0520fb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +hal/*.o diff --git a/CMakeLists.txt b/CMakeLists.txt index 39a5ebb..db22326 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,9 @@ project (adbd) find_package(uv REQUIRED) +option(ADBD_USB_HOTPLUG_BYTIMER "adb usb hotplug check by timer" OFF) +option(ADBD_USB_HOTPLUG_BYNOTIFY "adb usb hotplug check by notify" OFF) + option(ADBD_AUTHENTICATION "adb authentication" OFF) option(ADBD_AUTH_PUBKEY "adb auth public key" OFF) option(ADBD_FILE_SERVICE "adb file sync service" ON) diff --git a/adb.h b/adb.h index c2c94e6..c524925 100644 --- a/adb.h +++ b/adb.h @@ -185,6 +185,8 @@ void adb_send_okay_frame_with_data(adb_client_t *client, apacket *p, unsigned local, unsigned remote); void adb_send_open_frame(adb_client_t *client, apacket *p, unsigned local, unsigned remote, int size); +void adb_send_close_frame(adb_client_t *client, apacket *p, + unsigned local, unsigned remote); void adb_send_data_frame(adb_client_t *client, apacket *p); int adb_check_frame_data(apacket *p); @@ -215,7 +217,7 @@ int adb_hal_socket_connect(struct adb_client_s *client, adb_tcp_socket_t *socket void (*on_connect_cb)(adb_tcp_socket_t*, int)); void adb_hal_socket_close(adb_tcp_socket_t *socket, - void (*close_cb)(adb_tcp_socket_t*)); + void (*on_close_cb)(adb_tcp_socket_t*)); int adb_hal_socket_write(adb_tcp_socket_t *socket, struct apacket_s *p, void (*cb)(struct adb_client_s*, adb_tcp_socket_t*, struct apacket_s*, bool fail)); diff --git a/adb_client.c b/adb_client.c index 45a5d13..43606ed 100644 --- a/adb_client.c +++ b/adb_client.c @@ -31,13 +31,13 @@ #include "tcp_service.h" #endif +#define REBOOT_SERVICE ((adb_service_t *)(~0ul)) + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void send_frame(adb_client_t *s, apacket *p); -static void send_close_frame(adb_client_t *s, apacket *p, - unsigned local, unsigned remote); static void send_cnxn_frame(adb_client_t *s, apacket *p); #ifdef CONFIG_ADBD_AUTHENTICATION @@ -69,7 +69,7 @@ static void send_frame(adb_client_t *client, apacket *p) p->msg.magic = p->msg.command ^ 0xffffffff; count = p->msg.data_length; - x = (unsigned char *) p->data; + x = (unsigned char *)p->data; sum = 0; while(count-- > 0){ sum += *x++; @@ -85,17 +85,6 @@ static void send_frame(adb_client_t *client, apacket *p) } } -static void send_close_frame(adb_client_t *client, apacket *p, - unsigned local, unsigned remote) -{ - p->msg.command = A_CLSE; - p->msg.arg0 = local; - p->msg.arg1 = remote; - p->msg.data_length = 0; - p->write_len = 0; - send_frame(client, p); -} - static void send_cnxn_frame(adb_client_t *client, apacket *p) { p->msg.command = A_CNXN; @@ -133,7 +122,7 @@ static void send_auth_request(adb_client_t *client, apacket *p) static void handle_open_frame(adb_client_t *client, apacket *p) { adb_service_t *svc; - char *name = (char*) p->data; + char *name = (char*)p->data; /* OPEN(local-id, 0, "destination") */ if (p->msg.arg0 == 0 || p->msg.arg1 != 0) { @@ -150,9 +139,9 @@ static void handle_open_frame(adb_client_t *client, apacket *p) { p->msg.arg0); } else { - send_close_frame(client, p, 0, p->msg.arg0); + adb_send_close_frame(client, p, 0, p->msg.arg0); } - } else { + } else if (svc != REBOOT_SERVICE) { if (p->write_len == APACKET_SERVICE_INIT_ASYNC) { /* Service init is asynchronous. Release apacket. */ adb_hal_apacket_release(client, p); @@ -181,7 +170,7 @@ static void handle_write_frame(adb_client_t *client, apacket *p) { svc = adb_client_find_service(client, p->msg.arg1, p->msg.arg0); if (svc == NULL) { /* Ensure service is closed on peer side */ - send_close_frame(client, p, p->msg.arg1, p->msg.arg0); + adb_send_close_frame(client, p, p->msg.arg1, p->msg.arg0); return; } @@ -207,7 +196,7 @@ static void handle_okay_frame(adb_client_t *client, apacket *p) { adb_service_t *svc; svc = adb_client_find_service(client, p->msg.arg1, 0); if (!svc) { - send_close_frame(client, p, p->msg.arg1, p->msg.arg0); + adb_hal_apacket_release(client, p); return; } @@ -324,6 +313,17 @@ void adb_send_open_frame(adb_client_t *client, apacket *p, send_frame(client, p); } +void adb_send_close_frame(adb_client_t *client, apacket *p, + unsigned local, unsigned remote) +{ + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + p->msg.data_length = 0; + p->write_len = 0; + send_frame(client, p); +} + void adb_send_data_frame(adb_client_t *client, apacket *p) { p->msg.command = A_WRTE; @@ -343,8 +343,6 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a { adb_service_t *svc = NULL; - UNUSED(p); - if (client->next_service_id == 0) { /* service id overflow, exit */ fatal("service_id overflow"); @@ -371,7 +369,7 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a /* Search for logcat */ char *ptr = strstr(name, "exec logcat"); if (ptr) { - svc = logcat_service(client, name); + svc = logcat_service(client, ptr); break; } #endif /* CONFIG_ADBD_LOGCAT_SERVICE */ @@ -383,10 +381,9 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a #endif if (!strncmp(name, "reboot:", 7)) { + adb_send_okay_frame(client, p, client->next_service_id++, p->msg.arg0); adb_reboot_impl(&name[7]); - - /* One shot service, skip service register */ - return NULL; + return REBOOT_SERVICE; } } while (0); @@ -408,7 +405,7 @@ void adb_service_close(adb_client_t *client, adb_service_t *svc, apacket *p) { goto exit_free_service; } - while (cur_svc->next) { + while (cur_svc != NULL && cur_svc->next != NULL) { if (cur_svc->next == svc) { cur_svc->next = svc->next; goto exit_free_service; @@ -421,7 +418,7 @@ void adb_service_close(adb_client_t *client, adb_service_t *svc, apacket *p) { exit_free_service: if (p) { - send_close_frame(client, p, svc->id, svc->peer_id); + adb_send_close_frame(client, p, svc->id, svc->peer_id); } svc->ops->on_close(svc); } @@ -484,7 +481,8 @@ void adb_process_packet(adb_client_t *client, apacket *p) { p->write_len = 0; - if (p->msg.command == A_CNXN) { + switch(p->msg.command) { + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ #ifdef CONFIG_ADBD_AUTHENTICATION if (!client->is_connected) { @@ -495,22 +493,16 @@ void adb_process_packet(adb_client_t *client, apacket *p) send_cnxn_frame(client, p); client->is_connected = 1; return; - } #ifdef CONFIG_ADBD_AUTHENTICATION - if (p->msg.command == A_AUTH) { + case A_AUTH: if (!client->is_connected) { handle_auth_frame(client, p); return; } - - goto invalid_frame; - } + break; #endif /* CONFIG_ADBD_AUTHENTICATION */ - /* Client is connected */ - - switch(p->msg.command) { case A_OPEN: handle_open_frame(client, p); return; @@ -531,9 +523,6 @@ void adb_process_packet(adb_client_t *client, apacket *p) break; } -#ifdef CONFIG_ADBD_AUTHENTICATION -invalid_frame: -#endif adb_log("handle_packet: what is %08x?!\n", p->msg.command); adb_hal_apacket_release(client, p); client->ops->close(client); diff --git a/file_sync_service.c b/file_sync_service.c index 1f52341..1eca313 100644 --- a/file_sync_service.c +++ b/file_sync_service.c @@ -48,7 +48,7 @@ #define ID_FAIL MKID('F','A','I','L') #define ID_QUIT MKID('Q','U','I','T') -#define min(a,b) ((a) < (b) ? (a):(b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) #define SYNC_TEMP_BUFF_SIZE PATH_MAX @@ -99,6 +99,7 @@ enum { typedef struct afs_service_s { adb_service_t service; uint8_t *packet_ptr; + uint8_t *payload; uint8_t state; unsigned cmd; @@ -125,7 +126,7 @@ typedef struct afs_service_s { }; unsigned size; - char buff[SYNC_TEMP_BUFF_SIZE]; + char buff[SYNC_TEMP_BUFF_SIZE + 1]; } afs_service_t; /**************************************************************************** @@ -188,13 +189,13 @@ static void prepare_fail_message(afs_service_t *svc, apacket *p, const char *rea adb_err("sync: failure: %s\n", reason); len = min(strlen(reason), - CONFIG_ADBD_PAYLOAD_SIZE - sizeof(msg->data) - p->write_len); - memcpy((char*)(&msg->data+1), reason, len); + CONFIG_ADBD_PAYLOAD_SIZE - sizeof(msg->status) - p->write_len); + memcpy((char*)(&msg->status+1), reason, len); - msg->data.id = ID_FAIL; - msg->data.size = htoll(len); + msg->status.id = ID_FAIL; + msg->status.msglen = htoll(len); - p->write_len += sizeof(msg->data) + len; + p->write_len += sizeof(msg->status) + len; } static void prepare_fail_errno(afs_service_t *svc, apacket *p) @@ -262,6 +263,23 @@ static int create_path_directories(char *name) return 0; } +static uint8_t *get_payload(afs_service_t *svc, apacket *p) +{ + if (svc->size > 0) { + if (svc->payload == NULL) { + svc->payload = malloc(CONFIG_ADBD_PAYLOAD_SIZE); + if (svc->payload == NULL) { + return NULL; + } + } + + memcpy(svc->payload, p->data, p->msg.data_length); + return svc->payload; + } + + return p->data; +} + static void state_reset(afs_service_t *svc) { switch (svc->state) { @@ -582,11 +600,6 @@ static int state_process_send_sym(afs_service_t *svc, apacket *p) { return 0; } - if (svc->namelen >= SYNC_TEMP_BUFF_SIZE) { - prepare_fail_message(svc, p, "symlink target too long"); - return 0; - } - svc->buff[svc->namelen] = 0; ret = symlink(svc->buff, svc->send_link.path); @@ -694,10 +707,6 @@ static int state_wait_cmd_data(afs_service_t *svc, apacket *p) return -1; } - if (svc->namelen >= SYNC_TEMP_BUFF_SIZE) { - return -1; - } - svc->buff[svc->namelen] = 0; switch(svc->cmd) { @@ -714,11 +723,6 @@ static int state_wait_cmd_data(afs_service_t *svc, apacket *p) ret = state_init_recv(svc, p); break; - case ID_QUIT: - // adb_log("got QUIT command\n"); - ret = 0; - break; - default: adb_err("Unexpected command 0x%x\n", svc->cmd); ret = -1; @@ -730,7 +734,7 @@ static int state_wait_cmd_data(afs_service_t *svc, apacket *p) static int file_sync_on_write(adb_service_t *service, apacket *p) { int ret = 0; afs_service_t *svc = container_of(service, afs_service_t, service); - svc->packet_ptr = p->data; + svc->packet_ptr = get_payload(svc, p); /* Process all packet data */ @@ -779,7 +783,7 @@ static int file_sync_on_write(adb_service_t *service, apacket *p) { static int file_sync_on_ack(adb_service_t *service, apacket *p) { int ret; afs_service_t *svc = container_of(service, afs_service_t, service); - svc->packet_ptr = p->data; + svc->packet_ptr = get_payload(svc, p); /* No data in notify packet */ switch (svc->state) { @@ -791,11 +795,19 @@ static int file_sync_on_ack(adb_service_t *service, apacket *p) { ret = state_process_list(svc, p); break; + case AFS_STATE_PROCESS_SEND_FILE_DATA: case AFS_STATE_PROCESS_SEND_FILE_HDR: case AFS_STATE_PROCESS_SEND_SYM_HDR: + case AFS_STATE_WAIT_CMD_DATA: case AFS_STATE_WAIT_CMD: - /* Nothing to do */ - ret = 0; + /* Since the WRITE frame can contain multiple incomplete + * combinations of ID_SEND and ID_DONE, when the pc replies to ID_DONE, + * the OKAY frame sent can be at any time in the state machine. + * At this time, we should not reset the state machine and continue + * to process the next frame status. + */ + + ret = 1; break; default: @@ -817,6 +829,7 @@ static int file_sync_on_ack(adb_service_t *service, apacket *p) { static void file_sync_on_close(struct adb_service_s *service) { afs_service_t *svc = container_of(service, afs_service_t, service); state_reset(svc); + free(svc->payload); free(svc); } @@ -842,6 +855,7 @@ adb_service_t* file_sync_service(const char *params) } service->size = 0; + service->payload = NULL; service->state = AFS_STATE_WAIT_CMD; service->service.ops = &file_sync_ops; diff --git a/hal/hal_uv.c b/hal/hal_uv.c index 9452c09..9e47bb7 100644 --- a/hal/hal_uv.c +++ b/hal/hal_uv.c @@ -23,25 +23,35 @@ #include "hal_uv_priv.h" #include -static adb_context_uv_t g_adbd_context; - /**************************************************************************** * HAL Public Functions ****************************************************************************/ adb_context_t* adb_hal_create_context(void) { - adb_context_uv_t *adbd = &g_adbd_context; + adb_context_uv_t *adbd = malloc(sizeof(adb_context_uv_t)); + if (adbd == NULL) { + return NULL; + } adbd->loop = uv_default_loop(); #ifdef CONFIG_ADBD_TCP_SERVER if (adb_uv_tcp_setup(adbd)) { + adb_hal_destroy_context(&adbd->context); return NULL; } #endif #ifdef CONFIG_ADBD_USB_SERVER if (adb_uv_usb_setup(adbd, "/dev/adb0")) { + adb_hal_destroy_context(&adbd->context); + return NULL; + } +#endif + +#ifdef CONFIG_ADBD_QEMU_SERVER + if (adb_uv_qemu_setup(adbd)) { + adb_hal_destroy_context(&adbd->context); return NULL; } #endif @@ -50,7 +60,11 @@ adb_context_t* adb_hal_create_context(void) { } void adb_hal_destroy_context(adb_context_t *context) { - UNUSED(context); + adb_context_uv_t *adbd = + container_of(context, adb_context_uv_t, context); + + uv_loop_close(adbd->loop); + free(adbd); } adb_client_t *adb_hal_create_client(size_t size) { diff --git a/hal/hal_uv_client_qemu.c b/hal/hal_uv_client_qemu.c new file mode 100644 index 0000000..44aed59 --- /dev/null +++ b/hal/hal_uv_client_qemu.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2024 Xiaomi Inc. All rights reserved. + * + * 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 +#include + +#include "adb.h" +#include "hal_uv_priv.h" +#include + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +/**************************************************************************** + * Private types + ****************************************************************************/ + +typedef struct adb_client_qemu_s { + adb_client_uv_t uc; + /* libuv handle must be right after adb_client_uv_t */ + uv_pipe_t pipe; +} adb_client_qemu_t; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void qemu_uv_allocate_frame(uv_handle_t *handle, + size_t suggested_size, uv_buf_t *buf) { + UNUSED(suggested_size); + adb_client_qemu_t *client = container_of(handle, adb_client_qemu_t, pipe); + + adb_uv_allocate_frame(&client->uc, buf); +} + +static void qemu_uv_on_data_available(uv_stream_t *handle, + ssize_t nread, const uv_buf_t *buf) { + adb_client_qemu_t *client = container_of(handle, adb_client_qemu_t, pipe); + + adb_uv_on_data_available(&client->uc, handle, nread, buf); +} + +static int qemu_uv_write(adb_client_t *c, apacket *p) { + int ret; + uv_buf_t buf; + apacket_uv_t *up = container_of(p, apacket_uv_t, p); + adb_client_qemu_t *client = container_of(c, adb_client_qemu_t, uc.client); + + buf = uv_buf_init((char *)&p->msg, + sizeof(p->msg) + p->msg.data_length); + + /* Packet is now tracked by libuv */ + up->wr.data = &client->uc; + + ret = uv_write(&up->wr, (uv_stream_t *)&client->pipe, &buf, 1, + adb_uv_after_write); + if (ret < 0) { + adb_err("uv_write failed %d %d\n", ret, errno); + /* Caller will destroy client */ + return ret; + } + + return 0; +} + +static void qemu_uv_kick(adb_client_t *c) { + adb_client_qemu_t *client = container_of(c, adb_client_qemu_t, uc.client); + + if (!uv_is_active((uv_handle_t *)&client->pipe)) { + int ret = uv_read_start((uv_stream_t *)&client->pipe, + qemu_uv_allocate_frame, + qemu_uv_on_data_available); + /* TODO check return code */ + assert(ret == 0); + } + + adb_client_kick_services(c); +} + +static void qemu_uv_on_close(uv_handle_t *handle) { + adb_client_qemu_t *client = container_of(handle, adb_client_qemu_t, pipe); + + adb_uv_close_client(&client->uc); +} + +static void qemu_uv_close(adb_client_t *c) { + adb_client_qemu_t *client = container_of(c, adb_client_qemu_t, uc.client); + + /* Close pipe and cancel all pending write requests if any */ + uv_close((uv_handle_t *)&client->pipe, qemu_uv_on_close); +} + +static const adb_client_ops_t adb_qemu_uv_ops = { + .write = qemu_uv_write, + .kick = qemu_uv_kick, + .close = qemu_uv_close +}; + +/* Please refer to: + * https://android.googlesource.com/platform/system/core/+/refs/heads/android11-dev/adb/daemon/transport_qemu.cpp#59 + * https://android.googlesource.com/platform/system/core/+/refs/heads/android11-dev/qemu_pipe/qemu_pipe.cpp#37 + * for qemu adb pipe protocol details. + */ + +static void qemu_uv_on_setup(uv_handle_t *server) { + adb_context_uv_t *adbd = (adb_context_uv_t *)server->data; + + adb_uv_qemu_setup(adbd); +} + +static void qemu_uv_on_readable(uv_poll_t *server, int status, int events) { + int ret; + char buf[2]; + uv_os_fd_t fd; + adb_client_qemu_t *client; + adb_context_uv_t *adbd = (adb_context_uv_t *)server->data; + + ret = uv_fileno((uv_handle_t *)server, &fd); + assert(ret == 0); + uv_close((uv_handle_t *)server, qemu_uv_on_setup); + + if (status < 0 || (events & UV_DISCONNECT)) { + adb_err("connect failed %d %d\n", status, events); + goto err; + } + + ret = read(fd, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + adb_err("read failed %d %d\n", ret, errno); + goto err; + } + + if (buf[0] != 'o' || buf[1] != 'k') { + adb_err("handshake failed\n"); + goto err; + } + + ret = write(fd, "start", 5); + if (ret != 5) { + adb_err("write failed %d %d\n", ret, errno); + goto err; + } + + client = (adb_client_qemu_t *)adb_uv_create_client(sizeof(*client)); + if (client == NULL) { + adb_err("failed to allocate client\n"); + goto err; + } + + /* Setup adb_client */ + client->uc.client.ops = &adb_qemu_uv_ops; + + ret = uv_pipe_init(adbd->loop, &client->pipe, 0); + /* TODO check return code */ + assert(ret == 0); + + ret = uv_pipe_open(&client->pipe, fd); + /* TODO check return code */ + assert(ret == 0); + + ret = uv_read_start((uv_stream_t *)&client->pipe, + qemu_uv_allocate_frame, + qemu_uv_on_data_available); + /* TODO check return code */ + assert(ret == 0); + return; + +err: + close(fd); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int adb_uv_qemu_setup(adb_context_uv_t *adbd) { + const char cookie[] = "pipe:qemud:adb:" + STR(CONFIG_ADBD_QEMU_SERVER_PORT) + "\0accept"; + int ret; + int fd; + + fd = open("/dev/goldfish_pipe", O_RDWR | O_CLOEXEC); + if (fd < 0) { + adb_err("qemu server open error %d %d\n", fd, errno); + return fd; + } + + ret = write(fd, cookie, sizeof(cookie) - 1); + if (ret != sizeof(cookie) - 1) { + adb_err("qemu server write error %d %d\n", ret, errno); + goto err; + } + + ret = uv_poll_init(adbd->loop, &adbd->qemu_server, fd); + adbd->qemu_server.data = adbd; + if (ret < 0) { + adb_err("qemu server init error %d %d\n", ret, errno); + goto err; + } + + ret = uv_poll_start(&adbd->qemu_server, UV_READABLE, qemu_uv_on_readable); + if (ret < 0) { + adb_err("qemu server start error %d %d\n", ret, errno); + qemu_uv_on_readable(&adbd->qemu_server, ret, 0); + } + + return 0; + +err: + close(fd); + return ret; +} diff --git a/hal/hal_uv_client_tcp.c b/hal/hal_uv_client_tcp.c index 0d74dfb..609c23f 100644 --- a/hal/hal_uv_client_tcp.c +++ b/hal/hal_uv_client_tcp.c @@ -130,8 +130,6 @@ static void tcp_on_connection(uv_stream_t* server, int status) { goto exit_close_client; } - client->socket.data = server; - ret = uv_accept(server, (uv_stream_t*)&client->socket); if (ret) { goto exit_close_client; diff --git a/hal/hal_uv_client_usb.c b/hal/hal_uv_client_usb.c index 482fc89..e85b99d 100644 --- a/hal/hal_uv_client_usb.c +++ b/hal/hal_uv_client_usb.c @@ -29,6 +29,12 @@ typedef struct adb_client_usb_s { /* FIXME libuv handle must be right after adb_client_uv_t */ uv_pipe_t read_pipe; uv_pipe_t write_pipe; +#if defined(CONFIG_ADBD_USB_HOTPLUG_BYNOTIFY) + uv_fs_event_t event; +#elif defined(CONFIG_ADBD_USB_HOTPLUG_BYTIMER) + uv_timer_t timer; +#endif + char path[0]; } adb_client_usb_t; /**************************************************************************** @@ -50,6 +56,144 @@ static void usb_uv_on_data_available(uv_stream_t* handle, adb_uv_on_data_available(&client->uc, handle, nread, buf); } +static int usb_uv_open(adb_client_usb_t *client) { + char devname[32]; + int ret; + int fd; + + ret = uv_pipe_init(uv_default_loop(), &client->read_pipe, 0); + if (ret) { + adb_err("usb init error %d %d\n", ret, errno); + return ret; + } + + snprintf(devname, sizeof(devname), "%s/ep2", client->path); + fd = open(devname, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + adb_err("failed to open usb device %d %d\n", fd, errno); + return fd; + } + + ret = uv_pipe_open(&client->read_pipe, fd); + if (ret) { + adb_err("usb pipe open error %d %d\n", ret, errno); + close(fd); + return ret; + } + + ret = uv_pipe_init(uv_default_loop(), &client->write_pipe, 0); + if (ret) { + adb_err("usb init error %d %d\n", ret, errno); + goto err_with_write; + } + + snprintf(devname, sizeof(devname), "%s/ep1", client->path); + fd = open(devname, O_WRONLY | O_CLOEXEC); + if (fd < 0) { + adb_err("failed to open usb device %d %d\n", fd, errno); + goto err_with_write; + } + + ret = uv_pipe_open(&client->write_pipe, fd); + if (ret) { + adb_err("usb pipe open error %d %d\n", ret, errno); + close(fd); + goto err_with_write; + } + + ret = uv_read_start((uv_stream_t*)&client->read_pipe, + usb_uv_allocate_frame, + usb_uv_on_data_available); + if (ret < 0) { + goto err_with_read; + } + + return ret; + +err_with_read: + uv_close((uv_handle_t*)&client->read_pipe, NULL); +err_with_write: + uv_close((uv_handle_t*)&client->write_pipe, NULL); + return ret; +} + +#if defined(CONFIG_ADBD_USB_HOTPLUG_BYNOTIFY) +static void usb_hotplug_check_cb(uv_fs_event_t* handle, + const char* filename, + int events, int status) { + adb_client_usb_t *client = container_of(handle, adb_client_usb_t, event); + int ret; + + if (events == UV_RENAME) { + ret = usb_uv_open(client); + if (ret >= 0) { + uv_fs_event_stop(handle); + uv_close((uv_handle_t*)handle, NULL); + } + } +} + +#elif defined(CONFIG_ADBD_USB_HOTPLUG_BYTIMER) +static void usb_hotplug_check_cb(uv_timer_t* handle) { + adb_client_usb_t *client = container_of(handle, adb_client_usb_t, timer); + struct stat statbuf; + char devname[32]; + int ret; + + snprintf(devname, sizeof(devname), "%s/ep2", client->path); + ret = stat(devname, &statbuf); + if (ret >= 0) { + ret = usb_uv_open(client); + if (ret >= 0) { + uv_timer_stop(handle); + uv_close((uv_handle_t*)handle, NULL); + } + } +} +#endif + +static int usb_hotplug_check(adb_client_usb_t* client) { + int ret = -ENOTSUP; + +#if defined(CONFIG_ADBD_USB_HOTPLUG_BYNOTIFY) + struct stat statbuf; + + ret = stat(client->path, &statbuf); + if (ret < 0) + { + mkdir(client->path, 0666); + } + + ret = uv_fs_event_init(uv_default_loop(), &client->event); + if (ret != 0) { + adb_log("usb inotify init error %d %d\n", ret, errno); + return ret; + } + + ret = uv_fs_event_start(&client->event, usb_hotplug_check_cb, + client->path, 0); + if (ret != 0) { + adb_log("usb notify start error %d %d\n", ret, errno); + } + +#elif defined(CONFIG_ADBD_USB_HOTPLUG_BYTIMER) + ret = uv_timer_init(uv_default_loop(), &client->timer); + if (ret != 0) { + adb_log("usb timer init error %d %d\n", ret, errno); + return ret; + } + + /* Using 1s timer to check usb hotplug */ + + ret = uv_timer_start(&client->timer, usb_hotplug_check_cb, 0, 1000); + if (ret != 0) { + adb_log("usb timer start error %d %d\n", ret, errno); + } +#endif + + return ret; +} + static int usb_uv_write(adb_client_t *c, apacket *p) { int ret; uv_buf_t buf[2]; @@ -88,6 +232,10 @@ static int usb_uv_write(adb_client_t *c, apacket *p) { static void usb_uv_kick(adb_client_t *c) { adb_client_usb_t *client = container_of(c, adb_client_usb_t, uc.client); + if (uv_is_closing((uv_handle_t*)&client->read_pipe)) { + return; + } + if (!uv_is_active((uv_handle_t*)&client->read_pipe)) { /* Restart read events */ int ret = uv_read_start((uv_stream_t*)&client->read_pipe, @@ -104,15 +252,17 @@ static void usb_uv_kick(adb_client_t *c) { static void usb_uv_on_close(uv_handle_t* handle) { adb_client_usb_t *client = container_of(handle, adb_client_usb_t, read_pipe); - adb_uv_close_client(&client->uc); + if (usb_hotplug_check(client) != 0) { + adb_uv_close_client(&client->uc); + } } static void usb_uv_close(adb_client_t *c) { adb_client_usb_t *client = (adb_client_usb_t*)c; /* Close pipe and cancel all pending write requests if any */ - uv_close((uv_handle_t*)&client->write_pipe, NULL); uv_close((uv_handle_t*)&client->read_pipe, usb_uv_on_close); + uv_close((uv_handle_t*)&client->write_pipe, NULL); } static const adb_client_ops_t adb_usb_uv_ops = { @@ -126,12 +276,11 @@ static const adb_client_ops_t adb_usb_uv_ops = { ****************************************************************************/ int adb_uv_usb_setup(adb_context_uv_t *adbd, const char *path) { - char devname[32]; adb_client_usb_t *client; int ret; - int fd; - client = (adb_client_usb_t*)adb_uv_create_client(sizeof(*client)); + client = (adb_client_usb_t*)adb_uv_create_client(sizeof(*client) + + strlen(path) + 1); if (client == NULL) { adb_err("failed to allocate usb client\n"); return -ENOMEM; @@ -140,54 +289,15 @@ int adb_uv_usb_setup(adb_context_uv_t *adbd, const char *path) { /* Setup adb_client */ client->uc.client.ops = &adb_usb_uv_ops; - - ret = uv_pipe_init(adbd->loop, &client->read_pipe, 0); - client->read_pipe.data = adbd; - if (ret) { - adb_err("usb init error %d %d\n", ret, errno); - return ret; - } - - snprintf(devname, sizeof(devname), "%s/ep2", path); - fd = open(devname, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - adb_err("failed to open usb device %d %d\n", fd, errno); - return fd; + strcpy(client->path, path); + + ret = usb_uv_open(client); + if (ret < 0) { + ret = usb_hotplug_check(client); + if (ret != 0) { + adb_uv_close_client(&client->uc); + } } - ret = uv_pipe_open(&client->read_pipe, fd); - if (ret) { - adb_err("usb pipe open error %d %d\n", ret, errno); - close(fd); - return ret; - } - - ret = uv_pipe_init(adbd->loop, &client->write_pipe, 0); - client->write_pipe.data = adbd; - if (ret) { - adb_err("usb init error %d %d\n", ret, errno); - return ret; - } - - snprintf(devname, sizeof(devname), "%s/ep1", path); - fd = open(devname, O_WRONLY | O_CLOEXEC); - if (fd < 0) { - adb_err("failed to open usb device %d %d\n", fd, errno); - return fd; - } - - ret = uv_pipe_open(&client->write_pipe, fd); - if (ret) { - adb_err("usb pipe open error %d %d\n", ret, errno); - close(fd); - return ret; - } - - ret = uv_read_start((uv_stream_t*)&client->read_pipe, - usb_uv_allocate_frame, - usb_uv_on_data_available); - /* TODO check return code */ - assert(ret == 0); - - return 0; + return ret; } diff --git a/hal/hal_uv_priv.h b/hal/hal_uv_priv.h index dd0aff2..f04fcac 100644 --- a/hal/hal_uv_priv.h +++ b/hal/hal_uv_priv.h @@ -50,12 +50,15 @@ typedef struct adb_context_uv_s { #ifdef CONFIG_ADBD_TCP_SERVER uv_tcp_t tcp_server; #endif +#ifdef CONFIG_ADBD_QEMU_SERVER + uv_poll_t qemu_server; +#endif } adb_context_uv_t; #ifdef CONFIG_ADBD_SOCKET_SERVICE struct adb_tcp_socket_s { uv_tcp_t handle; - void (*close_cb)(struct adb_tcp_socket_s*); + void (*on_close_cb)(struct adb_tcp_socket_s*); void (*on_data_cb)(struct adb_tcp_socket_s*, struct apacket_s*); void (*on_write_cb)(struct adb_client_s*, struct adb_tcp_socket_s*, struct apacket_s*, bool); }; @@ -78,6 +81,10 @@ int adb_uv_tcp_setup(adb_context_uv_t *adbd); int adb_uv_usb_setup(adb_context_uv_t *adbd, const char *path); #endif +#ifdef CONFIG_ADBD_QEMU_SERVER +int adb_uv_qemu_setup(adb_context_uv_t *adbd); +#endif + /* hal packet management */ apacket_uv_t* adb_uv_packet_allocate(adb_client_uv_t *client, diff --git a/hal/hal_uv_socket.c b/hal/hal_uv_socket.c index 506b7ef..5ae10c4 100644 --- a/hal/hal_uv_socket.c +++ b/hal/hal_uv_socket.c @@ -47,10 +47,6 @@ static void tcp_stream_allocate_frame(uv_handle_t* handle, static void tcp_stream_on_data_available(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { - UNUSED(handle); - UNUSED(nread); - UNUSED(buf); - apacket_uv_t *ap = container_of(buf->base, apacket_uv_t, p.data); adb_tcp_socket_t *socket = container_of(handle, adb_tcp_socket_t, handle); adb_client_t *client = (adb_client_t*)socket->handle.data; @@ -79,11 +75,11 @@ static void tcp_stream_on_data_available(uv_stream_t* handle, static void socket_close_cb(uv_handle_t* handle) { adb_tcp_socket_t *socket = container_of(handle, adb_tcp_socket_t, handle); - socket->close_cb(socket); + socket->on_close_cb(socket); } -void adb_hal_socket_close(adb_tcp_socket_t *socket, void (*close_cb)(adb_tcp_socket_t*)) { - socket->close_cb = close_cb; +void adb_hal_socket_close(adb_tcp_socket_t *socket, void (*on_close_cb)(adb_tcp_socket_t*)) { + socket->on_close_cb = on_close_cb; uv_close((uv_handle_t*)&socket->handle, socket_close_cb); } @@ -105,7 +101,7 @@ int adb_hal_socket_stop(adb_tcp_socket_t *socket) { return uv_read_stop((uv_stream_t*)&socket->handle); } -static void fwd_tcp_after_write(uv_write_t* req, int status) { +static void tcp_stream_after_write(uv_write_t* req, int status) { apacket_uv_t *up = container_of(req, apacket_uv_t, wr); adb_tcp_socket_t *socket = (adb_tcp_socket_t*)req->data; adb_client_t *client = (adb_client_t*)socket->handle.data; @@ -126,7 +122,7 @@ int adb_hal_socket_write(adb_tcp_socket_t *socket, apacket *p, uv_p->wr.data = socket; socket->on_write_cb = cb; - ret = uv_write(&uv_p->wr, (uv_stream_t*)&socket->handle, &buf, 1, fwd_tcp_after_write); + ret = uv_write(&uv_p->wr, (uv_stream_t*)&socket->handle, &buf, 1, tcp_stream_after_write); if (ret) { adb_err("uv_write failed (len=%d, ret=%d, errno=%d)\n", buf.len, ret, errno); return -1; @@ -162,6 +158,6 @@ int adb_hal_socket_connect(adb_client_t *client, adb_tcp_socket_t *socket, return uv_tcp_connect(&conn->connect_req, &socket->handle, - (const struct sockaddr*) &addr, + (const struct sockaddr*)&addr, connect_cb); } diff --git a/hal/shell_service_uv.c b/hal/shell_service_uv.c index 0971dec..017cdc3 100644 --- a/hal/shell_service_uv.c +++ b/hal/shell_service_uv.c @@ -40,6 +40,7 @@ typedef struct ash_service_s { uv_pipe_t shell_pipe; uv_process_t process; int wait_ack; + bool exiting; } ash_service_t; /**************************************************************************** @@ -89,10 +90,20 @@ static int shell_set_cloexec(int fd) { static void on_child_exit(uv_process_t *process, int64_t exit_status, int term_signal) { ash_service_t *svc = (ash_service_t*)process->data; + adb_client_uv_t *client = (adb_client_uv_t *)svc->shell_pipe.data; + apacket_uv_t *p = adb_uv_packet_allocate(client, 0); - adb_log("shell %d<->%d exited with status %ld, signal %d\n", + if (p) { + adb_send_close_frame(&client->client, &p->p, + svc->service.id, svc->service.peer_id); + } + else { + svc->exiting = true; + } + + adb_log("shell %d<->%d exited with status %ld, signal %d, flag %d\n", svc->service.id, svc->service.peer_id, - exit_status, term_signal); + exit_status, term_signal, svc->exiting); } static void alloc_buffer(uv_handle_t *handle, size_t len, uv_buf_t *buf) { @@ -131,7 +142,7 @@ static void pipe_on_data_available(uv_stream_t* stream, ssize_t nread, if (nread != UV_EOF) { adb_err("closing due to error: %d\n", nread); } - adb_service_close(&client->client, &service->service, p); + adb_hal_apacket_release(&client->client, p); return; } @@ -153,7 +164,7 @@ static void shell_after_write(uv_write_t* req, int status) { if (status < 0) { adb_err("uv_write failed %d\n", status); - adb_service_close(&client->client, &svc->service, &up->p); + adb_hal_apacket_release(&client->client, &up->p); return; } @@ -169,14 +180,23 @@ static int shell_write(adb_service_t *service, apacket *p) { apacket_uv_t *up = container_of(p, apacket_uv_t, p); ash_service_t *svc = container_of(service, ash_service_t, service); - buf = uv_buf_init((char*)&p->data, p->msg.data_length); - up->wr.data = svc; + if (svc->exiting) { + adb_client_uv_t *client = (adb_client_uv_t *)svc->shell_pipe.data; - ret = uv_write(&up->wr, (uv_stream_t*)&svc->shell_pipe, &buf, 1, - shell_after_write); - if (ret) { - adb_err("uv_write failed %d %d\n", ret, errno); - return -1; + svc->exiting = false; + adb_send_close_frame(&client->client, p, + svc->service.id, svc->service.peer_id); + } + else { + buf = uv_buf_init((char*)&p->data, p->msg.data_length); + up->wr.data = svc; + + ret = uv_write(&up->wr, (uv_stream_t*)&svc->shell_pipe, &buf, 1, + shell_after_write); + if (ret) { + adb_err("uv_write failed %d %d\n", ret, errno); + return -1; + } } /* Notify ADB client that packet is now managed by service */ @@ -195,7 +215,17 @@ static int shell_ack(adb_service_t *service, apacket *p) { static void shell_kick(adb_service_t *service) { ash_service_t *svc = container_of(service, ash_service_t, service); - if (!svc->wait_ack) { + if (svc->exiting) { + adb_client_uv_t *client = (adb_client_uv_t *)svc->shell_pipe.data; + apacket_uv_t *p = adb_uv_packet_allocate(client, 0); + + if (p) { + svc->exiting = false; + adb_send_close_frame(&client->client, &p->p, + svc->service.id, svc->service.peer_id); + } + } + else if (!svc->wait_ack) { if (!uv_is_active((uv_handle_t*)&svc->shell_pipe)) { /* No need to check return code as it would only fail when * in case the pipe fd is closing */ @@ -219,8 +249,6 @@ static void shell_close(adb_service_t *service) { /* Terminate child process in case it is still running */ - uv_process_kill(&svc->process, SIGKILL); - uv_close((uv_handle_t *)&svc->shell_pipe, shell_close_pipe_callback); } @@ -256,6 +284,7 @@ adb_service_t *shell_service(adb_client_t *client, const char *params) { service->shell_pipe.data = client; service->process.data = service; service->wait_ack = 0; + service->exiting = false; target_cmd = ¶ms[sizeof(ADB_SHELL_PREFIX)-1];