From c1c488e835f7433ddec4de57024a4e3444359f96 Mon Sep 17 00:00:00 2001 From: Simon Piriou Date: Mon, 26 Oct 2020 17:18:48 +0100 Subject: [PATCH] system: add Android Debug Bridge daemon --- system/adb/Kconfig | 158 ++++++++++++++++++++++++ system/adb/Make.defs | 23 ++++ system/adb/Makefile | 92 ++++++++++++++ system/adb/adb_banner.c | 117 ++++++++++++++++++ system/adb/adb_main.c | 119 ++++++++++++++++++ system/adb/logcat_service.c | 213 ++++++++++++++++++++++++++++++++ system/adb/shell_pipe.c | 237 ++++++++++++++++++++++++++++++++++++ system/adb/shell_pipe.h | 56 +++++++++ system/adb/shell_service.c | 209 +++++++++++++++++++++++++++++++ 9 files changed, 1224 insertions(+) create mode 100644 system/adb/Kconfig create mode 100644 system/adb/Make.defs create mode 100644 system/adb/Makefile create mode 100644 system/adb/adb_banner.c create mode 100644 system/adb/adb_main.c create mode 100644 system/adb/logcat_service.c create mode 100644 system/adb/shell_pipe.c create mode 100644 system/adb/shell_pipe.h create mode 100644 system/adb/shell_service.c diff --git a/system/adb/Kconfig b/system/adb/Kconfig new file mode 100644 index 000000000..83a8019e6 --- /dev/null +++ b/system/adb/Kconfig @@ -0,0 +1,158 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_ADBD + tristate "ADB daemon application" + default n + ---help--- + Enable support for adb daemon. + +if SYSTEM_ADBD + +config ADBD_PROGNAME + string "Program name" + default "adbd" + ---help--- + This is the name of the program that will be used. + +config ADBD_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + ---help--- + The size of stack allocated for the adb daemon task. + +config ADBD_PRIORITY + int "Task priority" + default 100 + ---help--- + The priority of the adb daemon task. + +config ADBD_AUTHENTICATION + bool "Authentication support" + default n + ---help--- + Enable authentication for adb daemon. + +if ADBD_AUTHENTICATION + +config ADBD_AUTH_PUBKEY + bool "Public key authentication" + default n + ---help--- + Enable hook to accept new public keys. + +config ADBD_TOKEN_SIZE + int "Authentication token size" + default 20 + +endif # ADBD_AUTHENTICATION + +if ! BOARDCTL_UNIQUEID +config ADBD_DEVICE_ID + string "Default adb device id" + default "" +endif # BOARDCTL_UNIQUEID + +config ADBD_PRODUCT_NAME + string "Default adb product name" + default "adb dev" + +config ADBD_PRODUCT_MODEL + string "Default adb product model" + default "adb board" + +config ADBD_PRODUCT_DEVICE + string "Default adb product device" + default "NuttX device" + +config ADBD_FEATURES + string "Default adb server features list" + default "cmd" + +config ADBD_PAYLOAD_SIZE + int "Normal ADB frame size" + default 64 + ---help--- + Normal frame size in bytes. + +config ADBD_CNXN_PAYLOAD_SIZE + int "Connection frame size" + default 1024 + ---help--- + Connection frame is bigger than others. + Can be between 128 to 256 bytes in most of the cases. + If authentication is enabled, frame size must bigger + to receive public key from host (around 1024 bytes). + +config ADBD_FRAME_MAX + int "Frame pool size" + default 1 + ---help--- + ADB frame pool size. + +config ADBD_TCP_SERVER + bool "Network socket transport support" + depends on NET_TCP + default n + ---help--- + Run adb daemon on network socket. + +config ADBD_TCP_SERVER_PORT + int "Network socket transport port" + depends on ADBD_TCP_SERVER + default 5555 + ---help--- + Port used by adb daemon socket server + +config ADBD_USB_SERVER + bool "USB transport support" + depends on USBADB + default n + ---help--- + Run adb daemon on USB bus + +config ADBD_LOGCAT_SERVICE + bool "ADB logcat support" + depends on RAMLOG_SYSLOG + default n + ---help--- + Enable "adb logcat" feature. + +config ADBD_FILE_SERVICE + bool "ADB file sync support" + default n + ---help--- + Enable "adb ls/push/pull" feature. + + config ADBD_SHELL_SERVICE + bool "ADB shell support" + depends on NSH_CONSOLE + default n + ---help--- + Enable "adb shell" feature. + +config ADBD_FILE_SYMLINK + bool "File service symlink support" + default n + depends on PSEUDOFS_SOFTLINKS + ---help--- + Enable fs symlink support. + +config ADBD_BOARD_INIT + bool "Board initialization" + depends on LIB_BOARDCTL + default n + ---help--- + Setup board before running adb daemon. + +config ADBD_NET_INIT + bool "Network initialization" + default n + depends on NET + select NETUTILS_NETINIT + ---help--- + This option enables/disables all network initialization in ADB server. + +endif # SYSTEM_ADBD diff --git a/system/adb/Make.defs b/system/adb/Make.defs new file mode 100644 index 000000000..ead70b7d5 --- /dev/null +++ b/system/adb/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# system/adb/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_ADBD),) +CONFIGURED_APPS += $(APPDIR)/system/adb +endif diff --git a/system/adb/Makefile b/system/adb/Makefile new file mode 100644 index 000000000..f5e77785d --- /dev/null +++ b/system/adb/Makefile @@ -0,0 +1,92 @@ +############################################################################ +# system/adb/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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 $(APPDIR)/Make.defs + +ADB_DIR := $(APPDIR)/system/adb +CONFIG_ADBD_URL ?= "https://github.com/spiriou/microADB.git" +CONFIG_ADBD_VERSION ?= bbd1e74bd795aa2fc53eae2b76bff993d6ccaa37 + +ADB_UNPACKNAME := microADB +ADB_UNPACKDIR := $(ADB_DIR)/$(ADB_UNPACKNAME) + +$(ADB_UNPACKDIR): + @echo "Downloading: $(ADB_UNPACKNAME)" + $(call DELDIR, "$@") + $(Q) mkdir "$@" + $(Q) cd "$@" && git init && \ + git remote add origin "$(CONFIG_ADBD_URL)" && \ + git fetch origin $(CONFIG_ADBD_VERSION) --depth=1 && \ + git reset --hard FETCH_HEAD + +# adb server app + +PROGNAME := $(CONFIG_ADBD_PROGNAME) +PRIORITY := $(CONFIG_ADBD_PRIORITY) +STACKSIZE := $(CONFIG_ADBD_STACKSIZE) +MODULE := $(CONFIG_ADB_SERVER) + +# Files + +MAINSRC := adb_main.c +CSRCS += adb_banner.c +CSRCS += $(ADB_UNPACKNAME)/adb_client.c +CSRCS += $(ADB_UNPACKNAME)/adb_frame.c + +CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv.c +CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_packet.c + +CFLAGS += -D__NUTTX__=1 +CFLAGS += -I$(ADB_UNPACKNAME) + +ifeq ($(CONFIG_ADBD_TCP_SERVER),y) +CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_client_tcp.c +endif + +ifeq ($(CONFIG_ADBD_USB_SERVER),y) +CSRCS += $(ADB_UNPACKNAME)/hal/hal_uv_client_usb.c +endif + +ifeq ($(CONFIG_ADBD_AUTHENTICATION),y) +CSRCS += $(ADB_UNPACKNAME)/adb_auth_key.c +endif + +ifeq ($(CONFIG_ADBD_FILE_SERVICE),y) +CSRCS += $(ADB_UNPACKNAME)/file_sync_service.c +endif + +ifeq ($(CONFIG_ADBD_LOGCAT_SERVICE),y) +CSRCS += logcat_service.c +endif + +ifeq ($(CONFIG_ADBD_SHELL_SERVICE),y) +CSRCS += shell_service.c +CSRCS += shell_pipe.c +endif + +context:: $(ADB_UNPACKDIR) + +clean:: + $(call DELFILE, $(OBJS)) + +distclean:: + $(call DELDIR, $(ADB_UNPACKDIR)) + +include $(APPDIR)/Application.mk diff --git a/system/adb/adb_banner.c b/system/adb/adb_banner.c new file mode 100644 index 000000000..e44675670 --- /dev/null +++ b/system/adb/adb_banner.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * system/adb/adb_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "adb.h" +#ifdef CONFIG_BOARDCTL_UNIQUEID +#include +#include +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int adb_fill_connect_data(char *buf, size_t bufsize) +{ + size_t len; + size_t remaining = bufsize; + +#ifdef CONFIG_BOARDCTL_UNIQUEID + /* Get board id */ + + int ret; + uint8_t board_id[CONFIG_BOARDCTL_UNIQUEID_SIZE]; + + memset(board_id, 0, CONFIG_BOARDCTL_UNIQUEID_SIZE); + ret = boardctl(BOARDIOC_UNIQUEID, (uintptr_t)board_id); + + if (ret) + { + /* Failed to get board id */ + + adb_log("failed to get board id\n"); + len = snprintf(buf, remaining, "device::"); + } + else + { + /* FIXME only keep first 4 bytes */ + + len = snprintf(buf, remaining, "device:%x:", *(uint32_t *)board_id); + } +#else + len = snprintf(buf, remaining, "device:" CONFIG_ADBD_DEVICE_ID ":"); +#endif + + if (len >= remaining) + { + return -1; + } + +#ifdef CONFIG_ADBD_PRODUCT_NAME + remaining -= len; + buf += len; + len = snprintf(buf, remaining, + "ro.product.name=" CONFIG_ADBD_PRODUCT_NAME ";"); + + if (len >= remaining) + { + return bufsize; + } +#endif + +#ifdef CONFIG_ADBD_PRODUCT_MODEL + remaining -= len; + buf += len; + len = snprintf(buf, remaining, + "ro.product.model=" CONFIG_ADBD_PRODUCT_MODEL ";"); + + if (len >= remaining) + { + return bufsize; + } +#endif + +#ifdef CONFIG_ADBD_PRODUCT_DEVICE + remaining -= len; + buf += len; + len = snprintf(buf, remaining, + "ro.product.device=" CONFIG_ADBD_PRODUCT_DEVICE ";"); + + if (len >= remaining) + { + return bufsize; + } +#endif + + remaining -= len; + buf += len; + len = snprintf(buf, remaining, "features=" CONFIG_ADBD_FEATURES); + + if (len >= remaining) + { + return bufsize; + } + + return bufsize - remaining + len; +} diff --git a/system/adb/adb_main.c b/system/adb/adb_main.c new file mode 100644 index 000000000..d80c74613 --- /dev/null +++ b/system/adb/adb_main.c @@ -0,0 +1,119 @@ +/**************************************************************************** + * system/adb/adb_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "adb.h" + +#ifdef CONFIG_ADBD_BOARD_INIT +#include +#endif + +#ifdef CONFIG_ADBD_NET_INIT +#include "netutils/netinit.h" +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void adb_log_impl(FAR const char *func, int line, FAR const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "%s (%d): ", func, line); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +int main(int argc, FAR char **argv) +{ + UNUSED(argc); + UNUSED(argv); + + adb_context_t *ctx; + +#ifdef CONFIG_ADBD_BOARD_INIT +{ + boardctl(BOARDIOC_INIT, 0); + +#if defined(CONFIG_ADBD_USB_SERVER) && \ + defined(CONFIG_USBDEV_COMPOSITE) && \ + defined (CONFIG_BOARDCTL_USBDEVCTRL) + + /* Setup composite USB device */ + + struct boardioc_usbdev_ctrl_s ctrl; + int ret; + FAR void *handle; + + /* Perform architecture-specific initialization */ + + ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE; + ctrl.action = BOARDIOC_USBDEV_INITIALIZE; + ctrl.instance = 0; + ctrl.config = 0; + ctrl.handle = NULL; + + ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + if (ret < 0) + { + printf("boardctl(BOARDIOC_USBDEV_CONTROL) failed: %d\n", ret); + return 1; + } + + /* Initialize the USB composite device device */ + + ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE; + ctrl.action = BOARDIOC_USBDEV_CONNECT; + ctrl.instance = 0; + ctrl.config = 0; + ctrl.handle = &handle; + + ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + if (ret < 0) + { + printf("boardctl(BOARDIOC_USBDEV_CONTROL) failed: %d\n", ret); + return 1; + } +#endif /* ADBD_USB_SERVER && USBDEV_COMPOSITE && BOARDCTL_USBDEVCTRL */ +} + +#endif /* CONFIG_ADBD_BOARD_INIT */ + +#ifdef CONFIG_ADBD_NET_INIT + /* Bring up the network */ + + netinit_bringup(); +#endif + + ctx = adb_hal_create_context(); + if (!ctx) + { + return -1; + } + + adb_hal_run(ctx); + adb_hal_destroy_context(ctx); + return 0; +} diff --git a/system/adb/logcat_service.c b/system/adb/logcat_service.c new file mode 100644 index 000000000..c65f3dedc --- /dev/null +++ b/system/adb/logcat_service.c @@ -0,0 +1,213 @@ +/**************************************************************************** + * system/adb/logcat_service_uv.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include "adb.h" +#include "logcat_service.h" +#include "hal/hal_uv_priv.h" + +/**************************************************************************** + * Private types + ****************************************************************************/ + +typedef struct alog_service_s +{ + adb_service_t service; + uv_poll_t poll; + int wait_ack; +} alog_service_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void logcat_on_data_available(uv_poll_t * handle, + int status, int events); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int alog_on_write(adb_service_t *service, apacket *p) +{ + UNUSED(p); + UNUSED(service); + return -1; +} + +static void alog_on_kick(struct adb_service_s *service) +{ + alog_service_t *svc = container_of(service, alog_service_t, service); + if (!svc->wait_ack) + { + int ret; + ret = uv_poll_start(&svc->poll, UV_READABLE, logcat_on_data_available); + assert(ret == 0); + } +} + +static int alog_on_ack(adb_service_t *service, apacket *p) +{ + UNUSED(p); + alog_service_t *svc = container_of(service, alog_service_t, service); + svc->wait_ack = 0; + alog_on_kick(service); + return 0; +} + +static void close_cb(uv_handle_t *handle) +{ + alog_service_t *service = container_of(handle, alog_service_t, poll); + free(service); +} + +static void alog_close(struct adb_service_s *service) +{ + int fd; + int ret; + alog_service_t *svc = container_of(service, alog_service_t, service); + + ret = uv_fileno((uv_handle_t *)&svc->poll, &fd); + assert(ret == 0); + + close(fd); + uv_close((uv_handle_t *)&svc->poll, close_cb); +} + +static const adb_service_ops_t logcat_ops = +{ + .on_write_frame = alog_on_write, + .on_ack_frame = alog_on_ack, + .on_kick = alog_on_kick, + .close = alog_close +}; + +static void logcat_on_data_available(uv_poll_t * handle, + int status, int events) +{ + int ret; + int fd; + apacket_uv_t *ap; + alog_service_t *service = container_of(handle, alog_service_t, poll); + adb_client_uv_t *client = (adb_client_uv_t *)handle->data; + + ap = adb_uv_packet_allocate(client, 0); + if (ap == NULL) + { + uv_poll_stop(handle); + return; + } + + if (status) + { + adb_log("status error %d\n", status); + + /* Fatal error, stop service */ + + goto exit_stop_service; + } + + assert(uv_fileno((uv_handle_t *)handle, &fd) == 0); + ret = read(fd, ap->p.data, CONFIG_ADBD_PAYLOAD_SIZE); + + if (ret < 0) + { + adb_log("frame read failed %d %d\n", ret, errno); + if (errno == EAGAIN) + { + /* TODO this should never happen */ + + goto exit_release_packet; + } + + /* Fatal error, stop service */ + + goto exit_stop_service; + } + + if (ret == 0) + { + goto exit_release_packet; + } + + service->wait_ack = 1; + uv_poll_stop(handle); + + ap->p.write_len = ret; + ap->p.msg.arg0 = service->service.id; + ap->p.msg.arg1 = service->service.peer_id; + adb_send_data_frame(&client->client, &ap->p); + return; + +exit_release_packet: + adb_hal_apacket_release(&client->client, &ap->p); + return; + +exit_stop_service: + adb_service_close(&client->client, &service->service, &ap->p); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +adb_service_t * logcat_service(adb_client_t *client, const char *params) +{ + int ret; + alog_service_t *service = + (alog_service_t *)malloc(sizeof(alog_service_t)); + + if (service == NULL) + { + return NULL; + } + + service->service.ops = &logcat_ops; + service->wait_ack = 0; + + /* TODO parse params string to extract logcat parameters */ + + ret = open(CONFIG_SYSLOG_DEVPATH, O_RDONLY | O_CLOEXEC); + + if (ret < 0) + { + adb_log("failed to open %s (%d)\n", CONFIG_SYSLOG_DEVPATH, errno); + free(service); + return NULL; + } + + uv_handle_t *handle = adb_uv_get_client_handle(client); + ret = uv_poll_init(handle->loop, &service->poll, ret); + assert(ret == 0); + + service->poll.data = client; + alog_on_kick(&service->service); + + return &service->service; +} diff --git a/system/adb/shell_pipe.c b/system/adb/shell_pipe.c new file mode 100644 index 000000000..ad59b71ed --- /dev/null +++ b/system/adb/shell_pipe.c @@ -0,0 +1,237 @@ +/**************************************************************************** + * system/adb/shell_pipe.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "adb.h" +#include "shell_pipe.h" +#include "hal/hal_uv_priv.h" + +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void shell_on_data_available(uv_poll_t * handle, + int status, int events) +{ + int ret; + apacket_uv_t *ap; + shell_pipe_t *pipe = container_of(handle, shell_pipe_t, handle); + + adb_client_t *client = (adb_client_t *)pipe->handle.data; + + if (status) + { + adb_log("status error %d\n", status); + + /* FIXME missing logic here */ + + pipe->on_data_cb(pipe, NULL); + return; + } + + ap = adb_uv_packet_allocate((adb_client_uv_t *)client, 0); + if (ap == NULL) + { + /* frame allocation failed. Try again later */ + + uv_poll_stop(&pipe->handle); + return; + } + + int nread = 0; + do + { + ret = read(handle->io_watcher.fd, &ap->p.data[nread], 1); + + if (ret == 0) + { + /* EOF */ + + break; + } + + if (ret < 0) + { + /* Revisit. EAGAIN should not happen but it happens a lot */ + + if (errno == EAGAIN) + { + if (nread <= 0) + { + adb_hal_apacket_release( + (adb_client_t *)pipe->handle.data, &ap->p); + return; + } + break; + } + + /* Release packet and forward error */ + + adb_hal_apacket_release((adb_client_t *)pipe->handle.data, &ap->p); + pipe->on_data_cb(pipe, NULL); + return; + } + + /* FIXME CR LF conversion */ + + if (ap->p.data[nread++] == '\n') + { + ap->p.data[nread++] = '\r'; + } + } + while (nread < CONFIG_ADBD_PAYLOAD_SIZE - 1); + + ap->p.msg.data_length = nread; + pipe->on_data_cb(pipe, &ap->p); +} + +static void shell_pipe_close_callback(uv_handle_t *handle) +{ + shell_pipe_t *pipe = container_of(handle, shell_pipe_t, handle); + + /* Notify caller pipe is closed */ + + pipe->close_cb(pipe); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int shell_pipe_setup(adb_client_t *client, shell_pipe_t *apipe) +{ + apipe->handle.data = client; + return 0; +} + +void shell_pipe_destroy(shell_pipe_t *pipe, void (*close_cb)(shell_pipe_t *)) +{ + pipe->close_cb = close_cb; + close(pipe->write_fd); + uv_close((uv_handle_t *)&pipe->handle, shell_pipe_close_callback); +} + +int shell_pipe_write(shell_pipe_t *pipe, const void *buf, size_t count) +{ + /* TODO revisit */ + + return write(pipe->write_fd, buf, count); +} + +int shell_pipe_start(shell_pipe_t *pipe, + void (*on_data_cb)(shell_pipe_t *, apacket *)) +{ + pipe->on_data_cb = on_data_cb; + return uv_poll_start(&pipe->handle, UV_READABLE, shell_on_data_available); +} + +int shell_pipe_exec(char * const argv[], shell_pipe_t *apipe, + void (*on_data_cb)(shell_pipe_t *, apacket *)) +{ + int ret; + int in_fds[2]; + int out_fds[2]; + + adb_client_uv_t *client = (adb_client_uv_t *)apipe->handle.data; + + /* Create pipe for stdin */ + + ret = pipe(in_fds); + assert(ret == 0); + ret = pipe(out_fds); + assert(ret == 0); + + /* TODO check return code */ + + apipe->write_fd = in_fds[1]; + + /* Setup stdout (read: adb, write: child) */ + + ret = dup2(out_fds[1], 1); + assert(ret == 0); + + ret = close(out_fds[1]); + assert(ret == 0); + + ret = fcntl(out_fds[0], F_GETFD); + assert(ret >= 0); + ret = fcntl(out_fds[0], F_SETFD, ret | FD_CLOEXEC); + assert(ret == 0); + ret = fcntl(out_fds[0], F_GETFL); + assert(ret >= 0); + ret = fcntl(out_fds[0], F_SETFL, ret | O_NONBLOCK); + assert(ret >= 0); + + /* Setup stdin */ + + ret = dup2(in_fds[0], 0); + assert(ret == 0); + + ret = close(in_fds[0]); + assert(ret == 0); + + ret = fcntl(in_fds[1], F_GETFD); + assert(ret >= 0); + ret = fcntl(in_fds[1], F_SETFD, ret | FD_CLOEXEC); + assert(ret == 0); + ret = fcntl(in_fds[1], F_GETFL); + assert(ret >= 0); + ret = fcntl(in_fds[1], F_SETFL, ret | O_NONBLOCK); + assert(ret == 0); + + ret = uv_poll_init( + adb_uv_get_client_handle(client)->loop, + &apipe->handle, out_fds[0]); + + /* TODO check return code */ + + assert(ret == 0); + + /* Create shell process */ + + ret = task_create("ADB shell", CONFIG_SYSTEM_NSH_PRIORITY, + CONFIG_SYSTEM_NSH_STACKSIZE, nsh_consolemain, + argv); + + /* Close stdin and stdout */ + + dup2(2, 0); + dup2(2, 1); + + /* TODO check return code */ + + assert(ret >= 0); + + /* Start listening shell process stdout */ + + ret = shell_pipe_start(apipe, on_data_cb); + + /* TODO check return code */ + + assert(ret == 0); + return 0; +} diff --git a/system/adb/shell_pipe.h b/system/adb/shell_pipe.h new file mode 100644 index 000000000..16ca80f1f --- /dev/null +++ b/system/adb/shell_pipe.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * system/adb/shell_pipe.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "adb.h" + +/**************************************************************************** + * Private types + ****************************************************************************/ + +struct shell_pipe_s +{ + uv_poll_t handle; + int write_fd; + void (*close_cb)(struct shell_pipe_s *); + void (*on_data_cb)(struct shell_pipe_s *, struct apacket_s *); +}; + +typedef struct shell_pipe_s shell_pipe_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int shell_pipe_setup(adb_client_t *client, shell_pipe_t *pipe); +int shell_pipe_start(shell_pipe_t *pipe, + void (*on_data_cb)(shell_pipe_t *, apacket *)); +void shell_pipe_destroy(shell_pipe_t *pipe, + void (*close_cb)(shell_pipe_t *)); +int shell_pipe_write(shell_pipe_t *pipe, const void *buf, size_t count); + +int shell_pipe_exec(char * const argv[], shell_pipe_t *pipe, + void (*on_data_cb)(shell_pipe_t *, apacket *)); +int shell_exec_builtin(const char *appname, FAR char *const *argv, + shell_pipe_t *apipe); diff --git a/system/adb/shell_service.c b/system/adb/shell_service.c new file mode 100644 index 000000000..04bdfeba7 --- /dev/null +++ b/system/adb/shell_service.c @@ -0,0 +1,209 @@ +/**************************************************************************** + * system/adb/shell_service.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "adb.h" +#include "shell_service.h" +#include "shell_pipe.h" + +/**************************************************************************** + * Private types + ****************************************************************************/ + +typedef struct ash_service_s +{ + adb_service_t service; + shell_pipe_t pipe; + adb_client_t *client; +} ash_service_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void exec_on_data_available(shell_pipe_t * pipe, apacket * p); + +static int shell_ack(adb_service_t *service, apacket *p); +static int shell_write(adb_service_t *service, apacket *p); +static void shell_close(struct adb_service_s *service); +static void shell_kick(adb_service_t *service); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void exec_on_data_available(shell_pipe_t * pipe, apacket * p) +{ + ash_service_t *service = container_of(pipe, ash_service_t, pipe); + + if (p->msg.data_length <= 0) + { + /* Got EOF */ + + adb_service_close(service->client, &service->service, p); + return; + } + + p->write_len = p->msg.data_length; + p->msg.arg0 = service->service.id; + p->msg.arg1 = service->service.peer_id; + adb_send_data_frame(service->client, p); +} + +static int shell_write(adb_service_t *service, apacket *p) +{ + int ret; + ash_service_t *svc = container_of(service, ash_service_t, service); + UNUSED(svc); + + if (p->msg.data_length <= 0) + { + return -1; + } + + ret = shell_pipe_write(&svc->pipe, p->data, p->msg.data_length); + + if (ret < 0) + { + /* Shell process terminated, close service */ + + return -1; + } + + assert(ret == p->msg.data_length); + return 0; +} + +static int shell_ack(adb_service_t *service, apacket *p) +{ + UNUSED(service); + UNUSED(p); + return 0; +} + +static void shell_kick(adb_service_t *service) +{ + int ret; + ash_service_t *svc = container_of(service, ash_service_t, service); + ret = shell_pipe_start(&svc->pipe, exec_on_data_available); + + /* TODO handle return code */ + + assert(ret == 0); +} + +static void shell_on_close(shell_pipe_t *pipe) +{ + ash_service_t *svc = container_of(pipe, ash_service_t, pipe); + free(svc); +} + +static void shell_close(adb_service_t *service) +{ + ash_service_t *svc = container_of(service, ash_service_t, service); + + /* FIXME missing logic here if shell process is still running */ + + shell_pipe_destroy(&svc->pipe, shell_on_close); +} + +static const adb_service_ops_t shell_ops = +{ + .on_write_frame = shell_write, + .on_ack_frame = shell_ack, + .on_kick = shell_kick, + .close = shell_close +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +adb_service_t * shell_service(adb_client_t *client, const char *params) +{ + UNUSED(params); + UNUSED(client); + + int ret; + char **argv; + const char *target; + ash_service_t *service = + (ash_service_t *)malloc(sizeof(ash_service_t)); + + if (service == NULL) + { + return NULL; + } + + service->client = client; + service->service.ops = &shell_ops; + + ret = shell_pipe_setup(client, &service->pipe); + + /* TODO check return code */ + + assert(ret == 0); + + /* Check parameters after "shell:" */ + + target = ¶ms[6]; + + if (target[0] != 0) + { + /* Build argv: + * argv[0] => "-c" + * argv[1] => command + * argv[2] => NULL + * + * malloc content: + * - x3 argv pointers + * - 3 characters: "-c\0" + * - space for command string + */ + + argv = malloc(sizeof(char *) * 3 + 3 + (strlen(target)+1)); + + argv[0] = (char *)&argv[3]; + argv[1] = &((char *)&argv[3])[3]; + argv[2] = NULL; + strcpy(argv[0], "-c"); + strcpy(argv[1], target); + } + else + { + argv = NULL; + } + + ret = shell_pipe_exec(argv, &service->pipe, + exec_on_data_available); + + /* TODO check return code */ + + assert(ret == 0); + + free(argv); + + return &service->service; +}