Search code examples
clinuxforksystem-callsexecvp

Pipe issue: first child writes too much in to pipe


I'm trying to write a program in C that creates 2 child processes with each one of them executing an execvp.

My problem is that the first child writes too much input into the pipe from which the other child reads.

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

//unnamed pipe
int pipeFd[2], statusFirst,statusSecond;
pid_t childPidOne,childPidTwo;

if(pipe(pipeFd) < 0){
    perror("Pipe error:\n");
    exit(EXIT_FAILURE);
}

switch(childPidOne = fork()){
    case -1:
        perror("First Fork error:\n");
        exit(EXIT_FAILURE);
    case 0:
        printf("First child\n");
        close(pipeFd[1]);
        if( (execvp(argv[1], &argv[1])) < 0){
            perror("First execvp error:\n");
        }
        printf("End First cild\n");
        exit(0);
    default:
        //Do nothing
        break;  
}

switch(childPidTwo = fork()){
    case -1:
        perror("Second Fork error:\n");
        exit(EXIT_FAILURE);
    case 0:
        printf("Second cild\n");
        close(pipeFd[0]);
        if( (execvp(argv[3], &argv[3])) < 0){
            perror("Second execvp error:\n");
        }
        printf("End Second cild\n");
        exit(0);
    default:
        //Do nothing
        break;  
}


close(pipeFd[0]);
close(pipeFd[1]);   


if( (waitpid(childPidOne,&statusFirst,WUNTRACED | WCONTINUED)) < 0 ){
    perror("First waitpid error:\n");
}else{
    if (WIFEXITED(statusFirst)) {
       printf("First exited, status=%d\n", WEXITSTATUS(statusFirst));
    } else if (WIFSIGNALED(statusFirst)) {
       printf("First killed by signal %d\n", WTERMSIG(statusFirst));
    } else if (WIFSTOPPED(statusFirst)) {
       printf("First stopped by signal %d\n", WSTOPSIG(statusFirst));
    } else if (WIFCONTINUED(statusFirst)) {
       printf("First continued\n");
    }
}


if( (waitpid(childPidTwo,&statusSecond,WUNTRACED | WCONTINUED)) < 0 ){
    perror("Second waitpid error:\n");
}
if (WIFEXITED(statusSecond)) {
   printf("Second exited, status=%d\n", WEXITSTATUS(statusSecond));
  } else if (WIFSIGNALED(statusSecond)) {
   printf("Second killed by signal %d\n", WTERMSIG(statusSecond));
  } else if (WIFSTOPPED(statusSecond)) {
   printf("Second stopped by signal %d\n", WSTOPSIG(statusSecond));
  } else if (WIFCONTINUED(statusSecond)) {
      printf("Second continued\n");
      }

   exit(0);
return 0;
  }

Maybe I have a wrong understanding of how pipe + fork + execvp work so let me tell you what I'm doing in my code:

  1. I create an unnamed pipe - both childs use the same pipe
  2. I'll create two childs by forking them
  3. Since I execute my program like this: ./pipeline [FIRST SYSTEM CALL] | [SECOND SYSTEM CALL] or just to give you an example:./pipeline echo Hello | wc -m I close the reading site of the pipe
  4. And then call execvp(argv[1], &argv[1])

And this is where the error happens (I guess): I am never closing the writing side until the second child does because execvp will never return if it succeeds.

And I know that execvp will not close open file descriptors ( it can be closed by using a flag in fcntl as mentioned in What does the FD_CLOEXEC flag do? ).

Example

Let me give you an example.

echo Hello | wc -m

outputs the result

6

Because the system call wc (word count) counts the characters (-m) in a given String

That is correct because hello = 5 + 1 (which is \n or \0 I guess) and that makes 6.

Now, running my program gives the result

56

Or to get more information

echo hello | wc

outputs

1 (line) 1 (word) 6 (characters)

And ./pipeline echo hello | wc

outputs

3 (lines) 9 (word) 56 (characters)

I've searched for days but I can't figure it out.

Any ideas?

Thanks a lot!


Solution

  • Solved it myself.

    Forgot to use dup2.

    Just type the dup2 command after the close command and you will be fine.