Add command execution with redirection
Expose command execution with pipes to stdin, stdout and stderr. This will allow to read the result of adb commands.
This commit is contained in:
parent
207082977a
commit
eaf4afaad9
3 changed files with 265 additions and 43 deletions
|
@ -1,5 +1,6 @@
|
|||
#include "util/process.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
@ -50,65 +51,151 @@ search_executable(const char *file) {
|
|||
}
|
||||
|
||||
enum process_result
|
||||
process_execute(const char *const argv[], pid_t *pid) {
|
||||
int fd[2];
|
||||
process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin,
|
||||
int *pipe_stdout, int *pipe_stderr) {
|
||||
int in[2];
|
||||
int out[2];
|
||||
int err[2];
|
||||
int internal[2]; // communication between parent and children
|
||||
|
||||
if (pipe(fd) == -1) {
|
||||
if (pipe(internal) == -1) {
|
||||
perror("pipe");
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
enum process_result ret = PROCESS_SUCCESS;
|
||||
if (pipe_stdin) {
|
||||
if (pipe(in) == -1) {
|
||||
perror("pipe");
|
||||
close(internal[0]);
|
||||
close(internal[1]);
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
if (pipe_stdout) {
|
||||
if (pipe(out) == -1) {
|
||||
perror("pipe");
|
||||
// clean up
|
||||
if (pipe_stdin) {
|
||||
close(in[0]);
|
||||
close(in[1]);
|
||||
}
|
||||
close(internal[0]);
|
||||
close(internal[1]);
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
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]);
|
||||
}
|
||||
close(internal[0]);
|
||||
close(internal[1]);
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
*pid = fork();
|
||||
if (*pid == -1) {
|
||||
perror("fork");
|
||||
ret = PROCESS_ERROR_GENERIC;
|
||||
goto end;
|
||||
// clean up
|
||||
if (pipe_stderr) {
|
||||
close(err[0]);
|
||||
close(err[1]);
|
||||
}
|
||||
if (pipe_stdout) {
|
||||
close(out[0]);
|
||||
close(out[1]);
|
||||
}
|
||||
if (pipe_stdin) {
|
||||
close(in[0]);
|
||||
close(in[1]);
|
||||
}
|
||||
close(internal[0]);
|
||||
close(internal[1]);
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (*pid > 0) {
|
||||
// parent close write side
|
||||
close(fd[1]);
|
||||
fd[1] = -1;
|
||||
// wait for EOF or receive errno from child
|
||||
if (read(fd[0], &ret, sizeof(ret)) == -1) {
|
||||
perror("read");
|
||||
ret = PROCESS_ERROR_GENERIC;
|
||||
goto end;
|
||||
}
|
||||
} else if (*pid == 0) {
|
||||
// child close read side
|
||||
close(fd[0]);
|
||||
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) {
|
||||
execvp(argv[0], (char *const *)argv);
|
||||
if (errno == ENOENT) {
|
||||
ret = PROCESS_ERROR_MISSING_BINARY;
|
||||
} else {
|
||||
ret = PROCESS_ERROR_GENERIC;
|
||||
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]);
|
||||
}
|
||||
close(internal[0]);
|
||||
enum process_result err;
|
||||
if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) {
|
||||
execvp(argv[0], (char *const *) argv);
|
||||
perror("exec");
|
||||
err = errno == ENOENT ? PROCESS_ERROR_MISSING_BINARY
|
||||
: PROCESS_ERROR_GENERIC;
|
||||
} else {
|
||||
perror("fcntl");
|
||||
ret = PROCESS_ERROR_GENERIC;
|
||||
err = PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
// send ret to the parent
|
||||
if (write(fd[1], &ret, sizeof(ret)) == -1) {
|
||||
// send err to the parent
|
||||
if (write(internal[1], &err, sizeof(err)) == -1) {
|
||||
perror("write");
|
||||
}
|
||||
// close write side before exiting
|
||||
close(fd[1]);
|
||||
close(internal[1]);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
end:
|
||||
if (fd[0] != -1) {
|
||||
close(fd[0]);
|
||||
// parent
|
||||
assert(*pid > 0);
|
||||
|
||||
close(internal[1]);
|
||||
|
||||
enum process_result res = PROCESS_SUCCESS;
|
||||
// wait for EOF or receive err from child
|
||||
if (read(internal[0], &res, sizeof(res)) == -1) {
|
||||
perror("read");
|
||||
res = PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
if (fd[1] != -1) {
|
||||
close(fd[1]);
|
||||
|
||||
close(internal[0]);
|
||||
|
||||
if (pipe_stdin) {
|
||||
close(in[0]);
|
||||
*pipe_stdin = in[1];
|
||||
}
|
||||
return ret;
|
||||
if (pipe_stdout) {
|
||||
*pipe_stdout = out[0];
|
||||
close(out[1]);
|
||||
}
|
||||
if (pipe_stderr) {
|
||||
*pipe_stderr = err[0];
|
||||
close(err[1]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
enum process_result
|
||||
process_execute(const char *const argv[], pid_t *pid) {
|
||||
return process_execute_redirect(argv, pid, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -175,3 +262,15 @@ is_regular_file(const char *path) {
|
|||
}
|
||||
return S_ISREG(path_stat.st_mode);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,38 +23,129 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
|||
}
|
||||
|
||||
enum process_result
|
||||
process_execute(const char *const argv[], HANDLE *handle) {
|
||||
process_execute_redirect(const char *const argv[], HANDLE *handle,
|
||||
HANDLE *pipe_stdin, HANDLE *pipe_stdout,
|
||||
HANDLE *pipe_stderr) {
|
||||
enum process_result ret = PROCESS_ERROR_GENERIC;
|
||||
|
||||
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 PROCESS_ERROR_GENERIC;
|
||||
}
|
||||
if (!SetHandleInformation(*pipe_stdin, HANDLE_FLAG_INHERIT, 0)) {
|
||||
LOGE("SetHandleInformation stdin failed");
|
||||
goto error_close_stdin;
|
||||
}
|
||||
}
|
||||
if (pipe_stdout) {
|
||||
if (!CreatePipe(pipe_stdout, &stdout_write_handle, &sa, 0)) {
|
||||
perror("pipe");
|
||||
goto error_close_stdin;
|
||||
}
|
||||
if (!SetHandleInformation(*pipe_stdout, HANDLE_FLAG_INHERIT, 0)) {
|
||||
LOGE("SetHandleInformation stdout failed");
|
||||
goto error_close_stdout;
|
||||
}
|
||||
}
|
||||
if (pipe_stderr) {
|
||||
if (!CreatePipe(pipe_stderr, &stderr_write_handle, &sa, 0)) {
|
||||
perror("pipe");
|
||||
goto error_close_stdout;
|
||||
}
|
||||
if (!SetHandleInformation(*pipe_stderr, HANDLE_FLAG_INHERIT, 0)) {
|
||||
LOGE("SetHandleInformation stderr failed");
|
||||
goto error_close_stderr;
|
||||
}
|
||||
}
|
||||
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
if (pipe_stdin || pipe_stdout || pipe_stderr) {
|
||||
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 = malloc(CMD_MAX_LEN);
|
||||
if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) {
|
||||
*handle = NULL;
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
goto error_close_stderr;
|
||||
}
|
||||
|
||||
wchar_t *wide = utf8_to_wide_char(cmd);
|
||||
free(cmd);
|
||||
if (!wide) {
|
||||
LOGC("Could not allocate wide char string");
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
goto error_close_stderr;
|
||||
}
|
||||
|
||||
if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, 0, NULL, NULL, &si,
|
||||
if (!CreateProcessW(NULL, wide, NULL, NULL, TRUE, 0, NULL, NULL, &si,
|
||||
&pi)) {
|
||||
free(wide);
|
||||
*handle = NULL;
|
||||
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
return PROCESS_ERROR_MISSING_BINARY;
|
||||
ret = PROCESS_ERROR_MISSING_BINARY;
|
||||
}
|
||||
return PROCESS_ERROR_GENERIC;
|
||||
goto error_close_stderr;
|
||||
}
|
||||
|
||||
// These handles are used by the child process, close them for this process
|
||||
if (pipe_stdin) {
|
||||
CloseHandle(stdin_read_handle);
|
||||
}
|
||||
if (pipe_stdout) {
|
||||
CloseHandle(stdout_write_handle);
|
||||
}
|
||||
if (pipe_stderr) {
|
||||
CloseHandle(stderr_write_handle);
|
||||
}
|
||||
|
||||
free(wide);
|
||||
*handle = pi.hProcess;
|
||||
|
||||
return PROCESS_SUCCESS;
|
||||
|
||||
error_close_stderr:
|
||||
if (pipe_stderr) {
|
||||
CloseHandle(*pipe_stderr);
|
||||
CloseHandle(stderr_write_handle);
|
||||
}
|
||||
error_close_stdout:
|
||||
if (pipe_stdout) {
|
||||
CloseHandle(*pipe_stdout);
|
||||
CloseHandle(stdout_write_handle);
|
||||
}
|
||||
error_close_stdin:
|
||||
if (pipe_stdin) {
|
||||
CloseHandle(*pipe_stdin);
|
||||
CloseHandle(stdin_read_handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum process_result
|
||||
process_execute(const char *const argv[], HANDLE *handle) {
|
||||
return process_execute_redirect(argv, handle, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -116,3 +207,19 @@ is_regular_file(const char *path) {
|
|||
}
|
||||
return S_ISREG(path_stat.st_mode);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
# define NO_EXIT_CODE -1u // max value as unsigned
|
||||
typedef HANDLE process_t;
|
||||
typedef DWORD exit_code_t;
|
||||
typedef HANDLE pipe_t;
|
||||
|
||||
#else
|
||||
|
||||
|
@ -29,6 +30,7 @@
|
|||
# define NO_EXIT_CODE -1
|
||||
typedef pid_t process_t;
|
||||
typedef int exit_code_t;
|
||||
typedef int pipe_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -42,6 +44,14 @@ enum process_result {
|
|||
enum process_result
|
||||
process_execute(const char *const argv[], process_t *process);
|
||||
|
||||
enum process_result
|
||||
process_execute_redirect(const char *const argv[], process_t *process,
|
||||
pipe_t *pipe_stdin, pipe_t *pipe_stdout,
|
||||
pipe_t *pipe_stderr);
|
||||
|
||||
bool
|
||||
process_terminate(process_t pid);
|
||||
|
||||
// kill the process
|
||||
bool
|
||||
process_terminate(process_t pid);
|
||||
|
@ -83,4 +93,10 @@ get_local_file_path(const char *name);
|
|||
bool
|
||||
is_regular_file(const char *path);
|
||||
|
||||
ssize_t
|
||||
read_pipe(pipe_t pipe, char *data, size_t len);
|
||||
|
||||
void
|
||||
close_pipe(pipe_t pipe);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue