Implement DeProcess on fdio_spawn_etc

Launchpad has been replaced.

TEST: vim2:go/magma-tps#C0

Change-Id: Ic8a05b149e2383a3cf83825b3c29dc7c735d937c
diff --git a/framework/delibs/deutil/CMakeLists.txt b/framework/delibs/deutil/CMakeLists.txt
index 2850ed8..53d29af 100644
--- a/framework/delibs/deutil/CMakeLists.txt
+++ b/framework/delibs/deutil/CMakeLists.txt
@@ -21,9 +21,8 @@
 
 if (DE_OS_IS_FUCHSIA)
 	set(DEUTIL_SRCS ${DEUTIL_SRCS} deFuchsiaProcess.cc)
-	include_directories("${FUCHSIA_ROOT}/zircon/system/ulib/launchpad/include")
 	include_directories("${FUCHSIA_ROOT}/zircon/system/ulib/fdio/include")
-	set(DEUTIL_LIBS ${DEUTIL_LIBS} ${FUCHSIA_LIB_DIR}/liblaunchpad.so ${FUCHSIA_LIB_DIR}/libfdio.so zircon)
+	set(DEUTIL_LIBS ${DEUTIL_LIBS} ${FUCHSIA_LIB_DIR}/libfdio.so zircon)
 else ()
 	set(DEUTIL_SRCS ${DEUTIL_SRCS} deProcess.c)
 endif ()
