Index: services/native_support/process_impl.cc |
diff --git a/services/native_support/process_impl.cc b/services/native_support/process_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2fc77ffdc79a95571e2bd66f2cee3305308800c1 |
--- /dev/null |
+++ b/services/native_support/process_impl.cc |
@@ -0,0 +1,239 @@ |
+// Copyright 2015 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 "services/native_support/process_impl.h" |
+ |
+#include <fcntl.h> |
+#include <stdio.h> |
+#include <unistd.h> |
+ |
+#include <algorithm> |
+#include <limits> |
+#include <utility> |
+#include <vector> |
+ |
+#include "base/command_line.h" |
+#include "base/environment.h" |
+#include "base/files/scoped_file.h" |
+#include "base/logging.h" |
+#include "base/posix/eintr_wrapper.h" |
+#include "base/process/launch.h" |
+#include "base/process/process.h" |
+#include "build/build_config.h" |
+#include "mojo/services/files/public/interfaces/types.mojom.h" |
+#include "services/native_support/make_pty_pair.h" |
+#include "services/native_support/process_controller_impl.h" |
+#include "services/native_support/process_io_redirection.h" |
+ |
+namespace native_support { |
+ |
+namespace { |
+ |
+class SetsidPreExecDelegate : public base::LaunchOptions::PreExecDelegate { |
+ public: |
+ SetsidPreExecDelegate() {} |
+ ~SetsidPreExecDelegate() override {} |
+ |
+ void RunAsyncSafe() override { |
+ static const char kErrorMessage[] = "setsid() failed"; |
+ |
+ // Note: |setsid()| and |write()| are both async-signal-safe. |
+ if (setsid() == static_cast<pid_t>(-1)) |
+ write(STDERR_FILENO, kErrorMessage, sizeof(kErrorMessage) - 1); |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(SetsidPreExecDelegate); |
+}; |
+ |
+} // namespace |
+ |
+ProcessImpl::ProcessImpl(scoped_refptr<base::TaskRunner> worker_runner, |
+ mojo::ApplicationConnection* connection, |
+ mojo::InterfaceRequest<Process> request) |
+ : worker_runner_(worker_runner.Pass()), binding_(this, request.Pass()) {} |
+ |
+ProcessImpl::~ProcessImpl() {} |
+ |
+void ProcessImpl::Spawn( |
+ const mojo::String& path, |
+ mojo::Array<mojo::String> argv, |
+ mojo::Array<mojo::String> envp, |
+ mojo::files::FilePtr stdin_file, |
+ mojo::files::FilePtr stdout_file, |
+ mojo::files::FilePtr stderr_file, |
+ mojo::InterfaceRequest<ProcessController> process_controller, |
+ const SpawnCallback& callback) { |
+ std::vector<int> fds_to_inherit(3, -1); |
+ |
+ // stdin: |
+ base::ScopedFD stdin_fd; |
+ base::ScopedFD stdin_parent_fd; |
+ if (stdin_file) { |
+ int stdin_pipe_fds[2] = {-1, -1}; |
+ CHECK_EQ(pipe(stdin_pipe_fds), 0); |
+ stdin_fd.reset(stdin_pipe_fds[0]); |
+ stdin_parent_fd.reset(stdin_pipe_fds[1]); |
+ } else { |
+ stdin_fd.reset(HANDLE_EINTR(open("/dev/null", O_RDONLY))); |
+ } |
+ fds_to_inherit[STDIN_FILENO] = stdin_fd.get(); |
+ |
+ // stdout: |
+ base::ScopedFD stdout_fd; |
+ base::ScopedFD stdout_parent_fd; |
+ if (stdout_file) { |
+ int stdout_pipe_fds[2] = {-1, -1}; |
+ CHECK_EQ(pipe(stdout_pipe_fds), 0); |
+ stdout_fd.reset(stdout_pipe_fds[1]); |
+ stdout_parent_fd.reset(stdout_pipe_fds[0]); |
+ } else { |
+ stdout_fd.reset(HANDLE_EINTR(open("/dev/null", O_WRONLY))); |
+ } |
+ fds_to_inherit[STDOUT_FILENO] = stdout_fd.get(); |
+ |
+ // stderr: |
+ base::ScopedFD stderr_fd; |
+ base::ScopedFD stderr_parent_fd; |
+ if (stderr_file) { |
+ int stderr_pipe_fds[2] = {-1, -1}; |
+ CHECK_EQ(pipe(stderr_pipe_fds), 0); |
+ stderr_fd.reset(stderr_pipe_fds[1]); |
+ stderr_parent_fd.reset(stderr_pipe_fds[0]); |
+ } else { |
+ stderr_fd.reset(HANDLE_EINTR(open("/dev/null", O_WRONLY))); |
+ } |
+ fds_to_inherit[STDERR_FILENO] = stderr_fd.get(); |
+ |
+ std::unique_ptr<ProcessIORedirection> process_io_redirection( |
+ new ProcessIORedirectionForStdIO( |
+ stdin_file.Pass(), stdout_file.Pass(), stderr_file.Pass(), |
+ stdin_parent_fd.Pass(), stdout_parent_fd.Pass(), |
+ stderr_parent_fd.Pass())); |
+ |
+ SpawnImpl(path, argv.Pass(), envp.Pass(), std::move(process_io_redirection), |
+ fds_to_inherit, process_controller.Pass(), callback); |
+} |
+ |
+void ProcessImpl::SpawnWithTerminal( |
+ const mojo::String& path, |
+ mojo::Array<mojo::String> argv, |
+ mojo::Array<mojo::String> envp, |
+ mojo::files::FilePtr terminal_file, |
+ mojo::InterfaceRequest<ProcessController> process_controller, |
+ const SpawnWithTerminalCallback& callback) { |
+ DCHECK(terminal_file); |
+ |
+ std::vector<int> fds_to_inherit(3, -1); |
+ |
+ base::ScopedFD master_fd; |
+ base::ScopedFD slave_fd; |
+ int errno_value = 0; |
+ if (!MakePtyPair(&master_fd, &slave_fd, &errno_value)) { |
+ // TODO(vtl): Well, this is dumb (we should use errno_value). |
+ callback.Run(mojo::files::ERROR_UNKNOWN); |
+ return; |
+ } |
+ |
+ // stdin: |
+ base::ScopedFD stdin_fd(slave_fd.Pass()); |
+ fds_to_inherit[STDIN_FILENO] = stdin_fd.get(); |
+ |
+ // stdout: |
+ base::ScopedFD stdout_fd(HANDLE_EINTR(dup(stdin_fd.get()))); |
+ fds_to_inherit[STDOUT_FILENO] = stdout_fd.get(); |
+ |
+ // stderr: |
+ base::ScopedFD stderr_fd(HANDLE_EINTR(dup(stdin_fd.get()))); |
+ fds_to_inherit[STDERR_FILENO] = stderr_fd.get(); |
+ |
+ std::unique_ptr<ProcessIORedirection> process_io_redirection( |
+ new ProcessIORedirectionForTerminal(terminal_file.Pass(), |
+ master_fd.Pass())); |
+ |
+ SpawnImpl(path, argv.Pass(), envp.Pass(), std::move(process_io_redirection), |
+ fds_to_inherit, process_controller.Pass(), callback); |
+} |
+ |
+void ProcessImpl::SpawnImpl( |
+ const mojo::String& path, |
+ mojo::Array<mojo::String> argv, |
+ mojo::Array<mojo::String> envp, |
+ std::unique_ptr<ProcessIORedirection> process_io_redirection, |
+ const std::vector<int>& fds_to_inherit, |
+ mojo::InterfaceRequest<ProcessController> process_controller, |
+ const SpawnCallback& callback) { |
+ DCHECK(!path.is_null()); |
+ DCHECK(process_controller.is_pending()); |
+ |
+ size_t argc = std::max(argv.size(), static_cast<size_t>(1)); |
+ std::vector<const char*> argv_ptrs(argc); |
+ if (argv.is_null()) { |
+ argv_ptrs[0] = path.data(); |
+ } else { |
+ if (!argv.size() || |
+ argv.size() > static_cast<size_t>(std::numeric_limits<int>::max())) { |
+ callback.Run(mojo::files::ERROR_INVALID_ARGUMENT); |
+ return; |
+ } |
+ // TODO(vtl): Currently, we don't support setting argv[0], due to |
+ // |base::CommandLine| limitations. |
+ argv_ptrs[0] = path.data(); |
+ for (size_t i = 1; i < argv.size(); i++) |
+ argv_ptrs[i] = argv[i].data(); |
+ } |
+ base::CommandLine command_line(static_cast<int>(argc), argv_ptrs.data()); |
+ |
+ bool inherit_environment = true; |
+ base::EnvironmentMap environment_map; |
+ if (!envp.is_null()) { |
+ inherit_environment = false; |
+ for (size_t i = 0; i < envp.size(); i++) { |
+ std::string s(envp[i].data()); |
+ size_t equals_pos = s.find_first_of('='); |
+ environment_map[s.substr(0, equals_pos)] = |
+ (equals_pos == std::string::npos) ? std::string() |
+ : s.substr(equals_pos + 1); |
+ } |
+ } |
+ |
+ base::FileHandleMappingVector fd_mapping; |
+ DCHECK(fds_to_inherit.size() >= 3); |
+ for (size_t i = 0; i < fds_to_inherit.size(); i++) { |
+ DCHECK_GE(fds_to_inherit[i], 0); |
+ fd_mapping.push_back( |
+ std::make_pair(fds_to_inherit[i], static_cast<int>(i))); |
+ } |
+ |
+ SetsidPreExecDelegate pre_exec_delegate; |
+ base::LaunchOptions launch_options; |
+ launch_options.wait = false; |
+ launch_options.environ.swap(environment_map); |
+ launch_options.clear_environ = !inherit_environment; |
+ launch_options.fds_to_remap = &fd_mapping; |
+ // launch_options.maximize_rlimits |
+ launch_options.new_process_group = false; |
+ // launch_options.clone_flags = 0; |
+#if defined(OS_LINUX) |
+ launch_options.allow_new_privs = true; |
+#endif |
+ // launch_options.kill_on_parent_death = true; |
+ // launch_options.current_directory |
+ launch_options.pre_exec_delegate = &pre_exec_delegate; |
+ |
+ base::Process process = LaunchProcess(command_line, launch_options); |
+ // Note: Failure is extremely unusual. E.g., it won't fail even if |path| |
+ // doesn't exist (since fork succeeds; it's the exec that fails). |
+ if (!process.IsValid()) { |
+ // TODO(vtl): Well, this is dumb (can we check errno?). |
+ callback.Run(mojo::files::ERROR_UNKNOWN); |
+ return; |
+ } |
+ |
+ new ProcessControllerImpl(worker_runner_, process_controller.Pass(), |
+ process.Pass(), std::move(process_io_redirection)); |
+ callback.Run(mojo::files::ERROR_OK); |
+} |
+ |
+} // namespace native_support |