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 if (chdir(directory.c_str()) != 0) {
145 constexpr char kPrefix[] =
"Cannot enter command working directory: ";
146 constexpr char kNewline[] =
"\n";
147 (void)write(STDERR_FILENO, kPrefix,
sizeof(kPrefix) - 1);
148 (void)write(STDERR_FILENO, directory.data(), directory.size());
149 (void)write(STDERR_FILENO, kNewline,
sizeof(kNewline) - 1);
153 execl(
"/bin/sh",
"sh",
"-lc", command.c_str(),
static_cast<char*
>(
nullptr));
160 std::lock_guard<std::mutex> lock(
mutex_);
164 std::array<char, 512> buffer{};
165 bool sent_sigterm =
false;
166 bool sent_sigkill =
false;
167 auto cancel_requested_at = std::chrono::steady_clock::time_point{};
168 bool pipe_closed =
false;
170 bool child_reaped =
false;
172 while (!pipe_closed || !child_reaped) {
173 struct pollfd pfd{pipe_fds[0], POLLIN | POLLHUP, 0};
174 const int poll_result = poll(&pfd, 1, kPollIntervalMs);
175 if (poll_result > 0 && (pfd.revents & (POLLIN | POLLHUP))) {
176 const ssize_t bytes_read =
177 read(pipe_fds[0], buffer.data(), buffer.size());
178 if (bytes_read > 0) {
179 AppendOutput(buffer.data(),
static_cast<size_t>(bytes_read));
180 }
else if (bytes_read == 0) {
182 }
else if (errno != EINTR) {
187 const bool cancel_requested = [
this]() {
188 std::lock_guard<std::mutex> lock(
mutex_);
191 if (cancel_requested) {
192 if (cancel_requested_at == std::chrono::steady_clock::time_point{}) {
193 cancel_requested_at = std::chrono::steady_clock::now();
199 }
else if (!sent_sigkill &&
200 std::chrono::steady_clock::now() - cancel_requested_at >=
201 kCancelEscalationDelay) {
208 const pid_t wait_result = waitpid(pid, &wait_status, WNOHANG);
209 if (wait_result == pid) {
216 std::lock_guard<std::mutex> lock(
mutex_);
220 const bool cancel_requested = [
this]() {
221 std::lock_guard<std::mutex> lock(
mutex_);
224 if (cancel_requested) {
225 Finalize(absl::CancelledError(
"Background command cancelled"), -1);
229 if (!WIFEXITED(wait_status) || WEXITSTATUS(wait_status) != 0) {
230 const int exit_code =
231 WIFEXITED(wait_status) ? WEXITSTATUS(wait_status) : -1;
232 Finalize(absl::InternalError(
"Background command failed"), exit_code);
236 Finalize(absl::OkStatus(), WEXITSTATUS(wait_status));
262 const std::string& output) {
263 if (output.empty()) {
267 std::vector<std::string> lines;
268 for (absl::string_view line_view : absl::StrSplit(output,
'\n')) {
269 std::string line(line_view);
270 absl::StripAsciiWhitespace(&line);
272 lines.push_back(std::move(line));
279 lines.size() > kTailLineLimit ? lines.size() - kTailLineLimit : 0;
281 for (
size_t i = start; i < lines.size(); ++i) {