| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2021 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 "UtilsHost.h" | 
|  | 18 |  | 
|  | 19 | #include <poll.h> | 
|  | 20 | #include <string.h> | 
|  | 21 | #include <sys/types.h> | 
|  | 22 | #include <sys/wait.h> | 
|  | 23 |  | 
|  | 24 | #include <sstream> | 
|  | 25 |  | 
|  | 26 | #include <log/log.h> | 
|  | 27 |  | 
|  | 28 | namespace android { | 
|  | 29 |  | 
|  | 30 | CommandResult::~CommandResult() { | 
|  | 31 | if (!pid.has_value()) return; | 
|  | 32 | if (*pid == 0) { | 
|  | 33 | ALOGW("%s: PID is unexpectedly 0, won't kill it", __PRETTY_FUNCTION__); | 
|  | 34 | return; | 
|  | 35 | } | 
|  | 36 |  | 
|  | 37 | ALOGE_IF(kill(*pid, SIGKILL) != 0, "kill(%d): %s", *pid, strerror(errno)); | 
|  | 38 |  | 
|  | 39 | while (pid.has_value()) { | 
|  | 40 | int status; | 
|  | 41 | LOG_HOST("%s: Waiting for PID %d to exit.", __PRETTY_FUNCTION__, *pid); | 
|  | 42 | int waitres = waitpid(*pid, &status, 0); | 
|  | 43 | if (waitres == -1) { | 
|  | 44 | ALOGE("%s: waitpid(%d): %s", __PRETTY_FUNCTION__, *pid, strerror(errno)); | 
|  | 45 | break; | 
|  | 46 | } | 
|  | 47 | if (WIFEXITED(status)) { | 
|  | 48 | LOG_HOST("%s: PID %d exited.", __PRETTY_FUNCTION__, *pid); | 
|  | 49 | pid.reset(); | 
|  | 50 | } else if (WIFSIGNALED(status)) { | 
|  | 51 | LOG_HOST("%s: PID %d terminated by signal %d.", __PRETTY_FUNCTION__, *pid, | 
|  | 52 | WTERMSIG(status)); | 
|  | 53 | pid.reset(); | 
|  | 54 | } else if (WIFSTOPPED(status)) { | 
|  | 55 | ALOGW("%s: pid %d stopped", __PRETTY_FUNCTION__, *pid); | 
|  | 56 | } else if (WIFCONTINUED(status)) { | 
|  | 57 | ALOGW("%s: pid %d continued", __PRETTY_FUNCTION__, *pid); | 
|  | 58 | } | 
|  | 59 | } | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | std::ostream& operator<<(std::ostream& os, const CommandResult& res) { | 
|  | 63 | if (res.exitCode) os << "code=" << *res.exitCode; | 
|  | 64 | if (res.signal) os << "signal=" << *res.signal; | 
|  | 65 | if (res.pid) os << ", pid=" << *res.pid; | 
| Colin Cross | c9a77aa | 2021-09-13 16:31:38 -0700 | [diff] [blame] | 66 | return os << ", stdout=" << res.stdoutStr << ", stderr=" << res.stderrStr; | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 67 | } | 
|  | 68 |  | 
|  | 69 | std::string CommandResult::toString() const { | 
|  | 70 | std::stringstream ss; | 
|  | 71 | ss << (*this); | 
|  | 72 | return ss.str(); | 
|  | 73 | } | 
|  | 74 |  | 
|  | 75 | android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec, | 
|  | 76 | const std::function<bool(const CommandResult&)>& end) { | 
|  | 77 | // turn vector<string> into null-terminated char* vector. | 
|  | 78 | std::vector<char*> argv; | 
|  | 79 | argv.reserve(argStringVec.size() + 1); | 
|  | 80 | for (auto& arg : argStringVec) argv.push_back(arg.data()); | 
|  | 81 | argv.push_back(nullptr); | 
|  | 82 |  | 
|  | 83 | CommandResult ret; | 
|  | 84 | android::base::unique_fd outWrite; | 
|  | 85 | if (!android::base::Pipe(&ret.outPipe, &outWrite)) | 
|  | 86 | return android::base::ErrnoError() << "pipe() for outPipe"; | 
|  | 87 | android::base::unique_fd errWrite; | 
|  | 88 | if (!android::base::Pipe(&ret.errPipe, &errWrite)) | 
|  | 89 | return android::base::ErrnoError() << "pipe() for errPipe"; | 
|  | 90 |  | 
|  | 91 | int pid = fork(); | 
|  | 92 | if (pid == -1) return android::base::ErrnoError() << "fork()"; | 
|  | 93 | if (pid == 0) { | 
|  | 94 | // child | 
|  | 95 | ret.outPipe.reset(); | 
|  | 96 | ret.errPipe.reset(); | 
|  | 97 |  | 
|  | 98 | int res = TEMP_FAILURE_RETRY(dup2(outWrite.get(), STDOUT_FILENO)); | 
|  | 99 | LOG_ALWAYS_FATAL_IF(-1 == res, "dup2(outPipe): %s", strerror(errno)); | 
|  | 100 | outWrite.reset(); | 
|  | 101 |  | 
|  | 102 | res = TEMP_FAILURE_RETRY(dup2(errWrite.get(), STDERR_FILENO)); | 
|  | 103 | LOG_ALWAYS_FATAL_IF(-1 == res, "dup2(errPipe): %s", strerror(errno)); | 
|  | 104 | errWrite.reset(); | 
|  | 105 |  | 
|  | 106 | execvp(argv[0], argv.data()); | 
|  | 107 | LOG_ALWAYS_FATAL("execvp() returns"); | 
|  | 108 | } | 
|  | 109 | // parent | 
|  | 110 | outWrite.reset(); | 
|  | 111 | errWrite.reset(); | 
|  | 112 | ret.pid = pid; | 
|  | 113 |  | 
| George Burgess IV | a419830 | 2021-07-19 07:31:21 +0000 | [diff] [blame] | 114 | auto handlePoll = [](android::base::unique_fd* fd, const pollfd* pfd, std::string* s) { | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 115 | if (!fd->ok()) return true; | 
| George Burgess IV | a419830 | 2021-07-19 07:31:21 +0000 | [diff] [blame] | 116 | if (pfd->revents & POLLIN) { | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 117 | char buf[1024]; | 
|  | 118 | ssize_t n = TEMP_FAILURE_RETRY(read(fd->get(), buf, sizeof(buf))); | 
|  | 119 | if (n < 0) return false; | 
|  | 120 | if (n > 0) *s += std::string_view(buf, n); | 
|  | 121 | } | 
| George Burgess IV | a419830 | 2021-07-19 07:31:21 +0000 | [diff] [blame] | 122 | if (pfd->revents & POLLHUP) { | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 123 | fd->reset(); | 
|  | 124 | } | 
|  | 125 | return true; | 
|  | 126 | }; | 
|  | 127 |  | 
|  | 128 | // Drain both stdout and stderr. Check end() regularly until both are closed. | 
|  | 129 | while (ret.outPipe.ok() || ret.errPipe.ok()) { | 
|  | 130 | pollfd fds[2]; | 
|  | 131 | pollfd *outPollFd = nullptr, *errPollFd = nullptr; | 
|  | 132 | memset(fds, 0, sizeof(fds)); | 
|  | 133 | nfds_t nfds = 0; | 
|  | 134 | if (ret.outPipe.ok()) { | 
|  | 135 | outPollFd = &fds[nfds++]; | 
|  | 136 | *outPollFd = {.fd = ret.outPipe.get(), .events = POLLIN}; | 
|  | 137 | } | 
|  | 138 | if (ret.errPipe.ok()) { | 
|  | 139 | errPollFd = &fds[nfds++]; | 
|  | 140 | *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN}; | 
|  | 141 | } | 
|  | 142 | int pollRet = poll(fds, nfds, 1000 /* ms timeout */); | 
|  | 143 | if (pollRet == -1) return android::base::ErrnoError() << "poll()"; | 
|  | 144 |  | 
| Colin Cross | c9a77aa | 2021-09-13 16:31:38 -0700 | [diff] [blame] | 145 | if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 146 | return android::base::ErrnoError() << "read(stdout)"; | 
| Colin Cross | c9a77aa | 2021-09-13 16:31:38 -0700 | [diff] [blame] | 147 | if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) | 
| Yifan Hong | af766e6 | 2021-06-14 13:24:19 -0700 | [diff] [blame] | 148 | return android::base::ErrnoError() << "read(stderr)"; | 
|  | 149 |  | 
|  | 150 | if (end && end(ret)) return ret; | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | // If both stdout and stderr are closed by the subprocess, it may or may not be terminated. | 
|  | 154 | while (ret.pid.has_value()) { | 
|  | 155 | int status; | 
|  | 156 | auto exitPid = waitpid(pid, &status, 0); | 
|  | 157 | if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")"; | 
|  | 158 | if (exitPid == pid) { | 
|  | 159 | if (WIFEXITED(status)) { | 
|  | 160 | ret.pid = std::nullopt; | 
|  | 161 | ret.exitCode = WEXITSTATUS(status); | 
|  | 162 | } else if (WIFSIGNALED(status)) { | 
|  | 163 | ret.pid = std::nullopt; | 
|  | 164 | ret.signal = WTERMSIG(status); | 
|  | 165 | } else if (WIFSTOPPED(status)) { | 
|  | 166 | ALOGW("%s: pid %d stopped", __PRETTY_FUNCTION__, *ret.pid); | 
|  | 167 | } else if (WIFCONTINUED(status)) { | 
|  | 168 | ALOGW("%s: pid %d continued", __PRETTY_FUNCTION__, *ret.pid); | 
|  | 169 | } | 
|  | 170 | } | 
|  | 171 | // ret is not changed unless the process is terminated (where pid == nullopt). Hence there | 
|  | 172 | // is no need to check the predicate `end(ret)`. | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | return ret; | 
|  | 176 | } | 
|  | 177 | } // namespace android |