Search code examples
cshellpipeline

supporting pipes for shell in c


For a class, I need to create my own shell in C. I'm trying to support piping of commands using pipelines. As far as I can tell both ends of the pipe get closed. Still, I get no result back, can anyone spot what is wrong here?

The do_* functions are functions with a tiny error handling wrapper. For completeness sake, they are included at the bottom of the page.

This functions spawns a program and ensure the child can only read and write to the correct pipes.

    int execute_piped_binairy (int input, int output, Pgm *program){
      pid_t pid = do_fork();

      if (pid == 0){ //I am the child
        if (input != 0){//input is not standard input, overwrite stdin with given input
           do_dub_and_close(input, 0);  
        } if (output != 1) { //output is not standard output, overwrite stdout with given output
           do_dub_and_close(output, 1);
        }
        do_exec(program->pgmlist);
      } else {
        return pid;
      }
    }

This function spawns k children and k-1 pipes in order to execute k commands. The commands are given in reverse order meaning the first command should output to the global output.

    int execute_binairy_list (int input, int output, int pgmCount, Pgm *program){

      int childPids[pgmCount];
      int childStatus[pgmCount];
      int childId;
      int idx;

      // For k programs we need k-1 pipes and need to spawn k children
      for(idx = 0; idx<pgmCount-1; idx++){
        int file_descriptors[2]={0, 1};
        //create unnamed pipe
        do_pipe(file_descriptors);

        // pass stored input and created write file descriptor
        childId = do_execute_piped_binairy (file_descriptors[0], output, program+idx);
        childPids[idx]=childId; //keep child id to make parent wait for all children.

        if(output!=1){
           do_close(output);
        };
        do_close(file_descriptors[0]); // close read end of new pipe, child reads from this pipe. 
        output = file_descriptors [1]; //store write end of new pipe for next iteration
        }
      //we still need to spawn 1 child to execute the first command based on the global input
      // pass stored input and created write file descriptor
      childId = do_execute_piped_binairy (input, output, program+idx);
      childPids[idx]=childId; //keep child id to make parent wait for all children.
      if(output!=1){
          do_close(output);
      };
      for(idx=0; idx<pgmCount; idx++){
        do_wait(childPids[idx], childStatus+idx); //wait for all children to return;
      }
      return 0;
    }

I have tested my code with three commands wish should result in this order: command1 -> pipe1 -> command2 -> pipe2 -> command3 When run with a lot of printfs this is what happens (more or less as scheduling influences the order):

    created pipe: 3 4 //pipe 2
    created child: 2452 //command 3
    closed input: 3 //parent closes read end of pipe 2
    created pipe: 3 5 //pipe 1
    child: 2452 3 1 //command 3 reads from read end of pipe 2 and writes to 1
    closed input: 3 2452 //command 3 closes read end of pipe 2
    created child: 2453 //command 2
    closed output: 4 //parent closes write end of pipe 2
    closed input: 3 //parent closes read end of pipe 1
    closed output: 5 //parent closes write end of pipe 1
    child: 2453 3 4 //command 2 reads from read end of pipe 1 and writes to write end of pipe 2
    closed input: 3 2453 //command 2 closes read end of pipe 1
    closed output: 4 2453 //command 2 closes write end of pipe 2
    child: 2454 0 5 //command 1 reads from 0 and writes to write end of pipe 1
    closed output: 5 2454 //command 1 closes write end of pipe 1

Any help is greatly appreciated


Solution

  • figured it out myself. When forking the children receive a copy of both ends of the pipe from their parent. Since the children only close the ends they use there is always one write end of the pipe not close. Which means no eof messages are ever send