|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/process/launch.h" | 
|  |  | 
|  | #include <crt_externs.h> | 
|  | #include <mach/mach.h> | 
|  | #include <spawn.h> | 
|  | #include <string.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #include "base/threading/thread_restrictions.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of | 
|  | // functions return an errno value, as opposed to setting errno directly. This | 
|  | // macro emulates a DPCHECK(). | 
|  | #define DPSXCHECK(expr)                                              \ | 
|  | do {                                                               \ | 
|  | int rv = (expr);                                                 \ | 
|  | DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \ | 
|  | } while (0) | 
|  |  | 
|  | class PosixSpawnAttr { | 
|  | public: | 
|  | PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); } | 
|  |  | 
|  | ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); } | 
|  |  | 
|  | posix_spawnattr_t* get() { return &attr_; } | 
|  |  | 
|  | private: | 
|  | posix_spawnattr_t attr_; | 
|  | }; | 
|  |  | 
|  | class PosixSpawnFileActions { | 
|  | public: | 
|  | PosixSpawnFileActions() { | 
|  | DPSXCHECK(posix_spawn_file_actions_init(&file_actions_)); | 
|  | } | 
|  |  | 
|  | ~PosixSpawnFileActions() { | 
|  | DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_)); | 
|  | } | 
|  |  | 
|  | void Open(int filedes, const char* path, int mode) { | 
|  | DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path, | 
|  | mode, 0)); | 
|  | } | 
|  |  | 
|  | void Dup2(int filedes, int newfiledes) { | 
|  | DPSXCHECK( | 
|  | posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes)); | 
|  | } | 
|  |  | 
|  | void Inherit(int filedes) { | 
|  | DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes)); | 
|  | } | 
|  |  | 
|  | const posix_spawn_file_actions_t* get() const { return &file_actions_; } | 
|  |  | 
|  | private: | 
|  | posix_spawn_file_actions_t file_actions_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(PosixSpawnFileActions); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void RestoreDefaultExceptionHandler() { | 
|  | // This function is tailored to remove the Breakpad exception handler. | 
|  | // exception_mask matches s_exception_mask in | 
|  | // third_party/breakpad/breakpad/src/client/mac/handler/exception_handler.cc | 
|  | const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS | | 
|  | EXC_MASK_BAD_INSTRUCTION | | 
|  | EXC_MASK_ARITHMETIC | | 
|  | EXC_MASK_BREAKPOINT; | 
|  |  | 
|  | // Setting the exception port to MACH_PORT_NULL may not be entirely | 
|  | // kosher to restore the default exception handler, but in practice, | 
|  | // it results in the exception port being set to Apple Crash Reporter, | 
|  | // the desired behavior. | 
|  | task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL, | 
|  | EXCEPTION_DEFAULT, THREAD_STATE_NONE); | 
|  | } | 
|  |  | 
|  | Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv, | 
|  | const LaunchOptions& options) { | 
|  | DCHECK(!options.pre_exec_delegate) | 
|  | << "LaunchProcessPosixSpawn does not support PreExecDelegate"; | 
|  | DCHECK(options.current_directory.empty()) | 
|  | << "LaunchProcessPosixSpawn does not support current_directory"; | 
|  |  | 
|  | PosixSpawnAttr attr; | 
|  |  | 
|  | short flags = POSIX_SPAWN_CLOEXEC_DEFAULT; | 
|  | if (options.new_process_group) { | 
|  | flags |= POSIX_SPAWN_SETPGROUP; | 
|  | DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0)); | 
|  | } | 
|  | DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags)); | 
|  |  | 
|  | PosixSpawnFileActions file_actions; | 
|  |  | 
|  | // Process file descriptors for the child. By default, LaunchProcess will | 
|  | // open stdin to /dev/null and inherit stdout and stderr. | 
|  | bool inherit_stdout = true, inherit_stderr = true; | 
|  | bool null_stdin = true; | 
|  | for (const auto& dup2_pair : options.fds_to_remap) { | 
|  | if (dup2_pair.second == STDIN_FILENO) { | 
|  | null_stdin = false; | 
|  | } else if (dup2_pair.second == STDOUT_FILENO) { | 
|  | inherit_stdout = false; | 
|  | } else if (dup2_pair.second == STDERR_FILENO) { | 
|  | inherit_stderr = false; | 
|  | } | 
|  |  | 
|  | if (dup2_pair.first == dup2_pair.second) { | 
|  | file_actions.Inherit(dup2_pair.second); | 
|  | } else { | 
|  | file_actions.Dup2(dup2_pair.first, dup2_pair.second); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (null_stdin) { | 
|  | file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY); | 
|  | } | 
|  | if (inherit_stdout) { | 
|  | file_actions.Inherit(STDOUT_FILENO); | 
|  | } | 
|  | if (inherit_stderr) { | 
|  | file_actions.Inherit(STDERR_FILENO); | 
|  | } | 
|  |  | 
|  | std::vector<char*> argv_cstr; | 
|  | argv_cstr.reserve(argv.size() + 1); | 
|  | for (const auto& arg : argv) | 
|  | argv_cstr.push_back(const_cast<char*>(arg.c_str())); | 
|  | argv_cstr.push_back(nullptr); | 
|  |  | 
|  | std::unique_ptr<char* []> owned_environ; | 
|  | char** new_environ = options.clear_environ ? nullptr : *_NSGetEnviron(); | 
|  | if (!options.environ.empty()) { | 
|  | owned_environ = AlterEnvironment(new_environ, options.environ); | 
|  | new_environ = owned_environ.get(); | 
|  | } | 
|  |  | 
|  | const char* executable_path = !options.real_path.empty() | 
|  | ? options.real_path.value().c_str() | 
|  | : argv_cstr[0]; | 
|  |  | 
|  | // Use posix_spawnp as some callers expect to have PATH consulted. | 
|  | pid_t pid; | 
|  | int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(), | 
|  | &argv_cstr[0], new_environ); | 
|  |  | 
|  | if (rv != 0) { | 
|  | DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " " | 
|  | << strerror(rv); | 
|  | return Process(); | 
|  | } | 
|  |  | 
|  | if (options.wait) { | 
|  | // While this isn't strictly disk IO, waiting for another process to | 
|  | // finish is the sort of thing ThreadRestrictions is trying to prevent. | 
|  | base::AssertBlockingAllowed(); | 
|  | pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0)); | 
|  | DPCHECK(ret > 0); | 
|  | } | 
|  |  | 
|  | return Process(pid); | 
|  | } | 
|  |  | 
|  | }  // namespace base |