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 <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");
}
}