Search code examples
clinuxpipeipcpipeline

Multi-pipe does not work


I need to implement such IPC-schema:

runtime data -> filter1 -> filter2 -> output. (same as data | filter1 | filter2).

I can pass data to first filter, but to second I can not (maybe because in first child stdout fd is not closed). How to properly implement such schema?

P.S. filter1 and filter2 just read from stdin and write to stdout.

My code:

int main() {
    int fd1[2];
    pipe(fd1);
    pid_t pid1;

    if ((pid1 = fork()) > 0) {
        char data[] = "Hello world!";

        close(fd1[0]);
        write(fd1[1], data, sizeof(data));
        close(fd1[1]);
        wait(NULL);
        exit(EXIT_SUCCESS);

    } else if (pid1 == 0) {
        int fd2[2];
        pipe(fd2);
        pid_t pid2;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);
        close(fd1[0]); close(fd1[1]);
        close(fd2[0]); close(fd2[1]);

        if ((pid2 = fork()) > 0) {
            execl("./upcase", "upcase", NULL);
            perror("execl");
            exit(EXIT_FAILURE);
        } else if (pid2 == 0) {
            close(fd1[0]); close(fd1[1]);

            dup2(fd2[0], STDIN_FILENO);
            close(fd2[0]); close(fd2[1]);

            execl("./reverse", "reverse", NULL);
            perror("execl");
            exit(EXIT_FAILURE);
        } else {
            perror("pid2");
            exit(EXIT_FAILURE);
        }

    } else {
        perror("pid1");
        exit(EXIT_FAILURE);
    }

}

Solution

  • You are closing the pipes too early. Typically, you close fd2[0] before you use it in dup2. And as you redirect FILENO_STDOUT before the second fork, the second filter has no longer access to the original stdout.

    Following code works:

    int main() {
        int fd1[2];
        pipe(fd1);
        pid_t pid1;
    
        if ((pid1 = fork()) > 0) {
            char data[] = "Hello world!";
    
            close(fd1[0]); // OK, will no longer be used
            write(fd1[1], data, sizeof(data));
            close(fd1[1]);
            wait(NULL);
            exit(EXIT_SUCCESS);
    
        } else if (pid1 == 0) {
            int fd2[2];
            pipe(fd2);
            pid_t pid2;
            close(fd1[1]); // OK, no used from here
    
            if ((pid2 = fork()) > 0) {
            dup2(fd1[0], STDIN_FILENO);  // redirections for filter1
            dup2(fd2[1], STDOUT_FILENO);
            close(fd1[0]);               // close everything except stdin and stdout
            close(fd2[0]); close(fd2[1]);
                execl("./upcase", "upcase", NULL);
                perror("execl upcase");
                exit(EXIT_FAILURE);
            } else if (pid2 == 0) {
                close(fd1[0]);             // not used here
    
                dup2(fd2[0], STDIN_FILENO); // redirection for filter2
                close(fd2[0]); close(fd2[1]); // close all what remains
    
                execl("./reverse", "reverse", NULL);
                perror("execl reverse");
                exit(EXIT_FAILURE);
            } else {
                ...