blob: 3732bc1ecc50e596ad47c353850776d062746bee [file] [log] [blame]
Daniel Eratb8cf9492015-07-06 13:18:13 -06001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/process/launch.h"
6
Jay Civelli3a83cdd2017-03-22 17:31:44 -07007#include <crt_externs.h>
Daniel Eratb8cf9492015-07-06 13:18:13 -06008#include <mach/mach.h>
Jay Civelli3a83cdd2017-03-22 17:31:44 -07009#include <spawn.h>
10#include <string.h>
11#include <sys/wait.h>
Daniel Eratb8cf9492015-07-06 13:18:13 -060012
13#include "base/logging.h"
Jay Civelli3a83cdd2017-03-22 17:31:44 -070014#include "base/posix/eintr_wrapper.h"
15#include "base/threading/thread_restrictions.h"
Daniel Eratb8cf9492015-07-06 13:18:13 -060016
17namespace base {
18
Jay Civelli3a83cdd2017-03-22 17:31:44 -070019namespace {
20
21// DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
22// functions return an errno value, as opposed to setting errno directly. This
23// macro emulates a DPCHECK().
24#define DPSXCHECK(expr) \
25 do { \
26 int rv = (expr); \
27 DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \
28 } while (0)
29
30class PosixSpawnAttr {
31 public:
32 PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
33
34 ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
35
36 posix_spawnattr_t* get() { return &attr_; }
37
38 private:
39 posix_spawnattr_t attr_;
40};
41
42class PosixSpawnFileActions {
43 public:
44 PosixSpawnFileActions() {
45 DPSXCHECK(posix_spawn_file_actions_init(&file_actions_));
46 }
47
48 ~PosixSpawnFileActions() {
49 DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_));
50 }
51
52 void Open(int filedes, const char* path, int mode) {
53 DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path,
54 mode, 0));
55 }
56
57 void Dup2(int filedes, int newfiledes) {
58 DPSXCHECK(
59 posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
60 }
61
62 void Inherit(int filedes) {
63 DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
64 }
65
66 const posix_spawn_file_actions_t* get() const { return &file_actions_; }
67
68 private:
69 posix_spawn_file_actions_t file_actions_;
70
71 DISALLOW_COPY_AND_ASSIGN(PosixSpawnFileActions);
72};
73
74} // namespace
75
Daniel Eratb8cf9492015-07-06 13:18:13 -060076void RestoreDefaultExceptionHandler() {
77 // This function is tailored to remove the Breakpad exception handler.
78 // exception_mask matches s_exception_mask in
79 // breakpad/src/client/mac/handler/exception_handler.cc
80 const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
81 EXC_MASK_BAD_INSTRUCTION |
82 EXC_MASK_ARITHMETIC |
83 EXC_MASK_BREAKPOINT;
84
85 // Setting the exception port to MACH_PORT_NULL may not be entirely
86 // kosher to restore the default exception handler, but in practice,
87 // it results in the exception port being set to Apple Crash Reporter,
88 // the desired behavior.
89 task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL,
90 EXCEPTION_DEFAULT, THREAD_STATE_NONE);
91}
92
Jay Civelli3a83cdd2017-03-22 17:31:44 -070093Process LaunchProcessPosixSpawn(const std::vector<std::string>& argv,
94 const LaunchOptions& options) {
95 DCHECK(!options.pre_exec_delegate)
96 << "LaunchProcessPosixSpawn does not support PreExecDelegate";
97 DCHECK(options.current_directory.empty())
98 << "LaunchProcessPosixSpawn does not support current_directory";
99
100 PosixSpawnAttr attr;
101
102 short flags = POSIX_SPAWN_CLOEXEC_DEFAULT;
103 if (options.new_process_group) {
104 flags |= POSIX_SPAWN_SETPGROUP;
105 DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0));
106 }
107 DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags));
108
109 PosixSpawnFileActions file_actions;
110
111 // Process file descriptors for the child. By default, LaunchProcess will
112 // open stdin to /dev/null and inherit stdout and stderr.
113 bool inherit_stdout = true, inherit_stderr = true;
114 bool null_stdin = true;
115 if (options.fds_to_remap) {
116 for (const auto& dup2_pair : *options.fds_to_remap) {
117 if (dup2_pair.second == STDIN_FILENO) {
118 null_stdin = false;
119 } else if (dup2_pair.second == STDOUT_FILENO) {
120 inherit_stdout = false;
121 } else if (dup2_pair.second == STDERR_FILENO) {
122 inherit_stderr = false;
123 }
124
125 if (dup2_pair.first == dup2_pair.second) {
126 file_actions.Inherit(dup2_pair.second);
127 } else {
128 file_actions.Dup2(dup2_pair.first, dup2_pair.second);
129 }
130 }
131 }
132
133 if (null_stdin) {
134 file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
135 }
136 if (inherit_stdout) {
137 file_actions.Inherit(STDOUT_FILENO);
138 }
139 if (inherit_stderr) {
140 file_actions.Inherit(STDERR_FILENO);
141 }
142
143 std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]);
144 for (size_t i = 0; i < argv.size(); i++) {
145 argv_cstr[i] = const_cast<char*>(argv[i].c_str());
146 }
147 argv_cstr[argv.size()] = nullptr;
148
149 std::unique_ptr<char* []> owned_environ;
150 char** new_environ = options.clear_environ ? nullptr : *_NSGetEnviron();
151 if (!options.environ.empty()) {
152 owned_environ = AlterEnvironment(new_environ, options.environ);
153 new_environ = owned_environ.get();
154 }
155
156 const char* executable_path = !options.real_path.empty()
157 ? options.real_path.value().c_str()
158 : argv_cstr[0];
159
160 // Use posix_spawnp as some callers expect to have PATH consulted.
161 pid_t pid;
162 int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
163 &argv_cstr[0], new_environ);
164
165 if (rv != 0) {
166 DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " "
167 << strerror(rv);
168 return Process();
169 }
170
171 if (options.wait) {
172 // While this isn't strictly disk IO, waiting for another process to
173 // finish is the sort of thing ThreadRestrictions is trying to prevent.
174 base::ThreadRestrictions::AssertIOAllowed();
175 pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
176 DPCHECK(ret > 0);
177 }
178
179 return Process(pid);
180}
181
Daniel Eratb8cf9492015-07-06 13:18:13 -0600182} // namespace base