Search code examples
c++ncurses

execlp does nothing from the second time at C++ and ncurses


I'm trying to get strings from child process using pipe() and execlp(). After making function for this, I used it twice in main process and it works at first, but not work at second. I want to fix this code to work at any time. this is my code.

vector<string> getHelp2(string program_name){
    string command = "man " + program_name;
    int stdin_copy = dup(STDIN_FILENO);
    int stdout_copy = dup(STDOUT_FILENO);
    int stderr_copy = dup(STDERR_FILENO);
    int pipefd[2] = {0, };
    pipe(pipefd);
    pid_t pid = fork();
    if(pid == 0){
        close(pipefd[0]);
        dup2(pipefd[1], STDOUT_FILENO);
        dup2(pipefd[1], STDERR_FILENO);
        fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
        execlp("man", "man", program_name.data() ,NULL);
        
    }
    else{
        dup2(pipefd[0], STDIN_FILENO);
        waitpid(pid, 0, 0);
        close(pipefd[1]);
        string str;
        string substr;
        getline(cin, substr);
        getline(cin, substr);
        while(getline(cin, substr)){
            str+=substr+"\n";
        }
        dup2(stdin_copy, STDIN_FILENO);
        dup2(stdout_copy, STDOUT_FILENO);
        dup2(stderr_copy, STDERR_FILENO);
        close(pipefd[0]);
        cin.clear();

I restored stdin, stdout, stderr and pipefd[2] even using fcntl but it doesn't work. and I also use cin.clear().

Add : It seems that stdout is not restored unlike stdin. How can I restore it?


Solution

  • If the program initiated by execlp produces so much data that it fills the pipe and blocks before exiting, there will be a deadlock between the child waiting for the parent to drain some data from the pipe and the parent waiting for the child to exit in the call to waitpid. The call to waitpid should be placed after the point where the end-of-file on pipefd[0] is identified.

    Next, the swapping of the source to which STDIN_FILENO refers underneath the cin infrastructure may have indeterminate effects, especially if an end-of-file is detected between one association and the next. In effect, cin, and especially its underlying instance of filebuf, thinks it has struck end-of-file, and then you move the goal posts by changing what STDIN_FILENO refers to. clear should reset the end-of-file flag, but at best it's bad style. There would be two nice solutions. One would be to use an ifstream that reads from a file descriptor. See How to construct a c++ fstream from a POSIX file descriptor? The other would be to just use the Linux read function with a fixed length buffer. Either solution would obviate the need for all the file descriptor juggling.

    I also notice that stdin_copy, stdout_copy and stderr_copy are not closed -- in neither the parent nor the child. Although it is necessary to duplicate pipefd[1] to STDIN_FILENO in the child, all the other file descriptor juggling is a bit ugly and potentially error prone as observed above.