• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <ctime>
25 #include <string_view>
26 
27 #ifdef __BIONIC__
28 #include <sys/pidfd.h>
29 #endif
30 
31 #include <chrono>
32 #include <climits>
33 #include <condition_variable>
34 #include <cstdint>
35 #include <mutex>
36 #include <string>
37 #include <thread>
38 #include <vector>
39 
40 #include "android-base/file.h"
41 #include "android-base/parseint.h"
42 #include "android-base/scopeguard.h"
43 #include "android-base/stringprintf.h"
44 #include "android-base/strings.h"
45 #include "android-base/unique_fd.h"
46 #include "base/macros.h"
47 #include "base/utils.h"
48 #include "runtime.h"
49 
50 namespace art {
51 
52 namespace {
53 
54 using ::android::base::ParseInt;
55 using ::android::base::ReadFileToString;
56 using ::android::base::StringPrintf;
57 using ::android::base::unique_fd;
58 
ToCommandLine(const std::vector<std::string> & args)59 std::string ToCommandLine(const std::vector<std::string>& args) {
60   return android::base::Join(args, ' ');
61 }
62 
63 // Fork and execute a command specified in a subprocess.
64 // If there is a runtime (Runtime::Current != nullptr) then the subprocess is created with the
65 // same environment that existed when the runtime was started.
66 // Returns the process id of the child process on success, -1 otherwise.
ExecWithoutWait(const std::vector<std::string> & arg_vector,std::string * error_msg)67 pid_t ExecWithoutWait(const std::vector<std::string>& arg_vector, 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     // change process groups, so we don't get reaped by ProcessManager
83     setpgid(0, 0);
84 
85     // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
86     // Use the snapshot of the environment from the time the runtime was created.
87     char** envp = (Runtime::Current() == nullptr) ? nullptr : Runtime::Current()->GetEnvSnapshot();
88     if (envp == nullptr) {
89       execv(program, &args[0]);
90     } else {
91       execve(program, &args[0], envp);
92     }
93     // This should be regarded as a crash rather than a normal return.
94     PLOG(FATAL) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
95     UNREACHABLE();
96   } else if (pid == -1) {
97     *error_msg = StringPrintf("Failed to execute (%s) because fork failed: %s",
98                               ToCommandLine(arg_vector).c_str(),
99                               strerror(errno));
100     return -1;
101   } else {
102     return pid;
103   }
104 }
105 
WaitChild(pid_t pid,const std::vector<std::string> & arg_vector,bool no_wait,std::string * error_msg)106 ExecResult WaitChild(pid_t pid,
107                      const std::vector<std::string>& arg_vector,
108                      bool no_wait,
109                      std::string* error_msg) {
110   siginfo_t info;
111   // WNOWAIT leaves the child in a waitable state. The call is still blocking.
112   int options = WEXITED | (no_wait ? WNOWAIT : 0);
113   if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, options)) != 0) {
114     *error_msg = StringPrintf("waitid failed for (%s) pid %d: %s",
115                               ToCommandLine(arg_vector).c_str(),
116                               pid,
117                               strerror(errno));
118     return {.status = ExecResult::kUnknown};
119   }
120   if (info.si_pid != pid) {
121     *error_msg = StringPrintf("waitid failed for (%s): wanted pid %d, got %d: %s",
122                               ToCommandLine(arg_vector).c_str(),
123                               pid,
124                               info.si_pid,
125                               strerror(errno));
126     return {.status = ExecResult::kUnknown};
127   }
128   if (info.si_code != CLD_EXITED) {
129     *error_msg =
130         StringPrintf("Failed to execute (%s) because the child process is terminated by signal %d",
131                      ToCommandLine(arg_vector).c_str(),
132                      info.si_status);
133     return {.status = ExecResult::kSignaled, .signal = info.si_status};
134   }
135   return {.status = ExecResult::kExited, .exit_code = info.si_status};
136 }
137 
138 // A fallback implementation of `WaitChildWithTimeout` that creates a thread to wait instead of
139 // relying on `pidfd_open`.
WaitChildWithTimeoutFallback(pid_t pid,const std::vector<std::string> & arg_vector,int timeout_ms,std::string * error_msg)140 ExecResult WaitChildWithTimeoutFallback(pid_t pid,
141                                         const std::vector<std::string>& arg_vector,
142                                         int timeout_ms,
143                                         std::string* error_msg) {
144   bool child_exited = false;
145   bool timed_out = false;
146   std::condition_variable cv;
147   std::mutex m;
148 
149   std::thread wait_thread([&]() {
150     std::unique_lock<std::mutex> lock(m);
151     if (!cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [&] { return child_exited; })) {
152       timed_out = true;
153       kill(pid, SIGKILL);
154     }
155   });
156 
157   ExecResult result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
158 
159   {
160     std::unique_lock<std::mutex> lock(m);
161     child_exited = true;
162   }
163   cv.notify_all();
164   wait_thread.join();
165 
166   // The timeout error should have a higher priority than any other error.
167   if (timed_out) {
168     *error_msg =
169         StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
170                      ToCommandLine(arg_vector).c_str(),
171                      timeout_ms);
172     return ExecResult{.status = ExecResult::kTimedOut};
173   }
174 
175   return result;
176 }
177 
178 // 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)179 ExecResult WaitChildWithTimeout(pid_t pid,
180                                 unique_fd pidfd,
181                                 const std::vector<std::string>& arg_vector,
182                                 int timeout_ms,
183                                 std::string* error_msg) {
184   auto cleanup = android::base::make_scope_guard([&]() {
185     kill(pid, SIGKILL);
186     std::string ignored_error_msg;
187     WaitChild(pid, arg_vector, /*no_wait=*/true, &ignored_error_msg);
188   });
189 
190   struct pollfd pfd;
191   pfd.fd = pidfd.get();
192   pfd.events = POLLIN;
193   int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms));
194 
195   pidfd.reset();
196 
197   if (poll_ret < 0) {
198     *error_msg = StringPrintf("poll failed for pid %d: %s", pid, strerror(errno));
199     return {.status = ExecResult::kUnknown};
200   }
201   if (poll_ret == 0) {
202     *error_msg =
203         StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
204                      ToCommandLine(arg_vector).c_str(),
205                      timeout_ms);
206     return {.status = ExecResult::kTimedOut};
207   }
208 
209   cleanup.Disable();
210   return WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
211 }
212 
ParseProcStat(const std::string & stat_content,int64_t uptime_ms,int64_t ticks_per_sec,ProcessStat * stat)213 bool ParseProcStat(const std::string& stat_content,
214                    int64_t uptime_ms,
215                    int64_t ticks_per_sec,
216                    /*out*/ ProcessStat* stat) {
217   size_t pos = stat_content.rfind(") ");
218   if (pos == std::string::npos) {
219     return false;
220   }
221   std::vector<std::string> stat_fields;
222   // Skip the first two fields. The second field is the parenthesized process filename, which can
223   // contain anything, including spaces.
224   Split(std::string_view(stat_content).substr(pos + 2), ' ', &stat_fields);
225   constexpr int kSkippedFields = 2;
226   int64_t utime, stime, cutime, cstime, starttime;
227   if (stat_fields.size() < 22 - kSkippedFields ||
228       !ParseInt(stat_fields[13 - kSkippedFields], &utime) ||
229       !ParseInt(stat_fields[14 - kSkippedFields], &stime) ||
230       !ParseInt(stat_fields[15 - kSkippedFields], &cutime) ||
231       !ParseInt(stat_fields[16 - kSkippedFields], &cstime) ||
232       !ParseInt(stat_fields[21 - kSkippedFields], &starttime)) {
233     return false;
234   }
235   stat->cpu_time_ms = (utime + stime + cutime + cstime) * 1000 / ticks_per_sec;
236   stat->wall_time_ms = uptime_ms - starttime * 1000 / ticks_per_sec;
237   return true;
238 }
239 
240 }  // namespace
241 
ExecAndReturnCode(const std::vector<std::string> & arg_vector,std::string * error_msg) const242 int ExecUtils::ExecAndReturnCode(const std::vector<std::string>& arg_vector,
243                                  std::string* error_msg) const {
244   return ExecAndReturnResult(arg_vector, /*timeout_sec=*/-1, error_msg).exit_code;
245 }
246 
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int timeout_sec,std::string * error_msg) const247 ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
248                                           int timeout_sec,
249                                           std::string* error_msg) const {
250   return ExecAndReturnResult(arg_vector, timeout_sec, ExecCallbacks(), /*stat=*/nullptr, error_msg);
251 }
252 
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int timeout_sec,const ExecCallbacks & callbacks,ProcessStat * stat,std::string * error_msg) const253 ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
254                                           int timeout_sec,
255                                           const ExecCallbacks& callbacks,
256                                           /*out*/ ProcessStat* stat,
257                                           /*out*/ std::string* error_msg) const {
258   if (timeout_sec > INT_MAX / 1000) {
259     *error_msg = "Timeout too large";
260     return {.status = ExecResult::kStartFailed};
261   }
262 
263   // Start subprocess.
264   pid_t pid = ExecWithoutWait(arg_vector, error_msg);
265   if (pid == -1) {
266     return {.status = ExecResult::kStartFailed};
267   }
268 
269   callbacks.on_start(pid);
270 
271   // Wait for subprocess to finish.
272   ExecResult result;
273   if (timeout_sec >= 0) {
274     unique_fd pidfd = PidfdOpen(pid);
275     if (pidfd.get() >= 0) {
276       result =
277           WaitChildWithTimeout(pid, std::move(pidfd), arg_vector, timeout_sec * 1000, error_msg);
278     } else {
279       LOG(DEBUG) << StringPrintf(
280           "pidfd_open failed for pid %d: %s, falling back", pid, strerror(errno));
281       result = WaitChildWithTimeoutFallback(pid, arg_vector, timeout_sec * 1000, error_msg);
282     }
283   } else {
284     result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
285   }
286 
287   if (stat != nullptr) {
288     std::string local_error_msg;
289     if (!GetStat(pid, stat, &local_error_msg)) {
290       LOG(ERROR) << "Failed to get process stat: " << local_error_msg;
291     }
292   }
293 
294   callbacks.on_end(pid);
295 
296   std::string local_error_msg;
297   // TODO(jiakaiz): Use better logic to detect waitid failure.
298   if (WaitChild(pid, arg_vector, /*no_wait=*/false, &local_error_msg).status ==
299       ExecResult::kUnknown) {
300     LOG(ERROR) << "Failed to clean up child process '" << arg_vector[0] << "': " << local_error_msg;
301   }
302 
303   return result;
304 }
305 
Exec(const std::vector<std::string> & arg_vector,std::string * error_msg) const306 bool ExecUtils::Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) const {
307   int status = ExecAndReturnCode(arg_vector, error_msg);
308   if (status < 0) {
309     // Internal error. The error message is already set.
310     return false;
311   }
312   if (status > 0) {
313     *error_msg =
314         StringPrintf("Failed to execute (%s) because the child process returns non-zero exit code",
315                      ToCommandLine(arg_vector).c_str());
316     return false;
317   }
318   return true;
319 }
320 
PidfdOpen(pid_t pid) const321 unique_fd ExecUtils::PidfdOpen(pid_t pid) const {
322 #ifdef __BIONIC__
323   return unique_fd(pidfd_open(pid, /*flags=*/0));
324 #else
325   // There is no glibc wrapper for pidfd_open.
326 #ifndef SYS_pidfd_open
327   constexpr int SYS_pidfd_open = 434;
328 #endif
329   return unique_fd(syscall(SYS_pidfd_open, pid, /*flags=*/0));
330 #endif
331 }
332 
GetProcStat(pid_t pid) const333 std::string ExecUtils::GetProcStat(pid_t pid) const {
334   std::string stat_content;
335   if (!ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat_content)) {
336     stat_content = "";
337   }
338   return stat_content;
339 }
340 
GetUptimeMs() const341 int64_t ExecUtils::GetUptimeMs() const {
342   timespec t;
343   clock_gettime(CLOCK_MONOTONIC, &t);
344   return t.tv_sec * 1000 + t.tv_nsec / 1000000;
345 }
346 
GetTicksPerSec() const347 int64_t ExecUtils::GetTicksPerSec() const { return sysconf(_SC_CLK_TCK); }
348 
GetStat(pid_t pid,ProcessStat * stat,std::string * error_msg) const349 bool ExecUtils::GetStat(pid_t pid,
350                         /*out*/ ProcessStat* stat,
351                         /*out*/ std::string* error_msg) const {
352   int64_t uptime_ms = GetUptimeMs();
353   std::string stat_content = GetProcStat(pid);
354   if (stat_content.empty()) {
355     *error_msg = StringPrintf("Failed to read /proc/%d/stat: %s", pid, strerror(errno));
356     return false;
357   }
358   int64_t ticks_per_sec = GetTicksPerSec();
359   if (!ParseProcStat(stat_content, uptime_ms, ticks_per_sec, stat)) {
360     *error_msg = StringPrintf("Failed to parse /proc/%d/stat '%s'", pid, stat_content.c_str());
361     return false;
362   }
363   return true;
364 }
365 
366 }  // namespace art
367