|  | // 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 |