| // 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_controller_impl.h" |
| |
| #include <errno.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/task_runner.h" |
| #include "base/threading/simple_thread.h" |
| #include "mojo/services/files/interfaces/types.mojom.h" |
| #include "services/native_support/process_io_redirection.h" |
| |
| namespace native_support { |
| |
| namespace { |
| |
| void WaitForProcess( |
| base::Process process, |
| scoped_refptr<base::TaskRunner> done_runner, |
| const base::Callback<void(mojo::files::Error, int32_t)>& done_callback) { |
| int exit_status = 0; |
| mojo::files::Error result = process.WaitForExit(&exit_status) |
| ? mojo::files::Error::OK |
| : mojo::files::Error::UNKNOWN; |
| done_runner->PostTask( |
| FROM_HERE, |
| base::Bind(done_callback, result, static_cast<int32_t>(exit_status))); |
| } |
| |
| void TerminateProcess(base::Process process) { |
| if (!process.Terminate(-1, true)) |
| LOG(ERROR) << "Failed to kill PID " << process.Pid(); |
| } |
| |
| } // namespace |
| |
| ProcessControllerImpl::ProcessControllerImpl( |
| scoped_refptr<base::TaskRunner> worker_runner, |
| mojo::InterfaceRequest<ProcessController> request, |
| base::Process process, |
| std::unique_ptr<ProcessIORedirection> process_io_redirection) |
| : worker_runner_(worker_runner.Pass()), |
| binding_(this, request.Pass()), |
| process_(process.Pass()), |
| process_io_redirection_(std::move(process_io_redirection)), |
| weak_factory_(this) { |
| DCHECK(process_.IsValid()); |
| } |
| |
| ProcessControllerImpl::~ProcessControllerImpl() { |
| if (process_.IsValid()) { |
| worker_runner_->PostTask( |
| FROM_HERE, base::Bind(&TerminateProcess, base::Passed(&process_))); |
| } |
| } |
| |
| void ProcessControllerImpl::Wait(const WaitCallback& callback) { |
| if (!process_.IsValid()) { |
| // TODO(vtl): This isn't quite right. |
| callback.Run(mojo::files::Error::UNAVAILABLE, 0); |
| return; |
| } |
| |
| worker_runner_->PostTask( |
| FROM_HERE, base::Bind(&WaitForProcess, base::Passed(&process_), |
| base::MessageLoop::current()->task_runner(), |
| base::Bind(&ProcessControllerImpl::OnWaitComplete, |
| weak_factory_.GetWeakPtr(), callback))); |
| } |
| |
| void ProcessControllerImpl::Kill(int32_t signal, const KillCallback& callback) { |
| callback.Run(KillHelper(signal)); |
| } |
| |
| void ProcessControllerImpl::OnWaitComplete(const WaitCallback& callback, |
| mojo::files::Error result, |
| int32_t exit_status) { |
| callback.Run(result, exit_status); |
| } |
| |
| mojo::files::Error ProcessControllerImpl::KillHelper(int32_t signal) { |
| if (signal < 0) |
| return mojo::files::Error::INVALID_ARGUMENT; |
| |
| if (!process_.IsValid()) { |
| LOG(ERROR) << "Kill() called after Wait()"; |
| // TODO(vtl): This error code isn't quite right, but "unavailable" (which |
| // would also be wrong) is used for a more appropriate purpose below. |
| return mojo::files::Error::INVALID_ARGUMENT; |
| } |
| |
| // |base::HandleType| is just a typedef for |pid_t|. |
| pid_t pid = process_.Handle(); |
| |
| // Note: |kill()| is not interruptible. |
| if (kill(pid, static_cast<int>(signal)) == 0) |
| return mojo::files::Error::OK; |
| |
| switch (errno) { |
| case EINVAL: |
| return mojo::files::Error::INVALID_ARGUMENT; |
| case EPERM: |
| return mojo::files::Error::PERMISSION_DENIED; |
| case ESRCH: |
| return mojo::files::Error::UNAVAILABLE; |
| default: |
| break; |
| } |
| return mojo::files::Error::UNKNOWN; |
| } |
| |
| } // namespace native_support |