115 std::string directory) {
116#if defined(_WIN32) || defined(__EMSCRIPTEN__) || \
117 (defined(__APPLE__) && TARGET_OS_IPHONE)
119 "Background commands are not supported on this platform"),
124 if (pipe(pipe_fds) != 0) {
125 Finalize(absl::InternalError(
"Failed to create process pipe"), -1);
129 const pid_t pid = fork();
133 Finalize(absl::InternalError(
"Failed to fork background process"), -1);
139 dup2(pipe_fds[1], STDOUT_FILENO);
140 dup2(pipe_fds[1], STDERR_FILENO);
143 if (!directory.empty()) {
144 (void)chdir(directory.c_str());
146 execl(
"/bin/sh",
"sh",
"-lc", command.c_str(),
147 static_cast<char*
>(
nullptr));
154 std::lock_guard<std::mutex> lock(
mutex_);
158 std::array<char, 512> buffer{};
159 bool sent_sigterm =
false;
160 bool sent_sigkill =
false;
161 auto cancel_requested_at = std::chrono::steady_clock::time_point{};
162 bool pipe_closed =
false;
164 bool child_reaped =
false;
166 while (!pipe_closed || !child_reaped) {
168 pipe_fds[0], POLLIN | POLLHUP, 0
170 const int poll_result = poll(&pfd, 1, kPollIntervalMs);
171 if (poll_result > 0 && (pfd.revents & (POLLIN | POLLHUP))) {
172 const ssize_t bytes_read = read(pipe_fds[0], buffer.data(), buffer.size());
173 if (bytes_read > 0) {
174 AppendOutput(buffer.data(),
static_cast<size_t>(bytes_read));
175 }
else if (bytes_read == 0) {
177 }
else if (errno != EINTR) {
182 const bool cancel_requested = [
this]() {
183 std::lock_guard<std::mutex> lock(
mutex_);
186 if (cancel_requested) {
187 if (cancel_requested_at == std::chrono::steady_clock::time_point{}) {
188 cancel_requested_at = std::chrono::steady_clock::now();
194 }
else if (!sent_sigkill &&
195 std::chrono::steady_clock::now() - cancel_requested_at >=
196 kCancelEscalationDelay) {
203 const pid_t wait_result = waitpid(pid, &wait_status, WNOHANG);
204 if (wait_result == pid) {
211 std::lock_guard<std::mutex> lock(
mutex_);
215 const bool cancel_requested = [
this]() {
216 std::lock_guard<std::mutex> lock(
mutex_);
219 if (cancel_requested) {
220 Finalize(absl::CancelledError(
"Background command cancelled"), -1);
224 if (!WIFEXITED(wait_status) || WEXITSTATUS(wait_status) != 0) {
225 const int exit_code = WIFEXITED(wait_status) ? WEXITSTATUS(wait_status) : -1;
226 Finalize(absl::InternalError(
"Background command failed"), exit_code);
230 Finalize(absl::OkStatus(), WEXITSTATUS(wait_status));
256 if (output.empty()) {
260 std::vector<std::string> lines;
261 for (absl::string_view line_view : absl::StrSplit(output,
'\n')) {
262 std::string line(line_view);
263 absl::StripAsciiWhitespace(&line);
265 lines.push_back(std::move(line));
272 lines.size() > kTailLineLimit ? lines.size() - kTailLineLimit : 0;
274 for (
size_t i = start; i < lines.size(); ++i) {