posix: Make DoubleForkAndExec accept an envp parameter

This will be useful to allow setting variables such as CLASSPATH or
LD_LIBRARY_PATH without modifying or depending upon the application's
current environment.

Bug: crashpad:30
Change-Id: I34f31bcc397e51d789b48eb654d80f992a719074
Reviewed-on: https://chromium-review.googlesource.com/1194399
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc
index 6a7c638..ec5396f 100644
--- a/client/crashpad_client_linux.cc
+++ b/client/crashpad_client_linux.cc
@@ -201,7 +201,7 @@
 
   argv.push_back(FormatArgumentInt("initial-client-fd", socket));
 
-  return DoubleForkAndExec(argv, socket, true, nullptr);
+  return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
 }
 
 // static
diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc
index 4a40820..22bd538 100644
--- a/client/crashpad_client_mac.cc
+++ b/client/crashpad_client_mac.cc
@@ -338,6 +338,7 @@
     // this interface.
     if (!DoubleForkAndExec(
             argv,
+            nullptr,
             server_write_fd.get(),
             true,
             restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {
diff --git a/util/posix/double_fork_and_exec.cc b/util/posix/double_fork_and_exec.cc
index df74f70..e4ad998 100644
--- a/util/posix/double_fork_and_exec.cc
+++ b/util/posix/double_fork_and_exec.cc
@@ -27,9 +27,12 @@
 namespace crashpad {
 
 bool DoubleForkAndExec(const std::vector<std::string>& argv,
+                       const std::vector<std::string>* envp,
                        int preserve_fd,
                        bool use_path,
                        void (*child_function)()) {
+  DCHECK(!envp || !use_path);
+
   // argv_c contains const char* pointers and is terminated by nullptr. This is
   // suitable for passing to execv(). Although argv_c is not used in the parent
   // process, it must be built in the parent process because it’s unsafe to do
@@ -41,6 +44,15 @@
   }
   argv_c.push_back(nullptr);
 
+  std::vector<const char*> envp_c;
+  if (envp) {
+    envp_c.reserve(envp->size() + 1);
+    for (const std::string& variable : *envp) {
+      envp_c.push_back(variable.c_str());
+    }
+    envp_c.push_back(nullptr);
+  }
+
   // Double-fork(). The three processes involved are parent, child, and
   // grandchild. The grandchild will call execv(). The child exits immediately
   // after spawning the grandchild, so the grandchild becomes an orphan and its
@@ -102,6 +114,13 @@
     // const_cast is safe.
     char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]);
 
+    if (envp) {
+      // This cast is safe for the same reason that the argv_for_execv cast is.
+      char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]);
+      execve(argv_for_execv[0], argv_for_execv, envp_for_execv);
+      PLOG(FATAL) << "execve " << argv_for_execv[0];
+    }
+
     if (use_path) {
       execvp(argv_for_execv[0], argv_for_execv);
       PLOG(FATAL) << "execvp " << argv_for_execv[0];
diff --git a/util/posix/double_fork_and_exec.h b/util/posix/double_fork_and_exec.h
index df340d0..02fc0f2 100644
--- a/util/posix/double_fork_and_exec.h
+++ b/util/posix/double_fork_and_exec.h
@@ -36,6 +36,9 @@
 //!
 //! \param[in] argv The argument vector to start the grandchild process with.
 //!     `argv[0]` is used as the path to the executable.
+//! \param[in] envp A vector of environment variables of the form `var=value` to
+//!     be passed to `execve()`. If this value is `nullptr`, the current
+//!     environment is used.
 //! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
 //!     process. This file descriptor is inherited in addition to the three file
 //!     descriptors associated with the standard input/output streams. Use `-1`
@@ -49,6 +52,9 @@
 //!     that this function will run in the context of a forked process, and must
 //!     be safe for that purpose.
 //!
+//! Setting both \a envp to a value other than `nullptr` and \a use_path to
+//! `true` is not currently supported.
+//!
 //! \return `true` on success, and `false` on failure with a message logged.
 //!     Only failures that occur in the parent process that indicate a definite
 //!     failure to start the the grandchild are reported in the return value.
@@ -58,6 +64,7 @@
 //!     failures, for example, by observing a failure to perform a successful
 //!     handshake with the grandchild process.
 bool DoubleForkAndExec(const std::vector<std::string>& argv,
+                       const std::vector<std::string>* envp,
                        int preserve_fd,
                        bool use_path,
                        void (*child_function)());