From e19c3ca3fa695e223954f509cf339faeb24c72ad Mon Sep 17 00:00:00 2001 From: Boundary Effect Date: Sun, 6 Oct 2019 10:29:44 +0200 Subject: [PATCH] Add a USB API compatible with libusb --- Makefile | 1 + scripts/termux-usb | 57 ++++++++++++++++++++++++++++++++++++++++++++++ termux-api.c | 38 +++++++++++++++++++++++++++---- termux-callback | 4 ++++ 4 files changed, 96 insertions(+), 4 deletions(-) create mode 100755 scripts/termux-usb create mode 100755 termux-callback diff --git a/Makefile b/Makefile index ce7868e..09e96cf 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ termux-api: termux-api.c install: termux-api mkdir -p $(PREFIX)/bin/ $(PREFIX)/libexec/ install termux-api $(PREFIX)/libexec/ + install termux-callback $(PREFIX)/libexec/ install scripts/* $(PREFIX)/bin/ .PHONY: install diff --git a/scripts/termux-usb b/scripts/termux-usb new file mode 100755 index 0000000..5200b7a --- /dev/null +++ b/scripts/termux-usb @@ -0,0 +1,57 @@ +#!/data/data/com.termux/files/usr/bin/bash +set -e -u + +SCRIPTNAME=termux-usb +show_usage () { + echo "Usage: $SCRIPTNAME [-l | [-r] [-e command] device]" + echo "List or access USB devices. Devices cannot be accessed directly," + echo " only using $SCRIPTNAME." + echo " -l list available devices" + echo " -r show permission request dialog if necessary" + echo " -e command execute the specified command with a file descriptor" + echo " referring to the device as its argument" + exit 0 +} + +ACTION="permission" +PARAMS="" +MASK=0 +while getopts :hlre: option +do + case "$option" in + h) show_usage;; + l) ACTION="list"; ((MASK |= 1));; + r) PARAMS="$PARAMS --ez request true"; ((MASK |= 2));; + e) ACTION="open"; export TERMUX_CALLBACK="$OPTARG"; ((MASK |= 2));; + ?) echo "$SCRIPTNAME: illegal option -$OPTARG"; exit 1; + esac +done +shift $(($OPTIND-1)) + +if [ $MASK -eq 3 ]; then echo "$SCRIPTNAME: -l cannot be combined with other options"; exit 1; fi + +if [ "$ACTION" == "list" ] +then + if [ $# -gt 0 ]; then echo "$SCRIPTNAME: too many arguments"; exit 1; fi +else + if [ $# -gt 1 ]; then echo "$SCRIPTNAME: too many arguments"; exit 1; fi + if [ $# -lt 1 ]; then echo "$SCRIPTNAME: missing -l or device path"; exit 1; fi + PARAMS="$PARAMS --es device $1" +fi + +CMD="/data/data/com.termux/files/usr/libexec/termux-api Usb -a $ACTION $PARAMS" + +if [ "$ACTION" == "permission" ] +then + if [ "$($CMD)" == "yes" ] + then + echo "Access granted." + exit 0 + else + echo "Access denied." + exit 1 + fi +else + $CMD +fi + diff --git a/termux-api.c b/termux-api.c index 115c450..de4125b 100644 --- a/termux-api.c +++ b/termux-api.c @@ -66,6 +66,15 @@ _Noreturn void exec_am_broadcast(int argc, char** argv, char* input_address_stri exit(1); } +_Noreturn void exec_callback(int fd) +{ + char *fds; + if(asprintf(&fds, "%d", fd) == -1) { perror("asprintf"); } + execl("/data/data/com.termux/files/usr/libexec/termux-callback", "termux-callback", fds, NULL); + perror("execl(\"/data/data/com.termux/files/usr/libexec/termux-callback\")"); + exit(1); +} + void generate_uuid(char* str) { sprintf(str, "%x%x-%x-%x-%x-%x%x%x", arc4random(), arc4random(), // Generates a 64-bit Hex number @@ -93,13 +102,32 @@ void* transmit_stdin_to_socket(void* arg) { } // Main thread function which reads from input socket and writes to stdout. -void transmit_socket_to_stdout(int input_socket_fd) { +int transmit_socket_to_stdout(int input_socket_fd) { ssize_t len; char buffer[1024]; - while ((len = read(input_socket_fd, &buffer, sizeof(buffer))) > 0) { + char cbuf[256]; + struct iovec io = { .iov_base = buffer, .iov_len = sizeof(buffer) }; + struct msghdr msg = { 0 }; + int fd = -1; // An optional file descriptor received through the socket + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + while ((len = recvmsg(input_socket_fd, &msg, 0)) > 0) { + struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmsg->cmsg_type == SCM_RIGHTS) { + fd = *((int *) CMSG_DATA(cmsg)); + } + } + // A file descriptor must be accompanied by a non-empty message, + // so we use "@" when we don't want any output. + if (fd != -1 && len == 1 && buffer[0] == '@') { len = 0; } write(STDOUT_FILENO, buffer, len); + msg.msg_controllen = sizeof(cbuf); } - if (len < 0) perror("read()"); + if (len < 0) perror("recvmsg()"); + return fd; } int main(int argc, char** argv) { @@ -149,7 +177,9 @@ int main(int argc, char** argv) { pthread_t transmit_thread; pthread_create(&transmit_thread, NULL, transmit_stdin_to_socket, &output_server_socket); - transmit_socket_to_stdout(input_client_socket); + int fd = transmit_socket_to_stdout(input_client_socket); + close(input_client_socket); + if (fd != -1) { exec_callback(fd); } return 0; } diff --git a/termux-callback b/termux-callback new file mode 100755 index 0000000..86c1a77 --- /dev/null +++ b/termux-callback @@ -0,0 +1,4 @@ +#!/data/data/com.termux/files/usr/bin/bash +set -e -u +$TERMUX_CALLBACK "$@" +