Skip to content

Commit

Permalink
Changed: Try to connect over a unix socket to the plugin before falli…
Browse files Browse the repository at this point in the history
…ng back to am.
  • Loading branch information
tareksander committed Dec 1, 2021
1 parent 3154dfd commit 790e290
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ set(TERMUX_PREFIX ${CMAKE_INSTALL_PREFIX})

# TODO: get list through regex or similar
set(script_files
scripts/termux-api-start
scripts/termux-api-stop
scripts/termux-audio-info
scripts/termux-battery-status
scripts/termux-brightness
Expand Down
2 changes: 2 additions & 0 deletions scripts/termux-api-start.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!@TERMUX_PREFIX@/bin/sh
am startservice -n com.termux.api/.KeepAliveService
2 changes: 2 additions & 0 deletions scripts/termux-api-stop.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!@TERMUX_PREFIX@/bin/sh
am stopservice -n com.termux.api/.KeepAliveService
204 changes: 197 additions & 7 deletions termux-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PREFIX "/data/data/com.termux/files/usr"
#define LISTEN_SOCKET_ADDRESS "com.termux.api://listen"


// Function which execs "am broadcast ..".
_Noreturn void exec_am_broadcast(int argc, char** argv,
char* input_address_string,
char* output_address_string)
{
// Redirect stdout to /dev/null (but leave stderr open):
close(STDOUT_FILENO);
open("/dev/null", O_RDONLY);
// Close stdin:
close(STDIN_FILENO);

int const extra_args = 15; // Including ending NULL.
char** child_argv = malloc((sizeof(char*)) * (argc + extra_args));

Expand Down Expand Up @@ -71,6 +70,197 @@ _Noreturn void exec_am_broadcast(int argc, char** argv,
exit(1);
}


// passes the arguments to the plugin via the unix socket, falling back to exec_am_broadcast() if that doesn't work
_Noreturn void contact_pugin(int argc, char** argv,
char* input_address_string,
char* output_address_string)
{
// Redirect stdout to /dev/null (but leave stderr open):
close(STDOUT_FILENO);
open("/dev/null", O_RDONLY);
// Close stdin:
close(STDIN_FILENO);

// ignore SIGPIPE, so am will be called when the connection is closed unexpectedly
struct sigaction sigpipe_action = {
.sa_handler = SIG_IGN,
.sa_flags = 0
};
sigaction(SIGPIPE, &sigpipe_action, NULL);

// try to connect over the listen socket first
int listenfd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (listenfd != -1) {
struct sockaddr_un listen_addr = { .sun_family = AF_UNIX };
memcpy(listen_addr.sun_path+1, LISTEN_SOCKET_ADDRESS, strlen(LISTEN_SOCKET_ADDRESS));
if (connect(listenfd, (struct sockaddr*) &listen_addr, sizeof(sa_family_t) + strlen(LISTEN_SOCKET_ADDRESS) + 1) == 0) {
socklen_t optlen = sizeof(struct ucred);
// check the uid to see if the socket is actually provided by the plugin
struct ucred cred;
if (getsockopt(listenfd, SOL_SOCKET, SO_PEERCRED, &cred, &optlen) == 0 && cred.uid == getuid()) {

const char insock_str[] = "--es socket_input \"";
const char outsock_str[] = "--es socket_output \"";
const char method_str[] = "--es api_method \"";

int len = sizeof(insock_str)-1+strlen(output_address_string)+2+sizeof(outsock_str)-1+strlen(input_address_string)+2+sizeof(method_str)-1+strlen(argv[1])+2;
for (int i = 2; i<argc; i++) {
len += strlen(argv[i])+1;
if (strcmp(argv[i], "--es") == 0 || strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--esa") == 0) {
len += 2; // the string extra has to be enclosed in "
}
for (int a = 0; a<strlen(argv[i]); a++) {
if (argv[i][a] == '"') {
len += 1; // " has to be escaped, so one character more.
// This assumes " is only present in string extra arguments, but that is probably an acceptable assumption to make
}
}
}

char* buffer = malloc(len);

int offset = 0;
memcpy(buffer+offset, insock_str, sizeof(insock_str)-1);
offset += sizeof(insock_str)-1;

memcpy(buffer+offset, output_address_string, strlen(output_address_string));
offset += strlen(output_address_string);

buffer[offset] = '"';
offset++;
buffer[offset] = ' ';
offset++;


memcpy(buffer+offset, outsock_str, sizeof(outsock_str)-1);
offset += sizeof(outsock_str)-1;


memcpy(buffer+offset, input_address_string, strlen(input_address_string));
offset += strlen(input_address_string);

buffer[offset] = '"';
offset++;
buffer[offset] = ' ';
offset++;

memcpy(buffer+offset, method_str, sizeof(method_str)-1);
offset += sizeof(method_str)-1;


memcpy(buffer+offset, argv[1], strlen(argv[1]));
offset += strlen(argv[1]);

buffer[offset] = '"';
offset++;
buffer[offset] = ' ';
offset++;

for (int i = 2; i<argc; i++) {
if (strcmp(argv[i], "--es") == 0 || strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--esa") == 0) {
memcpy(buffer+offset, argv[i], strlen(argv[i]));
offset += strlen(argv[i]);
buffer[offset] = ' ';
offset++;
i++;
if (i < argc) {
memcpy(buffer+offset, argv[i], strlen(argv[i]));
offset += strlen(argv[i]);
buffer[offset] = ' ';
offset++;
}
i++;
if (i < argc) {
buffer[offset] = '"';
offset++;
for (int a = 0; a<strlen(argv[i]); a++) {
if (argv[i][a] == '"') {
buffer[offset] = '\\';
offset++;
buffer[offset] = '"';
offset++;
} else {
buffer[offset] = argv[i][a];
offset++;
}
}
buffer[offset] = '"';
offset++;
buffer[offset] = ' ';
offset++;
}
} else {
memcpy(buffer+offset, argv[i], strlen(argv[i]));
offset += strlen(argv[i]);
buffer[offset] = ' ';
offset++;
}
}


int netlen = htons(len);

bool err = false;
// transmit the size
int totransmit = 2;
void* transmit = &netlen;
while (totransmit > 0) {
int ret = send(listenfd, transmit, totransmit, 0);
if (ret == -1) {
err = true;
break;
}
totransmit -= ret;
}

// transmit the argument list
if (! err) {
totransmit = len;
transmit = buffer;
while (totransmit > 0) {
int ret = send(listenfd, transmit, totransmit, 0);
if (ret == -1) {
err = true;
break;
}
totransmit -= ret;
}
}


if (! err) {
char readbuffer[100];
int ret;
bool first = true;
err = true;
while ((ret = read(listenfd, readbuffer, 99)) > 0) {
// if a single null byte is received as the first message, the call was successfull
if (ret == 1 && readbuffer[0] == 0 && first) {
err = false;
break;
}
// otherwise it's an error message
readbuffer[ret] = '\0';
// printing out the error is good for debug purposes, but feel free to disable this
fprintf(stderr, "%s", readbuffer);
fflush(stderr);
first = false;
}
}

// if everything went well, there is no need to call am
if (! err) {
exit(0);
}
}
}
}

exec_am_broadcast(argc, argv, input_address_string, output_address_string);
}


_Noreturn void exec_callback(int fd)
{
char *fds;
Expand Down Expand Up @@ -202,7 +392,7 @@ int main(int argc, char** argv) {
pid_t fork_result = fork();
switch (fork_result) {
case -1: perror("fork()"); return 1;
case 0: exec_am_broadcast(argc, argv, input_address_string,
case 0: contact_pugin(argc, argv, input_address_string,
output_address_string);
}

Expand Down

0 comments on commit 790e290

Please sign in to comment.