diff --git a/app/src/command.c b/app/src/command.c index 2bbea4ba..2f5df823 100644 --- a/app/src/command.c +++ b/app/src/command.c @@ -18,8 +18,7 @@ static inline const char *get_adb_command() { return adb_command; } -process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) { - const char *cmd[len + 4]; +static void fill_cmd(const char *cmd[], const char *serial, const char *const adb_cmd[], int len) { int i; cmd[0] = get_adb_command(); if (serial) { @@ -32,9 +31,21 @@ process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) memcpy(&cmd[i], adb_cmd, len * sizeof(const char *)); cmd[len + i] = NULL; +} + +process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) { + const char *cmd[len + 4]; + fill_cmd(cmd, serial, adb_cmd, len); return cmd_execute(cmd[0], cmd); } +process_t adb_execute_redirect(const char *serial, const char *const adb_cmd[], int len, + pipe_t *pipe_stdin, pipe_t *pipe_stdout, pipe_t *pipe_stderr) { + const char *cmd[len + 4]; + fill_cmd(cmd, serial, adb_cmd, len); + return cmd_execute_redirect(cmd[0], cmd, pipe_stdin, pipe_stdout, pipe_stderr); +} + process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name) { char local[4 + 5 + 1]; // tcp:PORT char remote[108 + 14 + 1]; // localabstract:NAME @@ -77,6 +88,40 @@ process_t adb_remove_path(const char *serial, const char *path) { return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); } +static int adb_execute_get_output(const char *serial, const char *const adb_cmd[], int adb_cmd_len, + char *data, size_t data_len, const char *name) { + pipe_t pipe_stdout; + process_t proc = adb_execute_redirect(serial, adb_cmd, adb_cmd_len, NULL, &pipe_stdout, NULL); + if (!process_check_success(proc, name)) { + return -1; + } + int r = read_pipe(pipe_stdout, data, data_len); + close_pipe(pipe_stdout); + return r; +} + +static int truncate_first_line(char *data, int len) { + data[len - 1] = '\0'; + char *eol = strpbrk(data, "\r\n"); + if (eol) { + *eol = '\0'; + len = eol - data; + } + return len; +} + +int adb_read_serialno(const char *serial, char *data, size_t len) { + const char *const adb_cmd[] = {"get-serialno"}; + int r = adb_execute_get_output(serial, adb_cmd, ARRAY_LEN(adb_cmd), data, len, "get-serialno"); + return r <= 0 ? r : truncate_first_line(data, r); +} + +int adb_read_model(const char *serial, char *data, size_t len) { + const char *const adb_cmd[] = {"shell", "getprop", "ro.product.model"}; + int r = adb_execute_get_output(serial, adb_cmd, ARRAY_LEN(adb_cmd), data, len, "getprop model"); + return r <= 0 ? r : truncate_first_line(data, r); +} + SDL_bool process_check_success(process_t proc, const char *name) { if (proc == PROCESS_NONE) { LOGE("Could not execute \"%s\"", name); diff --git a/app/src/command.h b/app/src/command.h index 24ddd21d..a824206d 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -24,19 +24,26 @@ # define PROCESS_NONE NULL typedef HANDLE process_t; typedef DWORD exit_code_t; + typedef HANDLE pipe_t; #else # include # define PROCESS_NONE -1 typedef pid_t process_t; typedef int exit_code_t; + typedef int pipe_t; #endif # define NO_EXIT_CODE -1 process_t cmd_execute(const char *path, const char *const argv[]); +process_t cmd_execute_redirect(const char *path, const char *const argv[], + pipe_t *pipe_stdin, pipe_t *pipe_stdout, pipe_t *pipe_stderr); SDL_bool cmd_terminate(process_t pid); SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code); process_t adb_execute(const char *serial, const char *const adb_cmd[], int len); +process_t adb_execute_redirect(const char *serial, const char *const adb_cmd[], int len, + pipe_t *pipe_stdin, pipe_t *pipe_stdout, pipe_t *pipe_stderr); + process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name); process_t adb_forward_remove(const char *serial, uint16_t local_port); process_t adb_reverse(const char *serial, const char *device_socket_name, uint16_t local_port); @@ -44,8 +51,15 @@ process_t adb_reverse_remove(const char *serial, const char *device_socket_name) process_t adb_push(const char *serial, const char *local, const char *remote); process_t adb_remove_path(const char *serial, const char *path); +// return number of bytes read (-1 on error) +int adb_read_serialno(const char *serial, char *data, size_t len); +int adb_read_model(const char *serial, char *data, size_t len); + // convenience function to wait for a successful process execution // automatically log process errors with the provided process name SDL_bool process_check_success(process_t process, const char *name); +int read_pipe(pipe_t pipe, char *data, size_t len); +void close_pipe(pipe_t pipe); + #endif diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 89295e57..f1b40c5e 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -19,6 +19,93 @@ pid_t cmd_execute(const char *path, const char *const argv[]) { return pid; } +pid_t cmd_execute_redirect(const char *path, const char *const argv[], + int *pipe_stdin, int *pipe_stdout, int *pipe_stderr) { + int in[2]; + int out[2]; + int err[2]; + if (pipe_stdin) { + if (pipe(in) == -1) { + perror("pipe"); + return -1; + } + } + if (pipe_stdout) { + if (pipe(out) == -1) { + perror("pipe"); + // clean up + if (pipe_stdin) { + close(in[0]); + close(in[1]); + } + return -1; + } + } + if (pipe_stderr) { + if (pipe(err) == -1) { + perror("pipe"); + // clean up + if (pipe_stdout) { + close(out[0]); + close(out[1]); + } + if (pipe_stdin) { + close(in[0]); + close(in[1]); + } + return -1; + } + } + + pid_t pid = fork(); + if (pid == -1) { + perror("fork"); + return -1; + } + + if (pid == 0) { + if (pipe_stdin) { + if (in[0] != STDIN_FILENO) { + dup2(in[0], STDIN_FILENO); + close(in[0]); + } + close(in[1]); + } + if (pipe_stdout) { + if (out[1] != STDOUT_FILENO) { + dup2(out[1], STDOUT_FILENO); + close(out[1]); + } + close(out[0]); + } + if (pipe_stderr) { + if (err[1] != STDERR_FILENO) { + dup2(err[1], STDERR_FILENO); + close(err[1]); + } + close(err[0]); + } + execvp(path, (char *const *)argv); + perror("exec"); + _exit(1); + } + + if (pipe_stdin) { + close(in[0]); + *pipe_stdin = in[1]; + } + if (pipe_stdout) { + *pipe_stdout = out[0]; + close(out[1]); + } + if (pipe_stderr) { + *pipe_stderr = err[0]; + close(err[1]); + } + + return pid; +} + SDL_bool cmd_terminate(pid_t pid) { return kill(pid, SIGTERM) != -1; } @@ -37,3 +124,13 @@ SDL_bool cmd_simple_wait(pid_t pid, int *exit_code) { } return !code; } + +int read_pipe(int pipe, char *data, size_t len) { + return read(pipe, data, len); +} + +void close_pipe(int pipe) { + if (close(pipe)) { + perror("close pipe"); + } +} diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 719d985d..58bb0aa9 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -3,20 +3,27 @@ #include "log.h" #include "strutil.h" +static int build_cmd(char *cmd, size_t len, const char *const argv[]) { + // Windows command-line parsing is WTF: + // + // only make it work for this very specific program + // (don't handle escaping nor quotes) + size_t ret = xstrjoin(cmd, argv, ' ', len); + if (ret >= len) { + LOGE("Command too long (%" PRIsizet " chars)", len - 1); + return -1; + } + return 0; +} + HANDLE cmd_execute(const char *path, const char *const argv[]) { STARTUPINFO si; PROCESS_INFORMATION pi; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); - // Windows command-line parsing is WTF: - // - // only make it work for this very specific program - // (don't handle escaping nor quotes) char cmd[256]; - size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd)); - if (ret >= sizeof(cmd)) { - LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1); + if (build_cmd(cmd, sizeof(cmd), argv)) { return NULL; } @@ -27,6 +34,75 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) { return pi.hProcess; } +HANDLE cmd_execute_redirect(const char *path, const char *const argv[], + HANDLE *pipe_stdin, HANDLE *pipe_stdout, HANDLE *pipe_stderr) { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + HANDLE stdin_read_handle; + HANDLE stdout_write_handle; + HANDLE stderr_write_handle; + if (pipe_stdin) { + if (!CreatePipe(&stdin_read_handle, pipe_stdin, &sa, 0)) { + perror("pipe"); + return NULL; + } + } + if (pipe_stdout) { + if (!CreatePipe(pipe_stdout, &stdout_write_handle, &sa, 0)) { + perror("pipe"); + // clean up + if (pipe_stdin) { + CloseHandle(&stdin_read_handle); + CloseHandle(pipe_stdin); + } + return NULL; + } + } + if (pipe_stderr) { + if (!CreatePipe(pipe_stderr, &stderr_write_handle, &sa, 0)) { + perror("pipe"); + // clean up + if (pipe_stdin) { + CloseHandle(&stdin_read_handle); + CloseHandle(pipe_stdin); + } + if (pipe_stdout) { + CloseHandle(pipe_stdout); + CloseHandle(&stdout_write_handle); + } + } + } + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + if (pipe_stdin) { + si.hStdInput = stdin_read_handle; + } + if (pipe_stdout) { + si.hStdOutput = stdout_write_handle; + } + if (pipe_stderr) { + si.hStdError = stderr_write_handle; + } + + char cmd[256]; + if (build_cmd(cmd, sizeof(cmd), argv)) { + return NULL; + } + + if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + return NULL; + } + + return pi.hProcess; +} + SDL_bool cmd_terminate(HANDLE handle) { return TerminateProcess(handle, 1) && CloseHandle(handle); } @@ -42,3 +118,17 @@ SDL_bool cmd_simple_wait(HANDLE handle, DWORD *exit_code) { } return !code; } + +int read_pipe(HANDLE pipe, char *data, size_t len) { + DWORD r; + if (!ReadFile(pipe, data, len, &r, NULL)) { + return -1; + } + return r; +} + +void close_pipe(HANDLE pipe) { + if (!CloseHandle(pipe)) { + LOGW("Cannot close pipe"); + } +}