0

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!

Community
  • 1
  • 1
Big Dude
  • 264
  • 1
  • 6
  • 25
  • 1
    If you're using `./pipeline echo hello | wc` to run your program, then it's not doing what you think it is. Your program is seeing `argv[1] == echo`, `[2] == hello` and _nothing else_. The (combined) output of your program is then piped into `wc`. To (fully) achieve what I think you want to do, you need to escape the pipe character and detect it in your code (so you know which parts of the command-line to pass into each child process). – TripeHound Nov 14 '16 at 11:14
  • @TripeHound , I escaped the character manually by typing ./pipeline echo hello \| wc and it kind of worked for me, except the fact the second child starts wc in the interactive mode (waits for input and ctrl+d to mark end of input). This gives me the correct result of 1 1 6. This means that the input of the first child is ignored. Any ideas ? – Big Dude Nov 14 '16 at 14:26

1 Answers1

0

Solved it myself.

Forgot to use dup2.

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

Big Dude
  • 264
  • 1
  • 6
  • 25