OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "services/native_support/process_impl.h" |
| 6 |
| 7 #include <fcntl.h> |
| 8 #include <stdio.h> |
| 9 #include <unistd.h> |
| 10 |
| 11 #include <algorithm> |
| 12 #include <limits> |
| 13 #include <utility> |
| 14 #include <vector> |
| 15 |
| 16 #include "base/command_line.h" |
| 17 #include "base/environment.h" |
| 18 #include "base/files/scoped_file.h" |
| 19 #include "base/logging.h" |
| 20 #include "base/posix/eintr_wrapper.h" |
| 21 #include "base/process/launch.h" |
| 22 #include "base/process/process.h" |
| 23 #include "build/build_config.h" |
| 24 #include "mojo/services/files/public/interfaces/types.mojom.h" |
| 25 #include "services/native_support/make_pty_pair.h" |
| 26 #include "services/native_support/process_controller_impl.h" |
| 27 #include "services/native_support/process_io_redirection.h" |
| 28 |
| 29 namespace native_support { |
| 30 |
| 31 namespace { |
| 32 |
| 33 class SetsidPreExecDelegate : public base::LaunchOptions::PreExecDelegate { |
| 34 public: |
| 35 SetsidPreExecDelegate() {} |
| 36 ~SetsidPreExecDelegate() override {} |
| 37 |
| 38 void RunAsyncSafe() override { |
| 39 static const char kErrorMessage[] = "setsid() failed"; |
| 40 |
| 41 // Note: |setsid()| and |write()| are both async-signal-safe. |
| 42 if (setsid() == static_cast<pid_t>(-1)) |
| 43 write(STDERR_FILENO, kErrorMessage, sizeof(kErrorMessage) - 1); |
| 44 } |
| 45 |
| 46 private: |
| 47 DISALLOW_COPY_AND_ASSIGN(SetsidPreExecDelegate); |
| 48 }; |
| 49 |
| 50 } // namespace |
| 51 |
| 52 ProcessImpl::ProcessImpl(scoped_refptr<base::TaskRunner> worker_runner, |
| 53 mojo::ApplicationConnection* connection, |
| 54 mojo::InterfaceRequest<Process> request) |
| 55 : worker_runner_(worker_runner.Pass()), binding_(this, request.Pass()) {} |
| 56 |
| 57 ProcessImpl::~ProcessImpl() {} |
| 58 |
| 59 void ProcessImpl::Spawn( |
| 60 const mojo::String& path, |
| 61 mojo::Array<mojo::String> argv, |
| 62 mojo::Array<mojo::String> envp, |
| 63 mojo::files::FilePtr stdin_file, |
| 64 mojo::files::FilePtr stdout_file, |
| 65 mojo::files::FilePtr stderr_file, |
| 66 mojo::InterfaceRequest<ProcessController> process_controller, |
| 67 const SpawnCallback& callback) { |
| 68 std::vector<int> fds_to_inherit(3, -1); |
| 69 |
| 70 // stdin: |
| 71 base::ScopedFD stdin_fd; |
| 72 base::ScopedFD stdin_parent_fd; |
| 73 if (stdin_file) { |
| 74 int stdin_pipe_fds[2] = {-1, -1}; |
| 75 CHECK_EQ(pipe(stdin_pipe_fds), 0); |
| 76 stdin_fd.reset(stdin_pipe_fds[0]); |
| 77 stdin_parent_fd.reset(stdin_pipe_fds[1]); |
| 78 } else { |
| 79 stdin_fd.reset(HANDLE_EINTR(open("/dev/null", O_RDONLY))); |
| 80 } |
| 81 fds_to_inherit[STDIN_FILENO] = stdin_fd.get(); |
| 82 |
| 83 // stdout: |
| 84 base::ScopedFD stdout_fd; |
| 85 base::ScopedFD stdout_parent_fd; |
| 86 if (stdout_file) { |
| 87 int stdout_pipe_fds[2] = {-1, -1}; |
| 88 CHECK_EQ(pipe(stdout_pipe_fds), 0); |
| 89 stdout_fd.reset(stdout_pipe_fds[1]); |
| 90 stdout_parent_fd.reset(stdout_pipe_fds[0]); |
| 91 } else { |
| 92 stdout_fd.reset(HANDLE_EINTR(open("/dev/null", O_WRONLY))); |
| 93 } |
| 94 fds_to_inherit[STDOUT_FILENO] = stdout_fd.get(); |
| 95 |
| 96 // stderr: |
| 97 base::ScopedFD stderr_fd; |
| 98 base::ScopedFD stderr_parent_fd; |
| 99 if (stderr_file) { |
| 100 int stderr_pipe_fds[2] = {-1, -1}; |
| 101 CHECK_EQ(pipe(stderr_pipe_fds), 0); |
| 102 stderr_fd.reset(stderr_pipe_fds[1]); |
| 103 stderr_parent_fd.reset(stderr_pipe_fds[0]); |
| 104 } else { |
| 105 stderr_fd.reset(HANDLE_EINTR(open("/dev/null", O_WRONLY))); |
| 106 } |
| 107 fds_to_inherit[STDERR_FILENO] = stderr_fd.get(); |
| 108 |
| 109 std::unique_ptr<ProcessIORedirection> process_io_redirection( |
| 110 new ProcessIORedirectionForStdIO( |
| 111 stdin_file.Pass(), stdout_file.Pass(), stderr_file.Pass(), |
| 112 stdin_parent_fd.Pass(), stdout_parent_fd.Pass(), |
| 113 stderr_parent_fd.Pass())); |
| 114 |
| 115 SpawnImpl(path, argv.Pass(), envp.Pass(), std::move(process_io_redirection), |
| 116 fds_to_inherit, process_controller.Pass(), callback); |
| 117 } |
| 118 |
| 119 void ProcessImpl::SpawnWithTerminal( |
| 120 const mojo::String& path, |
| 121 mojo::Array<mojo::String> argv, |
| 122 mojo::Array<mojo::String> envp, |
| 123 mojo::files::FilePtr terminal_file, |
| 124 mojo::InterfaceRequest<ProcessController> process_controller, |
| 125 const SpawnWithTerminalCallback& callback) { |
| 126 DCHECK(terminal_file); |
| 127 |
| 128 std::vector<int> fds_to_inherit(3, -1); |
| 129 |
| 130 base::ScopedFD master_fd; |
| 131 base::ScopedFD slave_fd; |
| 132 int errno_value = 0; |
| 133 if (!MakePtyPair(&master_fd, &slave_fd, &errno_value)) { |
| 134 // TODO(vtl): Well, this is dumb (we should use errno_value). |
| 135 callback.Run(mojo::files::ERROR_UNKNOWN); |
| 136 return; |
| 137 } |
| 138 |
| 139 // stdin: |
| 140 base::ScopedFD stdin_fd(slave_fd.Pass()); |
| 141 fds_to_inherit[STDIN_FILENO] = stdin_fd.get(); |
| 142 |
| 143 // stdout: |
| 144 base::ScopedFD stdout_fd(HANDLE_EINTR(dup(stdin_fd.get()))); |
| 145 fds_to_inherit[STDOUT_FILENO] = stdout_fd.get(); |
| 146 |
| 147 // stderr: |
| 148 base::ScopedFD stderr_fd(HANDLE_EINTR(dup(stdin_fd.get()))); |
| 149 fds_to_inherit[STDERR_FILENO] = stderr_fd.get(); |
| 150 |
| 151 std::unique_ptr<ProcessIORedirection> process_io_redirection( |
| 152 new ProcessIORedirectionForTerminal(terminal_file.Pass(), |
| 153 master_fd.Pass())); |
| 154 |
| 155 SpawnImpl(path, argv.Pass(), envp.Pass(), std::move(process_io_redirection), |
| 156 fds_to_inherit, process_controller.Pass(), callback); |
| 157 } |
| 158 |
| 159 void ProcessImpl::SpawnImpl( |
| 160 const mojo::String& path, |
| 161 mojo::Array<mojo::String> argv, |
| 162 mojo::Array<mojo::String> envp, |
| 163 std::unique_ptr<ProcessIORedirection> process_io_redirection, |
| 164 const std::vector<int>& fds_to_inherit, |
| 165 mojo::InterfaceRequest<ProcessController> process_controller, |
| 166 const SpawnCallback& callback) { |
| 167 DCHECK(!path.is_null()); |
| 168 DCHECK(process_controller.is_pending()); |
| 169 |
| 170 size_t argc = std::max(argv.size(), static_cast<size_t>(1)); |
| 171 std::vector<const char*> argv_ptrs(argc); |
| 172 if (argv.is_null()) { |
| 173 argv_ptrs[0] = path.data(); |
| 174 } else { |
| 175 if (!argv.size() || |
| 176 argv.size() > static_cast<size_t>(std::numeric_limits<int>::max())) { |
| 177 callback.Run(mojo::files::ERROR_INVALID_ARGUMENT); |
| 178 return; |
| 179 } |
| 180 // TODO(vtl): Currently, we don't support setting argv[0], due to |
| 181 // |base::CommandLine| limitations. |
| 182 argv_ptrs[0] = path.data(); |
| 183 for (size_t i = 1; i < argv.size(); i++) |
| 184 argv_ptrs[i] = argv[i].data(); |
| 185 } |
| 186 base::CommandLine command_line(static_cast<int>(argc), argv_ptrs.data()); |
| 187 |
| 188 bool inherit_environment = true; |
| 189 base::EnvironmentMap environment_map; |
| 190 if (!envp.is_null()) { |
| 191 inherit_environment = false; |
| 192 for (size_t i = 0; i < envp.size(); i++) { |
| 193 std::string s(envp[i].data()); |
| 194 size_t equals_pos = s.find_first_of('='); |
| 195 environment_map[s.substr(0, equals_pos)] = |
| 196 (equals_pos == std::string::npos) ? std::string() |
| 197 : s.substr(equals_pos + 1); |
| 198 } |
| 199 } |
| 200 |
| 201 base::FileHandleMappingVector fd_mapping; |
| 202 DCHECK(fds_to_inherit.size() >= 3); |
| 203 for (size_t i = 0; i < fds_to_inherit.size(); i++) { |
| 204 DCHECK_GE(fds_to_inherit[i], 0); |
| 205 fd_mapping.push_back( |
| 206 std::make_pair(fds_to_inherit[i], static_cast<int>(i))); |
| 207 } |
| 208 |
| 209 SetsidPreExecDelegate pre_exec_delegate; |
| 210 base::LaunchOptions launch_options; |
| 211 launch_options.wait = false; |
| 212 launch_options.environ.swap(environment_map); |
| 213 launch_options.clear_environ = !inherit_environment; |
| 214 launch_options.fds_to_remap = &fd_mapping; |
| 215 // launch_options.maximize_rlimits |
| 216 launch_options.new_process_group = false; |
| 217 // launch_options.clone_flags = 0; |
| 218 #if defined(OS_LINUX) |
| 219 launch_options.allow_new_privs = true; |
| 220 #endif |
| 221 // launch_options.kill_on_parent_death = true; |
| 222 // launch_options.current_directory |
| 223 launch_options.pre_exec_delegate = &pre_exec_delegate; |
| 224 |
| 225 base::Process process = LaunchProcess(command_line, launch_options); |
| 226 // Note: Failure is extremely unusual. E.g., it won't fail even if |path| |
| 227 // doesn't exist (since fork succeeds; it's the exec that fails). |
| 228 if (!process.IsValid()) { |
| 229 // TODO(vtl): Well, this is dumb (can we check errno?). |
| 230 callback.Run(mojo::files::ERROR_UNKNOWN); |
| 231 return; |
| 232 } |
| 233 |
| 234 new ProcessControllerImpl(worker_runner_, process_controller.Pass(), |
| 235 process.Pass(), std::move(process_io_redirection)); |
| 236 callback.Run(mojo::files::ERROR_OK); |
| 237 } |
| 238 |
| 239 } // namespace native_support |
OLD | NEW |