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:
Romain Vimont 2020-11-14 22:12:07 +01:00
parent 207082977a
commit eaf4afaad9
3 changed files with 265 additions and 43 deletions

View file

@ -1,5 +1,6 @@
#include "util/process.h" #include "util/process.h"
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
@ -50,65 +51,151 @@ search_executable(const char *file) {
} }
enum process_result enum process_result
process_execute(const char *const argv[], pid_t *pid) { process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin,
int fd[2]; 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"); perror("pipe");
return PROCESS_ERROR_GENERIC; return PROCESS_ERROR_GENERIC;
} }
if (pipe_stdin) {
enum process_result ret = PROCESS_SUCCESS; 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(); *pid = fork();
if (*pid == -1) { if (*pid == -1) {
perror("fork"); perror("fork");
ret = PROCESS_ERROR_GENERIC; // clean up
goto end; 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) { if (*pid == 0) {
// parent close write side if (pipe_stdin) {
close(fd[1]); if (in[0] != STDIN_FILENO) {
fd[1] = -1; dup2(in[0], STDIN_FILENO);
// wait for EOF or receive errno from child close(in[0]);
if (read(fd[0], &ret, sizeof(ret)) == -1) {
perror("read");
ret = PROCESS_ERROR_GENERIC;
goto end;
} }
} else if (*pid == 0) { close(in[1]);
// child close read side }
close(fd[0]); if (pipe_stdout) {
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) { 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); execvp(argv[0], (char *const *) argv);
if (errno == ENOENT) {
ret = PROCESS_ERROR_MISSING_BINARY;
} else {
ret = PROCESS_ERROR_GENERIC;
}
perror("exec"); perror("exec");
err = errno == ENOENT ? PROCESS_ERROR_MISSING_BINARY
: PROCESS_ERROR_GENERIC;
} else { } else {
perror("fcntl"); perror("fcntl");
ret = PROCESS_ERROR_GENERIC; err = PROCESS_ERROR_GENERIC;
} }
// send ret to the parent // send err to the parent
if (write(fd[1], &ret, sizeof(ret)) == -1) { if (write(internal[1], &err, sizeof(err)) == -1) {
perror("write"); perror("write");
} }
// close write side before exiting close(internal[1]);
close(fd[1]);
_exit(1); _exit(1);
} }
end: // parent
if (fd[0] != -1) { assert(*pid > 0);
close(fd[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 bool
@ -175,3 +262,15 @@ is_regular_file(const char *path) {
} }
return S_ISREG(path_stat.st_mode); 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");
}
}

View file

@ -23,38 +23,129 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
} }
enum process_result 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; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si)); memset(&si, 0, sizeof(si));
si.cb = 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); char *cmd = malloc(CMD_MAX_LEN);
if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) { if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) {
*handle = NULL; *handle = NULL;
return PROCESS_ERROR_GENERIC; goto error_close_stderr;
} }
wchar_t *wide = utf8_to_wide_char(cmd); wchar_t *wide = utf8_to_wide_char(cmd);
free(cmd); free(cmd);
if (!wide) { if (!wide) {
LOGC("Could not allocate wide char string"); 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)) { &pi)) {
free(wide); free(wide);
*handle = NULL; *handle = NULL;
if (GetLastError() == ERROR_FILE_NOT_FOUND) { 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); free(wide);
*handle = pi.hProcess; *handle = pi.hProcess;
return PROCESS_SUCCESS; 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 bool
@ -116,3 +207,19 @@ is_regular_file(const char *path) {
} }
return S_ISREG(path_stat.st_mode); 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");
}
}

View file

@ -18,6 +18,7 @@
# define NO_EXIT_CODE -1u // max value as unsigned # define NO_EXIT_CODE -1u // max value as unsigned
typedef HANDLE process_t; typedef HANDLE process_t;
typedef DWORD exit_code_t; typedef DWORD exit_code_t;
typedef HANDLE pipe_t;
#else #else
@ -29,6 +30,7 @@
# define NO_EXIT_CODE -1 # define NO_EXIT_CODE -1
typedef pid_t process_t; typedef pid_t process_t;
typedef int exit_code_t; typedef int exit_code_t;
typedef int pipe_t;
#endif #endif
@ -42,6 +44,14 @@ enum process_result {
enum process_result enum process_result
process_execute(const char *const argv[], process_t *process); 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 // kill the process
bool bool
process_terminate(process_t pid); process_terminate(process_t pid);
@ -83,4 +93,10 @@ get_local_file_path(const char *name);
bool bool
is_regular_file(const char *path); 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 #endif