0
votes

I'd execute this command ls -al / | tr a-j A-J | tr k-z K-Z and I'd create three children from father process.

I know that I've to open two pipes, let father waits for all his children (closing all the pipes) and in the children I've to

  • close STDIN on the first child
  • close STDOUT on the first pipe and STDIN on the second pipe, on the second child
  • close STDOUT on the third child

Now, this is my code. If I run it with just two children, it works. But, if I try to run it with three children, it doesn't work.

//  command is ls -al / | tr a-j A-J | tr k-z K-Z

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv) {
int fd1[2];
int fd2[2];
pid_t pid = 0;

pipe(fd1);
pipe(fd2);

pid = fork();
if(pid == 0) {
    close(fd1[0]);
    dup2(fd1[1], 1);
    close(fd1[1]);

    execlp("ls", "ls", "-al", "/", NULL);
}

pid = fork();
if(pid == 0) {
    close(fd1[1]);
    dup2(fd1[0], 0);
    close(fd1[0]);

    close(fd2[0]);
    dup2(fd2[1], 1);
    close(fd2[1]);

    execlp("tr", "tr", "a-j", "A-J", NULL);
}

pid = fork();
if(pid == 0) {
    close(fd2[1]);
    dup2(fd2[0], 0);
    close(fd2[0]);

    execlp("tr", "tr", "k-z", "K-Z", NULL);
}

close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);

while ((pid = wait(NULL)) != -1);

exit(0);
}

Thank you in advance.

1
You never close any of the pipe ends in the parent. The child that is reading from the pipe is waiting for all write ends of the pipe to close, but the parent is still holding one end open. Close it.William Pursell
I close pipes at the end. I've to close them at the beginning?Federico Cuozzo
tr is not going to terminate until all the write ends of the pipe are closed. If the parent waits for tr to terminate, it must close the end of the pipe before it waits, or the two process will be deadlocked.William Pursell

1 Answers

0
votes

You could use pipe2() with close_on_exec flag set. The following code works:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv) {
  int fd1[2];
  int fd2[2];
  pid_t pid = 0;

  pipe2(fd1,O_CLOEXEC);
  pipe2(fd2,O_CLOEXEC);

  pid = fork();
  if(pid == 0) {
    dup2(fd1[1],1);
    execlp("ls", "ls", "-al", "/", NULL);
  }

  pid = fork();
  if(pid == 0) {
    dup2(fd1[0],0);
    dup2(fd2[1],1);
    execlp("tr", "tr", "a-j", "A-J", NULL);
  }

  pid = fork();
  if(pid == 0) {
    dup2(fd2[0],0);
    execlp("tr", "tr", "k-z", "K-Z", NULL);
  }

  close(fd1[0]);
  close(fd1[1]);
  close(fd2[0]);
  close(fd2[1]);

  while ((pid = wait(NULL)) != -1);

  exit(0);
}

It is possible to use pipe() call also, but you should take care to close descriptors everywhere manually, which is error prone (you demonstrated that by posting this problem ;-) ):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv) {
  int fd1[2];
  int fd2[2];
  pid_t pid = 0;

  pipe(fd1);
  pipe(fd2);

  pid = fork();
  if(pid == 0) {
    close(fd1[0]);
    dup2(fd1[1], 1);
    close(fd1[1]);

    execlp("ls", "ls", "-al", "/", NULL);
  }

  pid = fork();
  if(pid == 0) {
    close(fd1[1]);
    dup2(fd1[0], 0);
    close(fd1[0]);

    close(fd2[0]);
    dup2(fd2[1], 1);
    close(fd2[1]);

    execlp("tr", "tr", "a-j", "A-J", NULL);
  }

  pid = fork();
  if(pid == 0) {
    close(fd1[0]);
    close(fd1[1]);
    close(fd2[1]);
    dup2(fd2[0], 0);
    close(fd2[0]);

    execlp("tr", "tr", "k-z", "K-Z", NULL);
  }

  close(fd1[0]);
  close(fd1[1]);
  close(fd2[0]);
  close(fd2[1]);

  while ((pid = wait(NULL)) != -1);
}