diff --git a/framework/delibs/deutil/deCommandLine.c b/framework/delibs/deutil/deCommandLine.c
index d0ebb1c..0c0bf96 100644
--- a/framework/delibs/deutil/deCommandLine.c
+++ b/framework/delibs/deutil/deCommandLine.c
@@ -118,7 +118,7 @@
 	{
 		deCommandLine* cmdLine = (deCommandLine*)deCalloc(sizeof(deCommandLine));
 
-		if (!cmdLine || !(cmdLine->args = (char**)deCalloc(sizeof(char*)*(size_t)CharPtrArray_getNumElements(args))))
+		if (!cmdLine || !(cmdLine->args = (char**)deCalloc(sizeof(char*)*(1 + (size_t)CharPtrArray_getNumElements(args)))))
 		{
 			deFree(cmdLine);
 			deFree(buf);
@@ -131,6 +131,7 @@
 
 		for (argNdx = 0; argNdx < cmdLine->numArgs; argNdx++)
 			cmdLine->args[argNdx] = CharPtrArray_get(args, argNdx);
+		cmdLine->args[cmdLine->numArgs] = 0;
 
 		deMemPool_destroy(tmpPool);
 		return cmdLine;
diff --git a/framework/delibs/deutil/deFuchsiaProcess.cc b/framework/delibs/deutil/deFuchsiaProcess.cc
index 0498a87..56508bd 100644
--- a/framework/delibs/deutil/deFuchsiaProcess.cc
+++ b/framework/delibs/deutil/deFuchsiaProcess.cc
@@ -3,17 +3,18 @@
 // found in the LICENSE file.
 
 #include <assert.h>
-#include <launchpad/launchpad.h>
-#include <launchpad/vmo.h>
+#include <lib/fdio/io.h>
+#include <lib/fdio/spawn.h>
+#include <lib/fdio/util.h>
+#include <memory.h>
+#include <memory>
+#include <string>
+#include <unistd.h>
+#include <vector>
 #include <zircon/process.h>
 #include <zircon/processargs.h>
 #include <zircon/syscalls.h>
 #include <zircon/syscalls/object.h>
-#include <memory>
-#include <lib/fdio/io.h>
-#include <lib/fdio/util.h>
-#include <string>
-#include <unistd.h>
 
 #include "deProcess.h"
 
@@ -26,30 +27,59 @@
 struct deProcess_s {
 };
 
+class ModifiedEnviron {
+public:
+    explicit ModifiedEnviron(char** env)
+    {
+        for (size_t i = 0; env[i]; i++) {
+            envs_.push_back(env[i]);
+        }
+    }
+
+    void ReplacePwd(const char* working_directory)
+    {
+        assert(working_directory);
+        char pwd_string[strlen(working_directory) + 4 + 1];
+
+        sprintf(pwd_string, "PWD=%s", working_directory);
+        for (size_t i = 0; i < envs_.size(); i++) {
+            if (strstr(envs_[i].c_str(), "PWD=") == 0) {
+                envs_[i] = pwd_string;
+                return;
+            }
+        }
+        envs_.push_back(pwd_string);
+    }
+
+    // Return value must not outlive ModifiedEnviron. Invalidated if ReplacePwd
+    // is called.
+    std::vector<const char*> GetEnviron()
+    {
+        std::vector<const char*> env;
+        for (size_t i = 0; i < envs_.size(); i++) {
+            env.push_back(envs_[i].c_str());
+        }
+        env.push_back(nullptr);
+        return env;
+    }
+
+private:
+    std::vector<std::string> envs_;
+};
+
 class DeProcess : public deProcess_s {
 public:
     static std::unique_ptr<DeProcess> Create()
     {
-      zx_handle_t job_copy = ZX_HANDLE_INVALID;
-      zx_handle_duplicate(zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &job_copy);
-
-      launchpad_t *lp;
-      zx_status_t status = launchpad_create(job_copy, "DeProcess", &lp);
-      if (status != ZX_OK)
-        return std::unique_ptr<DeProcess>();
-
-      return std::unique_ptr<DeProcess>(new DeProcess(lp));
+        return std::unique_ptr<DeProcess>(new DeProcess());
     }
 
-    DeProcess(launchpad_t* lp)
-        : lp_(lp), proc_(ZX_HANDLE_INVALID), fstdin_(nullptr), fstdout_(nullptr), fstderr_(nullptr)
+    DeProcess() : proc_(ZX_HANDLE_INVALID), fstdin_(nullptr), fstdout_(nullptr), fstderr_(nullptr)
     {
     }
 
     ~DeProcess()
     {
-        if (lp_)
-            launchpad_destroy(lp_);
         if (proc_ != ZX_HANDLE_INVALID)
             zx_handle_close(proc_);
         closeStdIn();
@@ -57,32 +87,61 @@
         closeStdErr();
     }
 
-    launchpad_t* launchpad() { return lp_; }
-
-    bool start()
+    bool start(const char* commandLine, const char* workingDirectory)
     {
-        zx_status_t status;
-        status = launchpad_add_pipe(launchpad(), &remote_stdin_fd_, STDIN_FILENO);
-        assert(status == ZX_OK);
+        deCommandLine* cmdLine = deCommandLine_parse(commandLine);
+        assert(cmdLine->args[cmdLine->numArgs] == 0);
+        fdio_spawn_action_t spawn_actions[3];
+        int result;
+        int stdin_pipes[2];
+        result = pipe(stdin_pipes);
+        assert(result == 0);
+        int stdout_pipes[2];
+        result = pipe(stdout_pipes);
+        assert(result == 0);
+        int stderr_pipes[2];
+        result = pipe(stderr_pipes);
+        assert(result == 0);
 
-        int stdout_fd;
-        status = launchpad_add_pipe(launchpad(), &stdout_fd, STDOUT_FILENO);
-        assert(status == ZX_OK);
+        spawn_actions[0].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
+        spawn_actions[0].fd.local_fd = stdin_pipes[0];
+        spawn_actions[0].fd.target_fd = STDIN_FILENO;
+        fstdin_ = deFile_createFromHandle(stdin_pipes[1]);
 
-        int stderr_fd;
-        status = launchpad_add_pipe(launchpad(), &stderr_fd, STDERR_FILENO);
-        assert(status == ZX_OK);
+        spawn_actions[1].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
+        spawn_actions[1].fd.local_fd = stdout_pipes[1];
+        spawn_actions[1].fd.target_fd = STDOUT_FILENO;
+        fstdout_ = deFile_createFromHandle(stdout_pipes[0]);
 
-        fstdin_ = deFile_createFromHandle(remote_stdin_fd_);
-        fstdout_ = deFile_createFromHandle(stdout_fd);
-        fstderr_ = deFile_createFromHandle(stderr_fd);
+#ifndef RAW_STDERR
+        spawn_actions[2].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
+        spawn_actions[2].fd.local_fd = stderr_pipes[1];
+        spawn_actions[2].fd.target_fd = STDERR_FILENO;
+        fstderr_ = deFile_createFromHandle(stderr_pipes[0]);
+#else
+        // Printing to our stderr sometimes works better.
+        spawn_actions[2].action = FDIO_SPAWN_ACTION_CLONE_FD;
+        spawn_actions[2].fd.local_fd = STDERR_FILENO;
+        spawn_actions[2].fd.target_fd = STDERR_FILENO;
+        fstderr_ = deFile_createFromHandle(stderr_pipes[0]);
+#endif
 
-        const char* errmsg = NULL;
-        status = launchpad_go(lp_, &proc_, &errmsg);
-        lp_ = nullptr;
+        ModifiedEnviron env(environ);
+        if (workingDirectory)
+            env.ReplacePwd(workingDirectory);
 
-        if (status != ZX_OK)
+        char err_msg_out[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
+
+        uint32_t spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_ENVIRON |
+                               FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_JOB;
+        zx_status_t status = fdio_spawn_etc(
+            ZX_HANDLE_INVALID, spawn_flags, cmdLine->args[0], (const char* const*)&cmdLine->args[0],
+            env.GetEnviron().data(), 3, spawn_actions, &proc_, err_msg_out);
+
+        if (status != ZX_OK) {
+            fprintf(stderr, "fdio_spawn error: %d %s\n", status, err_msg_out);
             return false;
+        }
 
         return true;
     }
@@ -143,9 +202,7 @@
     }
 
 private:
-    launchpad_t* lp_;
     zx_handle_t proc_;
-    int remote_stdin_fd_;
     deFile* fstdin_;
     deFile* fstdout_;
     deFile* fstderr_;
@@ -168,26 +225,7 @@
 
     DeProcess* process = DeProcess::cast(de_process);
 
-    deCommandLine* cmdLine = deCommandLine_parse(commandLine);
-
-    zx_status_t status;
-    status = launchpad_set_args(process->launchpad(), cmdLine->numArgs - 1,
-                                (const char *const *)&cmdLine->args[1]);
-    if (status != ZX_OK)
-      fprintf(stderr, "launcpad_set_args failed: %d\n", status);
-    assert(status == ZX_OK);
-
-    status = launchpad_clone(process->launchpad(), LP_CLONE_FDIO_NAMESPACE | LP_CLONE_ENVIRON);
-    assert(status == ZX_OK);
-    if (status != ZX_OK)
-      fprintf(stderr, "launchpad_clone failed: %d\n", status);
-
-    status = launchpad_load_from_file(process->launchpad(), cmdLine->args[0]);
-    if (status != ZX_OK)
-      fprintf(stderr, "launchpad_load_from_file failed: %d\n", status);
-    assert(status == ZX_OK);
-
-    if (!process->start())
+    if (!process->start(commandLine, workingDirectory))
         return false;
 
     return true;