Search code examples
cpipeinter-process-communicat

An issue with communication between parent-process and child-process using pipe


I'm writing a C program, that given an executable a.out and input files in_1.txt, in_2.txt ... in_n.txt, will run a.out on all n input files and will produce corresponding output to each and every input file in the argument list.
I ran into some weird behaviour from my program (call it tester.out), and after couple of hours, I was able to identify my problem, but could not figure out what to solve it.

I will first post the code and will then explain the problem...

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<limits.h>
#include<string.h>

#define PERM S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP  // 660

int main(int argc, char* argv[]) {

    if (argc<3) {
        printf("Arguments required; an executable file and at least one input file.\n");
        return 1;
    }

    int channel[2];
    if (pipe(channel)<0) {
        perror("pipe");
        return 1;
    }

    int i;
    for (i=2; i<argc; ++i) {

        pid_t p=fork();
        if (p<0) {
            perror("fork");
            return 1;
        }

        if (p) { // parent

            // closing read end
            close(channel[0]); // *** The problem lies here! ***

            int indicator;

            if ((indicator=write(channel[1], argv[i], strlen(argv[i])+1))<0) {
                perror("write");
                return 1;
            }

            printf("Parent wrote %d bytes\n", indicator);

            int status;
            wait(&status);

            if (WIFEXITED(status)) {
                printf("Your program terminated normally with exit code %d\n", WEXITSTATUS(status));
                printf("See output file: %s_output\n", argv[i]);
            }
            else if (WIFSIGNALED(status)) {
                printf("Your program terminated by a signal! signal code: %d\n", WTERMSIG(status));
            }

        }
        else { // child

            // closing write end
            close(channel[1]);

            char input_file[PATH_MAX]={0};
            char output_file[PATH_MAX+10]={0};

            int indicator;

            // read input_file from pipe
            if ((indicator=read(channel[0], input_file, PATH_MAX))<1) {
                perror("child process: read");
                return 1;
            }

            printf("child read %d bytes\n", indicator);

            sprintf(output_file, "%s_output", input_file);
            printf("Got %s and output is %s\n", input_file, output_file);

            sleep(5); /* later, I will call execl() here to run argv[1] on input_file */
            return 1;
        }



    }



    return 0;
}

This works as expected for when I run:

$ ./tester.out a.out input1.txt

But fails for when I run it with more than one input file:

$ ./tester.out a.out input1.txt input2.txt

As the highlighted comment in the code suggests, the problem lies in the line calling to close(channel[0]), because closing channel[0] in the parent on the first iteration, means that on the second iteration, the child process has its channel[0] closed (by its parent - in the first iteration).

Any suggestions on how to solve this situation?


Solution

  • You should create a new pipe for each child process, and close both ends of the pipe when you are finished with it. You need to close both ends in both the parent and the child.