Skip to content

Commit beb96b9

Browse files
authored
Merge pull request #8650 from diffblue/run-with-pipe
`run(..., std::ostream &, ...)` with pipe
2 parents f1faa61 + c4a738b commit beb96b9

File tree

2 files changed

+103
-26
lines changed

2 files changed

+103
-26
lines changed

src/ansi-c/c_preprocess.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,6 @@ bool c_preprocess_visual_studio(
305305
command_file << shell_quote(file) << "\n";
306306
}
307307

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

src/util/run.cpp

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -541,40 +541,119 @@ int run(
541541

542542
return result;
543543
#else
544-
std::string command;
544+
int pipefd[2];
545+
if(pipe(pipefd) == -1)
546+
return -1;
547+
548+
int stdin_fd = stdio_redirection(STDIN_FILENO, std_input);
549+
int stdout_fd = pipefd[1];
550+
int stderr_fd = stdio_redirection(STDERR_FILENO, std_error);
545551

546-
bool first = true;
552+
if(stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1)
553+
return 1;
554+
555+
// temporarily suspend all signals
556+
sigset_t new_mask, old_mask;
557+
sigemptyset(&new_mask);
558+
sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
547559

548-
// note we use 'what' instead of 'argv[0]' as the name of the executable
549-
for(const auto &arg : argv)
560+
/* now create new process */
561+
pid_t childpid = fork();
562+
563+
if(childpid >= 0) /* fork succeeded */
550564
{
551-
if(first) // this is argv[0]
565+
if(childpid == 0) /* fork() returns 0 to the child process */
552566
{
553-
command += shell_quote(what);
554-
first = false;
567+
// resume signals
568+
remove_signal_catcher();
569+
sigprocmask(SIG_SETMASK, &old_mask, nullptr);
570+
571+
close(pipefd[0]); // unused in child
572+
573+
std::vector<char *> _argv(argv.size() + 1);
574+
for(std::size_t i = 0; i < argv.size(); i++)
575+
_argv[i] = strdup(argv[i].c_str());
576+
577+
_argv[argv.size()] = nullptr;
578+
579+
if(stdin_fd != STDIN_FILENO)
580+
dup2(stdin_fd, STDIN_FILENO);
581+
if(stdout_fd != STDOUT_FILENO)
582+
dup2(stdout_fd, STDOUT_FILENO);
583+
if(stderr_fd != STDERR_FILENO)
584+
dup2(stderr_fd, STDERR_FILENO);
585+
586+
errno = 0;
587+
execvp(what.c_str(), _argv.data());
588+
589+
/* usually no return */
590+
perror(std::string("execvp " + what + " failed").c_str());
591+
exit(1);
555592
}
556-
else
557-
command += " " + shell_quote(arg);
558-
}
593+
else /* fork() returns new pid to the parent process */
594+
{
595+
// must do before resuming signals to avoid race
596+
register_child(childpid);
559597

560-
if(!std_input.empty())
561-
command += " < " + shell_quote(std_input);
598+
// resume signals
599+
sigprocmask(SIG_SETMASK, &old_mask, nullptr);
562600

563-
if(!std_error.empty())
564-
command += " 2> " + shell_quote(std_error);
601+
close(pipefd[1]); // unused in the parent
602+
603+
const int buffer_size = 1024;
604+
std::vector<char> buffer(buffer_size);
605+
ssize_t bytes_read;
565606

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

568-
if(stream!=nullptr)
610+
int status; /* parent process: child's exit status */
611+
612+
/* wait for child to exit, and store its status */
613+
while(waitpid(childpid, &status, 0) == -1)
614+
{
615+
if(errno == EINTR)
616+
continue; // try again
617+
else
618+
{
619+
unregister_child();
620+
621+
perror("Waiting for child process failed");
622+
if(stdin_fd != STDIN_FILENO)
623+
close(stdin_fd);
624+
if(stdout_fd != STDOUT_FILENO)
625+
close(stdout_fd);
626+
if(stderr_fd != STDERR_FILENO)
627+
close(stderr_fd);
628+
return 1;
629+
}
630+
}
631+
632+
unregister_child();
633+
634+
if(stdin_fd != STDIN_FILENO)
635+
close(stdin_fd);
636+
if(stdout_fd != STDOUT_FILENO)
637+
close(stdout_fd);
638+
if(stderr_fd != STDERR_FILENO)
639+
close(stderr_fd);
640+
641+
return WEXITSTATUS(status);
642+
}
643+
}
644+
else /* fork returns -1 on failure */
569645
{
570-
int ch;
571-
while((ch=fgetc(stream))!=EOF)
572-
std_output << (unsigned char)ch;
646+
// resume signals
647+
sigprocmask(SIG_SETMASK, &old_mask, nullptr);
573648

574-
int result = pclose(stream);
575-
return WEXITSTATUS(result);
649+
if(stdin_fd != STDIN_FILENO)
650+
close(stdin_fd);
651+
if(stdout_fd != STDOUT_FILENO)
652+
close(stdout_fd);
653+
if(stderr_fd != STDERR_FILENO)
654+
close(stderr_fd);
655+
656+
return 1;
576657
}
577-
else
578-
return -1;
579-
#endif
658+
#endif
580659
}

0 commit comments

Comments
 (0)