| // Copyright 2017 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/process.h" |
| |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| |
| #include "base/debug/activity_tracker.h" |
| #include "base/fuchsia/default_job.h" |
| #include "base/strings/stringprintf.h" |
| |
| namespace base { |
| |
| Process::Process(ProcessHandle handle) |
| : process_(handle), is_current_process_(false) { |
| CHECK_NE(handle, zx_process_self()); |
| } |
| |
| Process::~Process() { |
| Close(); |
| } |
| |
| Process::Process(Process&& other) |
| : process_(std::move(other.process_)), |
| is_current_process_(other.is_current_process_) { |
| other.is_current_process_ = false; |
| } |
| |
| Process& Process::operator=(Process&& other) { |
| process_ = std::move(other.process_); |
| is_current_process_ = other.is_current_process_; |
| other.is_current_process_ = false; |
| return *this; |
| } |
| |
| // static |
| Process Process::Current() { |
| Process process; |
| process.is_current_process_ = true; |
| return process; |
| } |
| |
| // static |
| Process Process::Open(ProcessId pid) { |
| if (pid == GetCurrentProcId()) |
| return Current(); |
| |
| // While a process with object id |pid| might exist, the job returned by |
| // zx_job_default() might not contain it, so this call can fail. |
| ScopedZxHandle handle; |
| zx_status_t status = zx_object_get_child( |
| GetDefaultJob(), pid, ZX_RIGHT_SAME_RIGHTS, handle.receive()); |
| if (status != ZX_OK) { |
| DLOG(ERROR) << "zx_object_get_child failed: " << status; |
| return Process(); |
| } |
| return Process(handle.release()); |
| } |
| |
| // static |
| Process Process::OpenWithExtraPrivileges(ProcessId pid) { |
| // No privileges to set. |
| return Open(pid); |
| } |
| |
| // static |
| Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { |
| DCHECK_NE(handle, GetCurrentProcessHandle()); |
| ScopedZxHandle out; |
| if (zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, out.receive()) != |
| ZX_OK) { |
| DLOG(ERROR) << "zx_handle_duplicate failed: " << handle; |
| return Process(); |
| } |
| |
| return Process(out.release()); |
| } |
| |
| // static |
| bool Process::CanBackgroundProcesses() { |
| return false; |
| } |
| |
| // static |
| void Process::TerminateCurrentProcessImmediately(int exit_code) { |
| _exit(exit_code); |
| } |
| |
| bool Process::IsValid() const { |
| return process_.is_valid() || is_current(); |
| } |
| |
| ProcessHandle Process::Handle() const { |
| return is_current_process_ ? zx_process_self() : process_.get(); |
| } |
| |
| Process Process::Duplicate() const { |
| if (is_current()) |
| return Current(); |
| |
| if (!IsValid()) |
| return Process(); |
| |
| ScopedZxHandle out; |
| if (zx_handle_duplicate(process_.get(), ZX_RIGHT_SAME_RIGHTS, |
| out.receive()) != ZX_OK) { |
| DLOG(ERROR) << "zx_handle_duplicate failed: " << process_.get(); |
| return Process(); |
| } |
| |
| return Process(out.release()); |
| } |
| |
| ProcessId Process::Pid() const { |
| DCHECK(IsValid()); |
| return GetProcId(Handle()); |
| } |
| |
| bool Process::is_current() const { |
| return is_current_process_; |
| } |
| |
| void Process::Close() { |
| is_current_process_ = false; |
| process_.reset(); |
| } |
| |
| bool Process::Terminate(int exit_code, bool wait) const { |
| // exit_code isn't supportable. https://crbug.com/753490. |
| zx_status_t status = zx_task_kill(Handle()); |
| // TODO(scottmg): Put these LOG/CHECK back to DLOG/DCHECK after |
| // https://crbug.com/750756 is diagnosed. |
| if (status == ZX_OK && wait) { |
| zx_signals_t signals; |
| status = zx_object_wait_one(Handle(), ZX_TASK_TERMINATED, |
| zx_deadline_after(ZX_SEC(60)), &signals); |
| if (status != ZX_OK) { |
| LOG(ERROR) << "Error waiting for process exit: " << status; |
| } else { |
| CHECK(signals & ZX_TASK_TERMINATED); |
| } |
| } else if (status != ZX_OK) { |
| LOG(ERROR) << "Unable to terminate process: " << status; |
| } |
| |
| return status >= 0; |
| } |
| |
| bool Process::WaitForExit(int* exit_code) const { |
| return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); |
| } |
| |
| bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const { |
| if (is_current_process_) |
| return false; |
| |
| // Record the event that this thread is blocking upon (for hang diagnosis). |
| base::debug::ScopedProcessWaitActivity process_activity(this); |
| |
| zx_time_t deadline = timeout == TimeDelta::Max() |
| ? ZX_TIME_INFINITE |
| : (TimeTicks::Now() + timeout).ToZxTime(); |
| // TODO(scottmg): https://crbug.com/755282 |
| const bool kOnBot = getenv("CHROME_HEADLESS") != nullptr; |
| if (kOnBot) { |
| LOG(ERROR) << base::StringPrintf( |
| "going to wait for process %x (deadline=%zu, now=%zu)", process_.get(), |
| deadline, TimeTicks::Now().ToZxTime()); |
| } |
| zx_signals_t signals_observed = 0; |
| zx_status_t status = zx_object_wait_one(process_.get(), ZX_TASK_TERMINATED, |
| deadline, &signals_observed); |
| |
| // TODO(scottmg): Make these LOGs into DLOGs after https://crbug.com/750756 is |
| // fixed. |
| if (status != ZX_OK && status != ZX_ERR_TIMED_OUT) { |
| LOG(ERROR) << "zx_object_wait_one failed, status=" << status; |
| return false; |
| } |
| if (status == ZX_ERR_TIMED_OUT) { |
| zx_time_t now = TimeTicks::Now().ToZxTime(); |
| LOG(ERROR) << "zx_object_wait_one timed out, signals=" << signals_observed |
| << ", deadline=" << deadline << ", now=" << now |
| << ", delta=" << (now - deadline); |
| return false; |
| } |
| |
| zx_info_process_t proc_info; |
| status = zx_object_get_info(process_.get(), ZX_INFO_PROCESS, &proc_info, |
| sizeof(proc_info), nullptr, nullptr); |
| if (status != ZX_OK) { |
| LOG(ERROR) << "zx_object_get_info failed, status=" << status; |
| if (exit_code) |
| *exit_code = -1; |
| return false; |
| } |
| |
| if (exit_code) |
| *exit_code = proc_info.return_code; |
| |
| return true; |
| } |
| |
| void Process::Exited(int exit_code) const {} |
| |
| bool Process::IsProcessBackgrounded() const { |
| // See SetProcessBackgrounded(). |
| DCHECK(IsValid()); |
| return false; |
| } |
| |
| bool Process::SetProcessBackgrounded(bool value) { |
| // No process priorities on Fuchsia. TODO(fuchsia): See MG-783, and update |
| // this later if priorities are implemented. |
| return false; |
| } |
| |
| int Process::GetPriority() const { |
| DCHECK(IsValid()); |
| // No process priorities on Fuchsia. |
| return 0; |
| } |
| |
| } // namespace base |