@@ -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