clinuxpipedup2

Issue with dup2


I am trying a simple program where the STDOUT from Child process is directed to the Parent process. The program works if I don't fork multiple times and call the child_process and parent_process in a loop. However, when I put a for loop around the fork and child_process and parent_process, dup2 function returns -1 and hence the program fails. What am I doing wrong?

This code has a for loop around the child_process and parent_process and has issues with dup2

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
#define BUFSIZE 256
 
void child_process(int pipe_pair[2]){
 
    // closes the child's STDOUT descriptor and replaces it with
    // the write pipe linked to the parent's read pipe
    if (dup2(pipe_pair[1], STDOUT_FILENO) == -1){ 
      // if dup2 fails, perror writes to stderr so this reports
      // appropriately either way as the child and parent share stderr
      perror("dup2"); 
      // parent will know if the child failed since we return 1
      // (ideally return errno so that calling processes know the error code)
      exit(EXIT_FAILURE); 
    }   
 
    // duplicated by dup2 above, no longer needed
    close(pipe_pair[1]); 
    // close read end as we will never read from stdout
    close(pipe_pair[0]); 
 
    // printf writes to stdout by default
    // we could also use fprintf(stdout, ...)
    printf("Hello, parent!\n"); 
    // make sure the write buffer is flushed before we exit
    fflush(stdout); 
 
    // close to make sure read() returns 0 in the parent
    close(STDOUT_FILENO); 
 
    // child exits
    exit(EXIT_SUCCESS); 
}
 
void parent_process(int pipe_pair[2], pid_t cpid){
 
    // cstatus will store the return of the child process
    // buf will hold the child's writes to stdout --
    // {0} initializes the array elements to 0x00
    int cstatus; 
    char buf[BUFSIZE] = {0}; 
 
    close(pipe_pair[1]); // we won't write to stdout
 
    // read until closed, or error (0 or -1, respectively)
    for (int n = 0; (n = read(pipe_pair[0], buf, BUFSIZE)) > 0;){ 
      printf("Received %d bytes from child process: ", n); 
      // (needed otherwise write() may output before 
      // printf since stdio output to stdout is line buffered)
      fflush(stdout); 
      // writes just what we read so no need to reset buf
      write(STDOUT_FILENO, buf, n); 
      printf("\n");
      fflush(stdout);
    }   
 
    // close read pipe
    close(pipe_pair[0]); 
 
    // waits for child process with pid 'cpid' to
    // return and stores the exit code in cstatus
    waitpid(cpid, &cstatus, 0); 
 
    printf("Child exit status was: %d\n", cstatus);
 
    // terminate parent
    //exit(EXIT_SUCCESS); 
 
}
 
int main(int argc, char **argv){
 
  // cpid stores the process id of the child process
  // stdout_pipe array = pipe descriptor pair -- 
  // [0] is the read end, [1] is the write end
  pid_t cpid; 
  int stdout_pipe[2]; 
 
  // call that creates the two unidirectional pipe streams 
  // and stores the descriptors in the array
  if (pipe(stdout_pipe) == -1){ 
    perror("pipe");
    exit(EXIT_FAILURE);
  }
 
  // fork happens here, cpid will have the child's
  // process id or -1 if the call fails
  for (int i=0; i<3; i++) {
      cpid = fork(); 
      if (cpid == -1){
         perror("fork");
         exit(EXIT_FAILURE);
      }
 
      // child (fork returns 0 in the child and the child ID for the parent)
      if (cpid == 0) 
         child_process(stdout_pipe);
      // else when cpid is not 0 or -1 we're in the parent
      else 
         parent_process(stdout_pipe, cpid);
  }
  // we shouldn't get here, but return int from main for correctness
  return 0;
}

Solution

  • The problem is this block is outside the loop:

    if (pipe(stdout_pipe) == -1){ 
      perror("pipe");
      exit(EXIT_FAILURE);
    }
    

    The first call to parent_process closes the pipe descriptors, and they are not reopened for the second iteration of the loop, so there is nothing to dup.

    The simplest solution would be to move the pipe call in to the loop.