Merge "adb: report subprocess creation errors to the client."

This commit is contained in:
Josh Gao 2016-02-03 23:53:26 +00:00 committed by Gerrit Code Review
commit 7aa7a81eb1
1 changed files with 60 additions and 13 deletions

View File

@ -192,7 +192,7 @@ class Subprocess {
// Sets up FDs, forks a subprocess, starts the subprocess manager thread,
// and exec's the child. Returns false on failure.
bool ForkAndExec();
bool ForkAndExec(std::string* _Nonnull error);
private:
// Opens the file at |pts_name|.
@ -250,7 +250,7 @@ Subprocess::~Subprocess() {
WaitForExit();
}
bool Subprocess::ForkAndExec() {
bool Subprocess::ForkAndExec(std::string* error) {
ScopedFd child_stdinout_sfd, child_stderr_sfd;
ScopedFd parent_error_sfd, child_error_sfd;
char pts_name[PATH_MAX];
@ -265,7 +265,9 @@ bool Subprocess::ForkAndExec() {
// use threads, logging directly from the child might deadlock due to locks held in another
// thread during the fork.
if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
LOG(ERROR) << "failed to create pipe for subprocess error reporting";
*error = android::base::StringPrintf(
"failed to create pipe for subprocess error reporting: %s", strerror(errno));
return false;
}
// Construct the environment for the child before we fork.
@ -316,18 +318,22 @@ bool Subprocess::ForkAndExec() {
stdinout_sfd_.Reset(fd);
} else {
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
*error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
strerror(errno));
return false;
}
// Raw subprocess + shell protocol allows for splitting stderr.
if (protocol_ == SubprocessProtocol::kShell &&
!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
*error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
strerror(errno));
return false;
}
pid_ = fork();
}
if (pid_ == -1) {
PLOG(ERROR) << "fork failed";
*error = android::base::StringPrintf("fork failed: %s", strerror(errno));
return false;
}
@ -357,7 +363,8 @@ bool Subprocess::ForkAndExec() {
} else {
execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
}
WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed");
WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed: ");
WriteFdExactly(child_error_sfd.fd(), strerror(errno));
child_error_sfd.Reset();
_Exit(1);
}
@ -370,7 +377,7 @@ bool Subprocess::ForkAndExec() {
child_error_sfd.Reset();
std::string error_message = ReadAll(parent_error_sfd.fd());
if (!error_message.empty()) {
LOG(ERROR) << error_message;
*error = error_message;
return false;
}
@ -382,6 +389,9 @@ bool Subprocess::ForkAndExec() {
} else {
// Shell protocol: create another socketpair to intercept data.
if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
*error = android::base::StringPrintf(
"failed to create socketpair to intercept data: %s", strerror(errno));
kill(pid_, SIGKILL);
return false;
}
D("protocol FD = %d", protocol_sfd_.fd());
@ -389,7 +399,8 @@ bool Subprocess::ForkAndExec() {
input_.reset(new ShellProtocol(protocol_sfd_.fd()));
output_.reset(new ShellProtocol(protocol_sfd_.fd()));
if (!input_ || !output_) {
LOG(ERROR) << "failed to allocate shell protocol objects";
*error = "failed to allocate shell protocol objects";
kill(pid_, SIGKILL);
return false;
}
@ -400,7 +411,9 @@ bool Subprocess::ForkAndExec() {
for (int fd : {stdinout_sfd_.fd(), stderr_sfd_.fd()}) {
if (fd >= 0) {
if (!set_file_block_mode(fd, false)) {
LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
*error = android::base::StringPrintf(
"failed to set non-blocking mode for fd %d", fd);
kill(pid_, SIGKILL);
return false;
}
}
@ -408,7 +421,9 @@ bool Subprocess::ForkAndExec() {
}
if (!adb_thread_create(ThreadHandler, this)) {
PLOG(ERROR) << "failed to create subprocess thread";
*error =
android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
kill(pid_, SIGKILL);
return false;
}
@ -710,6 +725,37 @@ void Subprocess::WaitForExit() {
} // namespace
// Create a pipe containing the error.
static int ReportError(SubprocessProtocol protocol, const std::string& message) {
int pipefd[2];
if (pipe(pipefd) != 0) {
LOG(ERROR) << "failed to create pipe to report error";
return -1;
}
std::string buf = android::base::StringPrintf("error: %s\n", message.c_str());
if (protocol == SubprocessProtocol::kShell) {
ShellProtocol::Id id = ShellProtocol::kIdStderr;
uint32_t length = buf.length();
WriteFdExactly(pipefd[1], &id, sizeof(id));
WriteFdExactly(pipefd[1], &length, sizeof(length));
}
WriteFdExactly(pipefd[1], buf.data(), buf.length());
if (protocol == SubprocessProtocol::kShell) {
ShellProtocol::Id id = ShellProtocol::kIdExit;
uint32_t length = 1;
char exit_code = 126;
WriteFdExactly(pipefd[1], &id, sizeof(id));
WriteFdExactly(pipefd[1], &length, sizeof(length));
WriteFdExactly(pipefd[1], &exit_code, sizeof(exit_code));
}
adb_close(pipefd[1]);
return pipefd[0];
}
int StartSubprocess(const char* name, const char* terminal_type,
SubprocessType type, SubprocessProtocol protocol) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
@ -720,13 +766,14 @@ int StartSubprocess(const char* name, const char* terminal_type,
Subprocess* subprocess = new Subprocess(name, terminal_type, type, protocol);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
return -1;
return ReportError(protocol, "failed to allocate new subprocess");
}
if (!subprocess->ForkAndExec()) {
LOG(ERROR) << "failed to start subprocess";
std::string error;
if (!subprocess->ForkAndExec(&error)) {
LOG(ERROR) << "failed to start subprocess: " << error;
delete subprocess;
return -1;
return ReportError(protocol, error);
}
D("subprocess creation successful: local_socket_fd=%d, pid=%d",