Skip to content

Commit e19c3ca

Browse files
committed
Add a USB API compatible with libusb
1 parent 82ec2e3 commit e19c3ca

4 files changed

Lines changed: 96 additions & 4 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ termux-api: termux-api.c
66
install: termux-api
77
mkdir -p $(PREFIX)/bin/ $(PREFIX)/libexec/
88
install termux-api $(PREFIX)/libexec/
9+
install termux-callback $(PREFIX)/libexec/
910
install scripts/* $(PREFIX)/bin/
1011

1112
.PHONY: install

scripts/termux-usb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/data/data/com.termux/files/usr/bin/bash
2+
set -e -u
3+
4+
SCRIPTNAME=termux-usb
5+
show_usage () {
6+
echo "Usage: $SCRIPTNAME [-l | [-r] [-e command] device]"
7+
echo "List or access USB devices. Devices cannot be accessed directly,"
8+
echo " only using $SCRIPTNAME."
9+
echo " -l list available devices"
10+
echo " -r show permission request dialog if necessary"
11+
echo " -e command execute the specified command with a file descriptor"
12+
echo " referring to the device as its argument"
13+
exit 0
14+
}
15+
16+
ACTION="permission"
17+
PARAMS=""
18+
MASK=0
19+
while getopts :hlre: option
20+
do
21+
case "$option" in
22+
h) show_usage;;
23+
l) ACTION="list"; ((MASK |= 1));;
24+
r) PARAMS="$PARAMS --ez request true"; ((MASK |= 2));;
25+
e) ACTION="open"; export TERMUX_CALLBACK="$OPTARG"; ((MASK |= 2));;
26+
?) echo "$SCRIPTNAME: illegal option -$OPTARG"; exit 1;
27+
esac
28+
done
29+
shift $(($OPTIND-1))
30+
31+
if [ $MASK -eq 3 ]; then echo "$SCRIPTNAME: -l cannot be combined with other options"; exit 1; fi
32+
33+
if [ "$ACTION" == "list" ]
34+
then
35+
if [ $# -gt 0 ]; then echo "$SCRIPTNAME: too many arguments"; exit 1; fi
36+
else
37+
if [ $# -gt 1 ]; then echo "$SCRIPTNAME: too many arguments"; exit 1; fi
38+
if [ $# -lt 1 ]; then echo "$SCRIPTNAME: missing -l or device path"; exit 1; fi
39+
PARAMS="$PARAMS --es device $1"
40+
fi
41+
42+
CMD="/data/data/com.termux/files/usr/libexec/termux-api Usb -a $ACTION $PARAMS"
43+
44+
if [ "$ACTION" == "permission" ]
45+
then
46+
if [ "$($CMD)" == "yes" ]
47+
then
48+
echo "Access granted."
49+
exit 0
50+
else
51+
echo "Access denied."
52+
exit 1
53+
fi
54+
else
55+
$CMD
56+
fi
57+

termux-api.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ _Noreturn void exec_am_broadcast(int argc, char** argv, char* input_address_stri
6666
exit(1);
6767
}
6868

69+
_Noreturn void exec_callback(int fd)
70+
{
71+
char *fds;
72+
if(asprintf(&fds, "%d", fd) == -1) { perror("asprintf"); }
73+
execl("/data/data/com.termux/files/usr/libexec/termux-callback", "termux-callback", fds, NULL);
74+
perror("execl(\"/data/data/com.termux/files/usr/libexec/termux-callback\")");
75+
exit(1);
76+
}
77+
6978
void generate_uuid(char* str) {
7079
sprintf(str, "%x%x-%x-%x-%x-%x%x%x",
7180
arc4random(), arc4random(), // Generates a 64-bit Hex number
@@ -93,13 +102,32 @@ void* transmit_stdin_to_socket(void* arg) {
93102
}
94103

95104
// Main thread function which reads from input socket and writes to stdout.
96-
void transmit_socket_to_stdout(int input_socket_fd) {
105+
int transmit_socket_to_stdout(int input_socket_fd) {
97106
ssize_t len;
98107
char buffer[1024];
99-
while ((len = read(input_socket_fd, &buffer, sizeof(buffer))) > 0) {
108+
char cbuf[256];
109+
struct iovec io = { .iov_base = buffer, .iov_len = sizeof(buffer) };
110+
struct msghdr msg = { 0 };
111+
int fd = -1; // An optional file descriptor received through the socket
112+
msg.msg_iov = &io;
113+
msg.msg_iovlen = 1;
114+
msg.msg_control = cbuf;
115+
msg.msg_controllen = sizeof(cbuf);
116+
while ((len = recvmsg(input_socket_fd, &msg, 0)) > 0) {
117+
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
118+
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
119+
if (cmsg->cmsg_type == SCM_RIGHTS) {
120+
fd = *((int *) CMSG_DATA(cmsg));
121+
}
122+
}
123+
// A file descriptor must be accompanied by a non-empty message,
124+
// so we use "@" when we don't want any output.
125+
if (fd != -1 && len == 1 && buffer[0] == '@') { len = 0; }
100126
write(STDOUT_FILENO, buffer, len);
127+
msg.msg_controllen = sizeof(cbuf);
101128
}
102-
if (len < 0) perror("read()");
129+
if (len < 0) perror("recvmsg()");
130+
return fd;
103131
}
104132

105133
int main(int argc, char** argv) {
@@ -149,7 +177,9 @@ int main(int argc, char** argv) {
149177
pthread_t transmit_thread;
150178
pthread_create(&transmit_thread, NULL, transmit_stdin_to_socket, &output_server_socket);
151179

152-
transmit_socket_to_stdout(input_client_socket);
180+
int fd = transmit_socket_to_stdout(input_client_socket);
181+
close(input_client_socket);
182+
if (fd != -1) { exec_callback(fd); }
153183

154184
return 0;
155185
}

termux-callback

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/data/data/com.termux/files/usr/bin/bash
2+
set -e -u
3+
$TERMUX_CALLBACK "$@"
4+

0 commit comments

Comments
 (0)