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