10

I'm trying to write a program that will spawn an arbitrary number of child processes and pipe between them, similar to a command line pipeline. In my case I'm trying to do "ls -l | more" and output that to stdout, then have the parent continue executing more commands.

I have the following code as a minimal example:

int main (int argc, const char * argv[]) {
    int fd[2];
    pipe(fd); 
    chdir("/directory/with/lots/of/files");

    // Create one child process for more
    int pid = fork();
    if (pid == 0) {
        close(fd[1]);
        int ret = dup2(fd[0],0);
        if (ret < 0) perror("dup2");
        char *argv[10];
        argv[0] = "more";  argv[1] = NULL;
        execvp("more", argv);
    } 
    // Create another child process for ls
    int pid2 = fork();
    if (pid2 == 0) {
        int ret = dup2(fd[1],1);
        if (ret < 0) perror("dup2");
        char *argv[10];
        argv[0] = "ls";    argv[1] = "-l";   
        argv[2] = NULL;
        execvp("ls", argv);
    }

    // wait for the more process to finish
    int status;
    waitpid(pid, &status, 0);

    printf("Done!\n");
    return 0;
}

Now, when I execute the program (enclosed in a main() function of course) what I end up with is more, which is expected. I'll hit "d" to page down more's output and "u" to go up, and it seems to work fine. But when I reach the bottom, instead of exiting like more does, it just leaves a blank line. Ctrl-C works to exit it but it exits the entire program, meaning the "Done!" line never gets printed. A movie is available here that illustrates what happens (note that at the very end I press Ctrl-C to get back to bash).

Any thoughts on this? I'm just trying to figure out how to change it to where instead of going to a blank line after more reaches the bottom, more quits and returns to the parent process so it can continue executing.

jstm88
  • 3,335
  • 4
  • 38
  • 55
  • Check to see if `more` is a symlink to `less`. Less needs `-e` to quit at the end of the file. It's possible that there's a difference in the `PATH` in your program's environment versus your interactive environment. – Dennis Williamson Feb 20 '11 at 22:04
  • It's not, but thanks for the heads up. That just gives me one more test case. :) – jstm88 Feb 20 '11 at 22:24

2 Answers2

11

You need to close() at least the writing end of your pipe, otherwise more will never see EOF. For example:

    ...

    // close parent's pipes
    close(fd[0]);
    close(fd[1]);

    // wait for the more process to finish
    int status;
    waitpid(pid, &status, 0);

    printf("Done!\n");
    return 0;
}
Robie Basak
  • 6,492
  • 2
  • 30
  • 34
  • You should also `close(fd[0])` after the `dup2()` in the first child, and likewise for `fd[1]` in the second child. – caf Feb 20 '11 at 22:20
  • 1
    Bingo, that worked! I was closing the pipes in the child processes (at least in the final version, I just realized the minimal example is missing a `close(fd[0]);` in the second child) but I never thought of closing them on the parent's end. After looking a bit deeper I found the comment "After the fork you will have a single pipe with two inputs and two outputs" which makes sense in this context. Thanks! EDIT: Yep, caf, I spotted that as I was going over it, but I do close them in the production version. :) – jstm88 Feb 20 '11 at 22:22
-1

I think it is because of the wait() function. Following the logic, your second child process outputs to the first child process, meaning it should end first than the second one.

In your wait function you are waiting for the first process to end, but you are not waiting for the second process. That means that if the second process does not send an EOF to the output ever, your first process won't end, i guess.

You can try to wait for the second process instead of the first and find out if that's the problem.

Tiago
  • 1,337
  • 11
  • 17