1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/process/process.h"
6
7 #include <lib/zx/process.h>
8 #include <zircon/process.h>
9 #include <zircon/syscalls.h>
10
11 #include "base/clang_profiling_buildflags.h"
12 #include "base/fuchsia/default_job.h"
13 #include "base/fuchsia/fuchsia_logging.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/trace_event/base_tracing.h"
16
17 #if BUILDFLAG(CLANG_PROFILING)
18 #include "base/test/clang_profiling.h"
19 #endif
20
21 namespace base {
22
23 namespace {
24
FindProcessInJobTree(const zx::job & job,ProcessId pid)25 zx::process FindProcessInJobTree(const zx::job& job, ProcessId pid) {
26 zx::process process;
27 zx_status_t status = job.get_child(pid, ZX_RIGHT_SAME_RIGHTS, &process);
28
29 if (status == ZX_OK)
30 return process;
31
32 if (status == ZX_ERR_NOT_FOUND) {
33 std::vector<zx_koid_t> job_koids(32);
34 while (true) {
35 // Fetch the KOIDs of the job children of |job|.
36 size_t actual = 0u;
37 size_t available = 0u;
38 status = job.get_info(ZX_INFO_JOB_CHILDREN, job_koids.data(),
39 job_koids.size() * sizeof(zx_koid_t), &actual,
40 &available);
41
42 if (status != ZX_OK) {
43 ZX_DLOG(ERROR, status) << "zx_object_get_info(JOB_CHILDREN)";
44 return zx::process();
45 }
46
47 // If |job_koids| was too small then resize it and try again.
48 if (available > actual) {
49 job_koids.resize(available);
50 continue;
51 }
52
53 // Break out of the loop and iterate over |job_koids|, to find the PID.
54 job_koids.resize(actual);
55 break;
56 }
57
58 for (zx_koid_t job_koid : job_koids) {
59 zx::job child_job;
60 if (job.get_child(job_koid, ZX_RIGHT_SAME_RIGHTS, &child_job) != ZX_OK)
61 continue;
62 process = FindProcessInJobTree(child_job, pid);
63 if (process)
64 return process;
65 }
66
67 return zx::process();
68 }
69
70 ZX_DLOG(ERROR, status) << "zx_object_get_child";
71 return zx::process();
72 }
73
74 } // namespace
75
Process(ProcessHandle handle)76 Process::Process(ProcessHandle handle)
77 : process_(handle), is_current_process_(false) {
78 CHECK_NE(handle, zx_process_self());
79 }
80
~Process()81 Process::~Process() {
82 Close();
83 }
84
Process(Process && other)85 Process::Process(Process&& other)
86 : process_(std::move(other.process_)),
87 is_current_process_(other.is_current_process_) {
88 other.is_current_process_ = false;
89 }
90
operator =(Process && other)91 Process& Process::operator=(Process&& other) {
92 process_ = std::move(other.process_);
93 is_current_process_ = other.is_current_process_;
94 other.is_current_process_ = false;
95 return *this;
96 }
97
98 // static
Current()99 Process Process::Current() {
100 Process process;
101 process.is_current_process_ = true;
102 return process;
103 }
104
105 // static
Open(ProcessId pid)106 Process Process::Open(ProcessId pid) {
107 if (pid == GetCurrentProcId())
108 return Current();
109
110 return Process(FindProcessInJobTree(*GetDefaultJob(), pid).release());
111 }
112
113 // static
OpenWithExtraPrivileges(ProcessId pid)114 Process Process::OpenWithExtraPrivileges(ProcessId pid) {
115 // No privileges to set.
116 return Open(pid);
117 }
118
119 // static
CanSetPriority()120 bool Process::CanSetPriority() {
121 return false;
122 }
123
124 // static
TerminateCurrentProcessImmediately(int exit_code)125 void Process::TerminateCurrentProcessImmediately(int exit_code) {
126 #if BUILDFLAG(CLANG_PROFILING)
127 WriteClangProfilingProfile();
128 #endif
129 _exit(exit_code);
130 }
131
IsValid() const132 bool Process::IsValid() const {
133 return process_.is_valid() || is_current();
134 }
135
Handle() const136 ProcessHandle Process::Handle() const {
137 return is_current_process_ ? zx_process_self() : process_.get();
138 }
139
Duplicate() const140 Process Process::Duplicate() const {
141 if (is_current())
142 return Current();
143
144 if (!IsValid())
145 return Process();
146
147 zx::process out;
148 zx_status_t result = process_.duplicate(ZX_RIGHT_SAME_RIGHTS, &out);
149 if (result != ZX_OK) {
150 ZX_DLOG(ERROR, result) << "zx_handle_duplicate";
151 return Process();
152 }
153
154 return Process(out.release());
155 }
156
Release()157 ProcessHandle Process::Release() {
158 if (is_current()) {
159 // Caller expects to own the reference, so duplicate the self handle.
160 zx::process handle;
161 zx_status_t result =
162 zx::process::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &handle);
163 if (result != ZX_OK) {
164 return kNullProcessHandle;
165 }
166 is_current_process_ = false;
167 return handle.release();
168 }
169 return process_.release();
170 }
171
Pid() const172 ProcessId Process::Pid() const {
173 DCHECK(IsValid());
174 return GetProcId(Handle());
175 }
176
CreationTime() const177 Time Process::CreationTime() const {
178 zx_info_process_t proc_info;
179 zx_status_t status =
180 zx_object_get_info(Handle(), ZX_INFO_PROCESS, &proc_info,
181 sizeof(proc_info), nullptr, nullptr);
182 if (status != ZX_OK) {
183 ZX_DLOG(ERROR, status) << "zx_process_get_info";
184 return Time();
185 }
186 if ((proc_info.flags & ZX_INFO_PROCESS_FLAG_STARTED) == 0) {
187 DLOG(WARNING) << "zx_process_get_info: Not started.";
188 return Time();
189 }
190 // Process creation times are expressed in ticks since system boot, so
191 // perform a best-effort translation from that to UTC "wall-clock" time.
192 return Time::Now() +
193 (TimeTicks::FromZxTime(proc_info.start_time) - TimeTicks::Now());
194 }
195
is_current() const196 bool Process::is_current() const {
197 return is_current_process_;
198 }
199
Close()200 void Process::Close() {
201 is_current_process_ = false;
202 process_.reset();
203 }
204
Terminate(int exit_code,bool wait) const205 bool Process::Terminate(int exit_code, bool wait) const {
206 // exit_code isn't supportable. https://crbug.com/753490.
207 zx_status_t status = zx_task_kill(Handle());
208 if (status == ZX_OK && wait) {
209 zx_signals_t signals;
210 status = zx_object_wait_one(Handle(), ZX_TASK_TERMINATED,
211 zx_deadline_after(ZX_SEC(60)), &signals);
212 if (status != ZX_OK) {
213 ZX_DLOG(ERROR, status) << "zx_object_wait_one(terminate)";
214 } else {
215 CHECK(signals & ZX_TASK_TERMINATED);
216 }
217 } else if (status != ZX_OK) {
218 ZX_DLOG(ERROR, status) << "zx_task_kill";
219 }
220
221 return status >= 0;
222 }
223
WaitForExit(int * exit_code) const224 bool Process::WaitForExit(int* exit_code) const {
225 return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
226 }
227
WaitForExitWithTimeout(TimeDelta timeout,int * exit_code) const228 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
229 if (is_current_process_)
230 return false;
231
232 TRACE_EVENT0("base", "Process::WaitForExitWithTimeout");
233
234 if (!timeout.is_zero()) {
235 // Assert that this thread is allowed to wait below. This intentionally
236 // doesn't use ScopedBlockingCallWithBaseSyncPrimitives because the process
237 // being waited upon tends to itself be using the CPU and considering this
238 // thread non-busy causes more issue than it fixes: http://crbug.com/905788
239 internal::AssertBaseSyncPrimitivesAllowed();
240 }
241
242 zx::time deadline = timeout == TimeDelta::Max()
243 ? zx::time::infinite()
244 : zx::time((TimeTicks::Now() + timeout).ToZxTime());
245 zx_signals_t signals_observed = 0;
246 zx_status_t status =
247 process_.wait_one(ZX_TASK_TERMINATED, deadline, &signals_observed);
248 if (status != ZX_OK) {
249 ZX_DLOG_IF(ERROR, status != ZX_ERR_TIMED_OUT, status)
250 << "zx_object_wait_one";
251 return false;
252 }
253
254 zx_info_process_t proc_info;
255 status = process_.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info),
256 nullptr, nullptr);
257 if (status != ZX_OK) {
258 ZX_DLOG(ERROR, status) << "zx_object_get_info";
259 if (exit_code)
260 *exit_code = -1;
261 return false;
262 }
263
264 if (exit_code)
265 *exit_code = static_cast<int>(proc_info.return_code);
266
267 return true;
268 }
269
Exited(int exit_code) const270 void Process::Exited(int exit_code) const {}
271
GetPriority() const272 Process::Priority Process::GetPriority() const {
273 // See SetPriority().
274 DCHECK(IsValid());
275 return Priority::kUserBlocking;
276 }
277
SetPriority(Priority priority)278 bool Process::SetPriority(Priority priority) {
279 // No process priorities on Fuchsia.
280 // TODO(fxbug.dev/30735): Update this later if priorities are implemented.
281 return false;
282 }
283
GetOSPriority() const284 int Process::GetOSPriority() const {
285 DCHECK(IsValid());
286 // No process priorities on Fuchsia.
287 return 0;
288 }
289
290 } // namespace base
291