0
votes

I'm in the process of properly understanding pipes and FDs and I'm trying to program the following thing: The program basically compresses stuff like gzip does with the options -cf. The basic idea is:

I create two pipes in the parent process, then I fork it twice so that I'll have two children. In the first child, I redirect the first pipe's read end to stdin, and the second pipe's write end to stdout. Then I exec gzip with the -cf options so that it'll write to stdout (now the writing end of pipe2)

In the second child, I read from pipe2 and either output it directly or save it to a file. The problem is, however, that no data arrives at the second child and I'm not really sure why. Here's the code:

int main(int argc, char **argv) {
    char *file;
    int out = 0;
    if(argc == 2) {
        file = argv[1];
        out = 1;
    } else if (argc > 2) {
        exit(EXIT_FAILURE);         
    }

    int c1pipe[2];
    int c2pipe[2];
    pipe(c1pipe);
    pipe(c2pipe);

    int f;    
    for(int i = 0; i < 2; i++) {
        switch(f = fork()) {
            case 0: //child
                if(i == 0) { //first loop iteration, child 1
                    close(c1pipe[1]); 
                    dup2(c1pipe[0], fileno(stdin));
                    close(c1pipe[0]); 
                    close(c2pipe[0]); 
                    dup2(c2pipe[1], fileno(stdout));
                    close(c2pipe[1]); 
                    execlp("gzip", "gzip", "-cf", (char *) NULL);
                } else if (i == 1) { //second loop iteration, child2
                    close(c1pipe[0]);
                    close(c1pipe[1]);
                    close(c2pipe[1]);
                    FILE *read = fdopen(c2pipe[0], "r");    
                    char buffer[1024];

                    if(out == 0) { //output to stdout
                        while(fgets(buffer, 1024, read) != NULL) {
                            fprintf(stdout, "%s", buffer);
                            fflush(stdout);
                        }
                    } else { //write to specified file
                        FILE *writeto = fopen(file, "w");
                        while(fread(buffer, sizeof(char), strlen(buffer)+1, read) > 0) {
                            fwrite(buffer, sizeof(char), strlen(buffer)+1, writeto);
                            fflush(writeto);
                        }
                        fclose(writeto);
                    }

                    close(c2pipe[0]);
                    fclose(read);
                }           
            break;
            case -1: //err
                //not implemented
            break;
            default: //parent
                if(i == 0) {
                    close(c2pipe[0]);
                    close(c2pipe[1]);
                    close(c1pipe[0]);
                    FILE *writer;
                    writer = fdopen(c1pipe[1], "w");
                    char buffer[1024];
                    while(fgets(buffer, sizeof buffer, stdin) != NULL) {
                        fwrite(buffer, sizeof (char), strlen(buffer)+1, writer);
                    }
                    close(c1pipe[1]);
                    fclose(writer);
                }
            break;
        }
    }

    return 0;
}

Please excuse the missing error handling as I wanted to create a quick-and-dirty version.

Any help is appreciated.

1

1 Answers

2
votes

In the parent process, you are closing both ends of c2pipe before you have forked the second child.

You'd probably have figured this out already if you had put any error handling in on any of the read/write calls. In fact, if you checked for an error on the dup2 calls and then looked at errno, you probably would have found that it was EBADF (bad file descriptor).

Another issue is that your parent process exits before it knows that both child processes have finished. This means that the child processes will receive a signal and will themselves be terminated. The parent needs to call one of the variants of wait() to make sure both children have gone.