adb: report subprocess creation errors to the client.

Bug: http://b/26589627
Change-Id: I38e7a33ea8274f0eb47dc0ed87fcbca08a0ab22a
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 491bb68..d080e09 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -192,7 +192,7 @@
 
     // 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 @@
     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 @@
     // 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 @@
         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 @@
         } 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 @@
     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 @@
     } 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 @@
         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 @@
         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 @@
     }
 
     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 @@
 
 }  // 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 @@
     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",