Skip to content

run(..., std::ostream &, ...) with pipe #8650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/ansi-c/c_preprocess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,6 @@ bool c_preprocess_visual_studio(
command_file << shell_quote(file) << "\n";
}

// _popen isn't very reliable on WIN32
// that's why we use run()
int result =
run("cl", {"cl", "@" + command_file_name()}, "", outstream, stderr_file());

Expand Down
127 changes: 103 additions & 24 deletions src/util/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,40 +541,119 @@

return result;
#else
std::string command;
int pipefd[2];
if(pipe(pipefd) == -1)
return -1;

int stdin_fd = stdio_redirection(STDIN_FILENO, std_input);
int stdout_fd = pipefd[1];
int stderr_fd = stdio_redirection(STDERR_FILENO, std_error);

bool first = true;
if(stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1)
return 1;

// temporarily suspend all signals
sigset_t new_mask, old_mask;
sigemptyset(&new_mask);
sigprocmask(SIG_SETMASK, &new_mask, &old_mask);

// note we use 'what' instead of 'argv[0]' as the name of the executable
for(const auto &arg : argv)
/* now create new process */
pid_t childpid = fork();

if(childpid >= 0) /* fork succeeded */
{
if(first) // this is argv[0]
if(childpid == 0) /* fork() returns 0 to the child process */
{
command += shell_quote(what);
first = false;
// resume signals
remove_signal_catcher();
sigprocmask(SIG_SETMASK, &old_mask, nullptr);

Check warning on line 569 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L568-L569

Added lines #L568 - L569 were not covered by tests

close(pipefd[0]); // unused in child

Check warning on line 571 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L571

Added line #L571 was not covered by tests

std::vector<char *> _argv(argv.size() + 1);
for(std::size_t i = 0; i < argv.size(); i++)
_argv[i] = strdup(argv[i].c_str());

Check warning on line 575 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L573-L575

Added lines #L573 - L575 were not covered by tests

_argv[argv.size()] = nullptr;

Check warning on line 577 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L577

Added line #L577 was not covered by tests

if(stdin_fd != STDIN_FILENO)
dup2(stdin_fd, STDIN_FILENO);
if(stdout_fd != STDOUT_FILENO)
dup2(stdout_fd, STDOUT_FILENO);
if(stderr_fd != STDERR_FILENO)
dup2(stderr_fd, STDERR_FILENO);

Check warning on line 584 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L579-L584

Added lines #L579 - L584 were not covered by tests

errno = 0;
execvp(what.c_str(), _argv.data());

Check warning on line 587 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L586-L587

Added lines #L586 - L587 were not covered by tests

/* usually no return */
perror(std::string("execvp " + what + " failed").c_str());
exit(1);

Check warning on line 591 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L590-L591

Added lines #L590 - L591 were not covered by tests
}
else
command += " " + shell_quote(arg);
}
else /* fork() returns new pid to the parent process */
{
// must do before resuming signals to avoid race
register_child(childpid);

if(!std_input.empty())
command += " < " + shell_quote(std_input);
// resume signals
sigprocmask(SIG_SETMASK, &old_mask, nullptr);

if(!std_error.empty())
command += " 2> " + shell_quote(std_error);
close(pipefd[1]); // unused in the parent

const int buffer_size = 1024;
std::vector<char> buffer(buffer_size);
ssize_t bytes_read;

FILE *stream=popen(command.c_str(), "r");
while((bytes_read = read(pipefd[0], buffer.data(), buffer_size)) > 0)
std_output.write(buffer.data(), bytes_read);

if(stream!=nullptr)
int status; /* parent process: child's exit status */

/* wait for child to exit, and store its status */
while(waitpid(childpid, &status, 0) == -1)
{
if(errno == EINTR)
continue; // try again

Check warning on line 616 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L615-L616

Added lines #L615 - L616 were not covered by tests
else
{
unregister_child();

Check warning on line 619 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L619

Added line #L619 was not covered by tests

perror("Waiting for child process failed");
if(stdin_fd != STDIN_FILENO)
close(stdin_fd);
if(stdout_fd != STDOUT_FILENO)
close(stdout_fd);
if(stderr_fd != STDERR_FILENO)
close(stderr_fd);
return 1;

Check warning on line 628 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L621-L628

Added lines #L621 - L628 were not covered by tests
}
}

unregister_child();

if(stdin_fd != STDIN_FILENO)
close(stdin_fd);

Check warning on line 635 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L635

Added line #L635 was not covered by tests
if(stdout_fd != STDOUT_FILENO)
close(stdout_fd);
if(stderr_fd != STDERR_FILENO)
close(stderr_fd);

return WEXITSTATUS(status);
}
}
else /* fork returns -1 on failure */
{
int ch;
while((ch=fgetc(stream))!=EOF)
std_output << (unsigned char)ch;
// resume signals
sigprocmask(SIG_SETMASK, &old_mask, nullptr);

Check warning on line 647 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L647

Added line #L647 was not covered by tests

int result = pclose(stream);
return WEXITSTATUS(result);
if(stdin_fd != STDIN_FILENO)
close(stdin_fd);
if(stdout_fd != STDOUT_FILENO)
close(stdout_fd);
if(stderr_fd != STDERR_FILENO)
close(stderr_fd);

Check warning on line 654 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L649-L654

Added lines #L649 - L654 were not covered by tests

return 1;

Check warning on line 656 in src/util/run.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/run.cpp#L656

Added line #L656 was not covered by tests
}
else
return -1;
#endif
#endif
}
Loading