// Fork and execute a command specified in a subprocess. // If there is a runtime (Runtime::Current != nullptr) then the subprocess is created with the // same environment that existed when the runtime was started. // Returns the process id of the child process on success, -1 otherwise.
pid_t ExecWithoutWait(const std::vector<std::string>& arg_vector, bool new_process_group,
std::string* error_msg) { // Convert the args to char pointers. constchar* program = arg_vector[0].c_str();
std::vector<char*> args;
args.reserve(arg_vector.size() + 1); for (constauto& arg : arg_vector) {
args.push_back(const_cast<char*>(arg.c_str()));
}
args.push_back(nullptr);
// fork and exec
pid_t pid = fork(); if (pid == 0) { // no allocation allowed between fork and exec
if (new_process_group) {
setpgid(0, 0);
}
// (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Use the snapshot of the environment from the time the runtime was created. char** envp = (Runtime::Current() == nullptr) ? nullptr : Runtime::Current()->GetEnvSnapshot(); if (envp == nullptr) {
execv(program, &args[0]);
} else {
execve(program, &args[0], envp);
} // This should be regarded as a crash rather than a normal return.
PLOG(FATAL) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
UNREACHABLE();
} elseif (pid == -1) {
*error_msg = StringPrintf("Failed to execute (%s) because fork failed: %s",
ToCommandLine(arg_vector).c_str(),
strerror(errno)); return -1;
} else { return pid;
}
}
ExecResult WaitChild(pid_t pid, const std::vector<std::string>& arg_vector, bool no_wait,
std::string* error_msg) {
siginfo_t info; // WNOWAIT leaves the child in a waitable state. The call is still blocking. int options = WEXITED | (no_wait ? WNOWAIT : 0); if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, options)) != 0) {
*error_msg = StringPrintf("waitid failed for (%s) pid %d: %s",
ToCommandLine(arg_vector).c_str(),
pid,
strerror(errno)); return {.status = ExecResult::kUnknown};
} if (info.si_pid != pid) {
*error_msg = StringPrintf("waitid failed for (%s): wanted pid %d, got %d: %s",
ToCommandLine(arg_vector).c_str(),
pid,
info.si_pid,
strerror(errno)); return {.status = ExecResult::kUnknown};
} if (info.si_code != CLD_EXITED) {
*error_msg =
StringPrintf("Failed to execute (%s) because the child process is terminated by signal %d",
ToCommandLine(arg_vector).c_str(),
info.si_status); return {.status = ExecResult::kSignaled, .signal = info.si_status};
} return {.status = ExecResult::kExited, .exit_code = info.si_status};
}
// A fallback implementation of `WaitChildWithTimeout` that creates a thread to wait instead of // relying on `pidfd_open`.
ExecResult WaitChildWithTimeoutFallback(pid_t pid, const std::vector<std::string>& arg_vector, int timeout_ms,
std::string* error_msg) { bool child_exited = false; bool timed_out = false;
std::condition_variable cv;
std::mutex m;
// The timeout error should have a higher priority than any other error. if (timed_out) {
*error_msg =
StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
ToCommandLine(arg_vector).c_str(),
timeout_ms); return ExecResult{.status = ExecResult::kTimedOut};
}
return result;
}
// Waits for the child process to finish and leaves the child in a waitable state.
ExecResult WaitChildWithTimeout(pid_t pid,
unique_fd pidfd, const std::vector<std::string>& arg_vector, int timeout_ms,
std::string* error_msg) { auto cleanup = android::base::make_scope_guard([&]() {
kill(pid, SIGKILL);
std::string ignored_error_msg;
WaitChild(pid, arg_vector, /*no_wait=*/true, &ignored_error_msg);
});
// Wait for subprocess to finish.
ExecResult result; if (timeout_sec >= 0) {
unique_fd pidfd = PidfdOpen(pid); if (pidfd.get() >= 0) {
result =
WaitChildWithTimeout(pid, std::move(pidfd), arg_vector, timeout_sec * 1000, error_msg);
} else {
LOG(DEBUG) << StringPrintf( "pidfd_open failed for pid %d: %s, falling back", pid, strerror(errno));
result = WaitChildWithTimeoutFallback(pid, arg_vector, timeout_sec * 1000, error_msg);
}
} else {
result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
}
if (stat != nullptr) { if (!start_time.has_value() || !GetStat(pid, start_time.value(), stat, &stat_error_msg)) {
LOG(ERROR) << "Failed to get process stat: " << stat_error_msg;
}
}
callbacks.on_end(pid);
std::string local_error_msg; // TODO(jiakaiz): Use better logic to detect waitid failure. if (WaitChild(pid, arg_vector, /*no_wait=*/false, &local_error_msg).status ==
ExecResult::kUnknown) {
LOG(ERROR) << "Failed to clean up child process '" << arg_vector[0] << "': " << local_error_msg;
}
return result;
}
bool ExecUtils::Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) const { int status = ExecAndReturnCode(arg_vector, error_msg); if (status < 0) { // Internal error. The error message is already set. returnfalse;
} if (status > 0) {
*error_msg =
StringPrintf("Failed to execute (%s) because the child process returns non-zero exit code",
ToCommandLine(arg_vector).c_str()); returnfalse;
} returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.