1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "exec_utils.h"
18
19 #include <poll.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23
24 #include <chrono>
25 #include <climits>
26 #include <condition_variable>
27 #include <cstdint>
28 #include <cstring>
29 #include <ctime>
30 #include <mutex>
31 #include <optional>
32 #include <string>
33 #include <string_view>
34 #include <thread>
35 #include <vector>
36
37 #include "android-base/file.h"
38 #include "android-base/parseint.h"
39 #include "android-base/scopeguard.h"
40 #include "android-base/stringprintf.h"
41 #include "android-base/strings.h"
42 #include "android-base/unique_fd.h"
43 #include "base/macros.h"
44 #include "base/pidfd.h"
45 #include "base/utils.h"
46 #include "runtime.h"
47
48 namespace art HIDDEN {
49
50 namespace {
51
52 using ::android::base::ParseInt;
53 using ::android::base::ReadFileToString;
54 using ::android::base::StringPrintf;
55 using ::android::base::unique_fd;
56
ToCommandLine(const std::vector<std::string> & args)57 std::string ToCommandLine(const std::vector<std::string>& args) {
58 return android::base::Join(args, ' ');
59 }
60
61 // Fork and execute a command specified in a subprocess.
62 // If there is a runtime (Runtime::Current != nullptr) then the subprocess is created with the
63 // same environment that existed when the runtime was started.
64 // Returns the process id of the child process on success, -1 otherwise.
ExecWithoutWait(const std::vector<std::string> & arg_vector,bool new_process_group,std::string * error_msg)65 pid_t ExecWithoutWait(const std::vector<std::string>& arg_vector,
66 bool new_process_group,
67 std::string* error_msg) {
68 // Convert the args to char pointers.
69 const char* program = arg_vector[0].c_str();
70 std::vector<char*> args;
71 args.reserve(arg_vector.size() + 1);
72 for (const auto& arg : arg_vector) {
73 args.push_back(const_cast<char*>(arg.c_str()));
74 }
75 args.push_back(nullptr);
76
77 // fork and exec
78 pid_t pid = fork();
79 if (pid == 0) {
80 // no allocation allowed between fork and exec
81
82 if (new_process_group) {
83 setpgid(0, 0);
84 }
85
86 // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
87 // Use the snapshot of the environment from the time the runtime was created.
88 char** envp = (Runtime::Current() == nullptr) ? nullptr : Runtime::Current()->GetEnvSnapshot();
89 if (envp == nullptr) {
90 execv(program, &args[0]);
91 } else {
92 execve(program, &args[0], envp);
93 }
94 // This should be regarded as a crash rather than a normal return.
95 PLOG(FATAL) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
96 UNREACHABLE();
97 } else if (pid == -1) {
98 *error_msg = StringPrintf("Failed to execute (%s) because fork failed: %s",
99 ToCommandLine(arg_vector).c_str(),
100 strerror(errno));
101 return -1;
102 } else {
103 return pid;
104 }
105 }
106
WaitChild(pid_t pid,const std::vector<std::string> & arg_vector,bool no_wait,std::string * error_msg)107 ExecResult WaitChild(pid_t pid,
108 const std::vector<std::string>& arg_vector,
109 bool no_wait,
110 std::string* error_msg) {
111 siginfo_t info;
112 // WNOWAIT leaves the child in a waitable state. The call is still blocking.
113 int options = WEXITED | (no_wait ? WNOWAIT : 0);
114 if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, options)) != 0) {
115 *error_msg = StringPrintf("waitid failed for (%s) pid %d: %s",
116 ToCommandLine(arg_vector).c_str(),
117 pid,
118 strerror(errno));
119 return {.status = ExecResult::kUnknown};
120 }
121 if (info.si_pid != pid) {
122 *error_msg = StringPrintf("waitid failed for (%s): wanted pid %d, got %d: %s",
123 ToCommandLine(arg_vector).c_str(),
124 pid,
125 info.si_pid,
126 strerror(errno));
127 return {.status = ExecResult::kUnknown};
128 }
129 if (info.si_code != CLD_EXITED) {
130 *error_msg =
131 StringPrintf("Failed to execute (%s) because the child process is terminated by signal %d",
132 ToCommandLine(arg_vector).c_str(),
133 info.si_status);
134 return {.status = ExecResult::kSignaled, .signal = info.si_status};
135 }
136 return {.status = ExecResult::kExited, .exit_code = info.si_status};
137 }
138
139 // A fallback implementation of `WaitChildWithTimeout` that creates a thread to wait instead of
140 // relying on `pidfd_open`.
WaitChildWithTimeoutFallback(pid_t pid,const std::vector<std::string> & arg_vector,int timeout_ms,std::string * error_msg)141 ExecResult WaitChildWithTimeoutFallback(pid_t pid,
142 const std::vector<std::string>& arg_vector,
143 int timeout_ms,
144 std::string* error_msg) {
145 bool child_exited = false;
146 bool timed_out = false;
147 std::condition_variable cv;
148 std::mutex m;
149
150 std::thread wait_thread([&]() {
151 std::unique_lock<std::mutex> lock(m);
152 if (!cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [&] { return child_exited; })) {
153 timed_out = true;
154 kill(pid, SIGKILL);
155 }
156 });
157
158 ExecResult result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
159
160 {
161 std::unique_lock<std::mutex> lock(m);
162 child_exited = true;
163 }
164 cv.notify_all();
165 wait_thread.join();
166
167 // The timeout error should have a higher priority than any other error.
168 if (timed_out) {
169 *error_msg =
170 StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
171 ToCommandLine(arg_vector).c_str(),
172 timeout_ms);
173 return ExecResult{.status = ExecResult::kTimedOut};
174 }
175
176 return result;
177 }
178
179 // Waits for the child process to finish and leaves the child in a waitable state.
WaitChildWithTimeout(pid_t pid,unique_fd pidfd,const std::vector<std::string> & arg_vector,int timeout_ms,std::string * error_msg)180 ExecResult WaitChildWithTimeout(pid_t pid,
181 unique_fd pidfd,
182 const std::vector<std::string>& arg_vector,
183 int timeout_ms,
184 std::string* error_msg) {
185 auto cleanup = android::base::make_scope_guard([&]() {
186 kill(pid, SIGKILL);
187 std::string ignored_error_msg;
188 WaitChild(pid, arg_vector, /*no_wait=*/true, &ignored_error_msg);
189 });
190
191 struct pollfd pfd;
192 pfd.fd = pidfd.get();
193 pfd.events = POLLIN;
194 int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms));
195
196 pidfd.reset();
197
198 if (poll_ret < 0) {
199 *error_msg = StringPrintf("poll failed for pid %d: %s", pid, strerror(errno));
200 return {.status = ExecResult::kUnknown};
201 }
202 if (poll_ret == 0) {
203 *error_msg =
204 StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
205 ToCommandLine(arg_vector).c_str(),
206 timeout_ms);
207 return {.status = ExecResult::kTimedOut};
208 }
209
210 cleanup.Disable();
211 return WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
212 }
213
ParseProcStat(const std::string & stat_content,int64_t uptime_ms,int64_t ticks_per_sec,ProcessStat * stat)214 bool ParseProcStat(const std::string& stat_content,
215 int64_t uptime_ms,
216 int64_t ticks_per_sec,
217 /*out*/ ProcessStat* stat) {
218 size_t pos = stat_content.rfind(") ");
219 if (pos == std::string::npos) {
220 return false;
221 }
222 std::vector<std::string> stat_fields;
223 // Skip the first two fields. The second field is the parenthesized process filename, which can
224 // contain anything, including spaces.
225 Split(std::string_view(stat_content).substr(pos + 2), ' ', &stat_fields);
226 constexpr int kSkippedFields = 2;
227 int64_t utime, stime, cutime, cstime, starttime;
228 if (stat_fields.size() < 22 - kSkippedFields ||
229 !ParseInt(stat_fields[13 - kSkippedFields], &utime) ||
230 !ParseInt(stat_fields[14 - kSkippedFields], &stime) ||
231 !ParseInt(stat_fields[15 - kSkippedFields], &cutime) ||
232 !ParseInt(stat_fields[16 - kSkippedFields], &cstime) ||
233 !ParseInt(stat_fields[21 - kSkippedFields], &starttime)) {
234 return false;
235 }
236 if (starttime == 0) {
237 // The start time is the time the process started after system boot, so it's not supposed to be
238 // zero unless the process is `init`.
239 return false;
240 }
241 stat->cpu_time_ms = (utime + stime + cutime + cstime) * 1000 / ticks_per_sec;
242 stat->wall_time_ms = uptime_ms - starttime * 1000 / ticks_per_sec;
243 return true;
244 }
245
246 } // namespace
247
ExecAndReturnCode(const std::vector<std::string> & arg_vector,std::string * error_msg) const248 int ExecUtils::ExecAndReturnCode(const std::vector<std::string>& arg_vector,
249 std::string* error_msg) const {
250 return ExecAndReturnResult(arg_vector, /*timeout_sec=*/-1, error_msg).exit_code;
251 }
252
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int timeout_sec,std::string * error_msg) const253 ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
254 int timeout_sec,
255 std::string* error_msg) const {
256 return ExecAndReturnResult(arg_vector,
257 timeout_sec,
258 ExecCallbacks(),
259 /*new_process_group=*/false,
260 /*stat=*/nullptr,
261 error_msg);
262 }
263
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int timeout_sec,const ExecCallbacks & callbacks,bool new_process_group,ProcessStat * stat,std::string * error_msg) const264 ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
265 int timeout_sec,
266 const ExecCallbacks& callbacks,
267 bool new_process_group,
268 /*out*/ ProcessStat* stat,
269 /*out*/ std::string* error_msg) const {
270 if (timeout_sec > INT_MAX / 1000) {
271 *error_msg = "Timeout too large";
272 return {.status = ExecResult::kStartFailed};
273 }
274
275 // Start subprocess.
276 pid_t pid = ExecWithoutWait(arg_vector, new_process_group, error_msg);
277 if (pid == -1) {
278 return {.status = ExecResult::kStartFailed};
279 }
280
281 callbacks.on_start(pid);
282
283 // Wait for subprocess to finish.
284 ExecResult result;
285 if (timeout_sec >= 0) {
286 unique_fd pidfd = PidfdOpen(pid);
287 if (pidfd.get() >= 0) {
288 result =
289 WaitChildWithTimeout(pid, std::move(pidfd), arg_vector, timeout_sec * 1000, error_msg);
290 } else {
291 LOG(DEBUG) << StringPrintf(
292 "pidfd_open failed for pid %d: %s, falling back", pid, strerror(errno));
293 result = WaitChildWithTimeoutFallback(pid, arg_vector, timeout_sec * 1000, error_msg);
294 }
295 } else {
296 result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
297 }
298
299 if (stat != nullptr) {
300 std::string local_error_msg;
301 if (!GetStat(pid, stat, &local_error_msg)) {
302 LOG(ERROR) << "Failed to get process stat: " << local_error_msg;
303 }
304 }
305
306 callbacks.on_end(pid);
307
308 std::string local_error_msg;
309 // TODO(jiakaiz): Use better logic to detect waitid failure.
310 if (WaitChild(pid, arg_vector, /*no_wait=*/false, &local_error_msg).status ==
311 ExecResult::kUnknown) {
312 LOG(ERROR) << "Failed to clean up child process '" << arg_vector[0] << "': " << local_error_msg;
313 }
314
315 return result;
316 }
317
Exec(const std::vector<std::string> & arg_vector,std::string * error_msg) const318 bool ExecUtils::Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) const {
319 int status = ExecAndReturnCode(arg_vector, error_msg);
320 if (status < 0) {
321 // Internal error. The error message is already set.
322 return false;
323 }
324 if (status > 0) {
325 *error_msg =
326 StringPrintf("Failed to execute (%s) because the child process returns non-zero exit code",
327 ToCommandLine(arg_vector).c_str());
328 return false;
329 }
330 return true;
331 }
332
PidfdOpen(pid_t pid) const333 unique_fd ExecUtils::PidfdOpen(pid_t pid) const { return art::PidfdOpen(pid, /*flags=*/0); }
334
GetProcStat(pid_t pid) const335 std::string ExecUtils::GetProcStat(pid_t pid) const {
336 std::string stat_content;
337 if (!ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat_content)) {
338 stat_content = "";
339 }
340 return stat_content;
341 }
342
GetUptimeMs(std::string * error_msg) const343 std::optional<int64_t> ExecUtils::GetUptimeMs(std::string* error_msg) const {
344 timespec t;
345 if (clock_gettime(CLOCK_MONOTONIC, &t) != 0) {
346 *error_msg = ART_FORMAT("Failed to get uptime: {}", strerror(errno));
347 return std::nullopt;
348 }
349 return t.tv_sec * 1000 + t.tv_nsec / 1000000;
350 }
351
GetTicksPerSec() const352 int64_t ExecUtils::GetTicksPerSec() const { return sysconf(_SC_CLK_TCK); }
353
GetStat(pid_t pid,ProcessStat * stat,std::string * error_msg) const354 bool ExecUtils::GetStat(pid_t pid,
355 /*out*/ ProcessStat* stat,
356 /*out*/ std::string* error_msg) const {
357 std::optional<int64_t> uptime_ms = GetUptimeMs(error_msg);
358 if (!uptime_ms.has_value()) {
359 return false;
360 }
361 std::string stat_content = GetProcStat(pid);
362 if (stat_content.empty()) {
363 *error_msg = StringPrintf("Failed to read /proc/%d/stat: %s", pid, strerror(errno));
364 return false;
365 }
366 int64_t ticks_per_sec = GetTicksPerSec();
367 if (!ParseProcStat(stat_content, *uptime_ms, ticks_per_sec, stat)) {
368 *error_msg = StringPrintf("Failed to parse /proc/%d/stat '%s'", pid, stat_content.c_str());
369 return false;
370 }
371 return true;
372 }
373
374 } // namespace art
375