0

I am working on a project and I got it mostly figured out except for one minor(big) problem. I can't seem to figure out how to create pipes between any number of children.

for example I am taking in command line arguments to determine how many children will be produced. The first child doesn't have input but has output and the last child outputs to STD output. I need to pass values into the first child and into each child after that in order. Here is what i got:

#include <errno.h>
#include <cstdio>
#include <iostream>
#include <sys/wait.h>

using namespace std;

int main(int argc, char *argv[]) {
    pid_t childpid;
    int x2ypipe[2];
    pipe(x2ypipe);
    if(x2ypipe==0) {
        cout<<"ERROR:"<<errno<<endl;
    }
    int y2zpipe[2];
    pipe(y2zpipe);
    if(y2zpipe==0) {
        cout<<"ERROR:"<<errno<<endl;
    }
    pid_t xchild =fork();
    if(xchild==0) {
        dup2(x2ypipe[1],STDOUT_FILENO);
        close(x2ypipe[0]);
        close(x2ypipe[1]);
        int a=execl(argv[1],argv[1], (char*)NULL);
        if(a==-1) {
            perror("The following error occurred at A");
        }
    }
    for(int i=2; i<(argc-1); i++) {
        childpid =fork();
        if(childpid==0) {
            dup2(x2ypipe[0],STDIN_FILENO);
            close(x2ypipe[0]);
            close(x2ypipe[1]);
            //direct y2z pipe to standard output and replace the child with the program part2
            dup2(x2ypipe[1],y2zpipe[1]);
            dup2(y2zpipe[1],STDOUT_FILENO);
            close(y2zpipe[0]);
            close(y2zpipe[1]);
            int b=execl(argv[i],argv[i],(char *)NULL);
            if(b==-1) {
                perror("The following error occurred at B");
            }
        }
    }
    pid_t zchild =fork();
    if(zchild==0) {
        dup2(y2zpipe[0],STDIN_FILENO);
        close(y2zpipe[0]);
        close(y2zpipe[1]);
        int c=execl(argv[argc-1],argv[argc-1],(char *)NULL);
        if(c==-1) {
            perror("The following error occurred at C");
        }
    }
    close(x2ypipe[0]);
    close(x2ypipe[1]);
    wait(NULL);
    wait(NULL);
    wait(NULL);
}

now right now I am only passing in three programs in to the argv[] and it works fine. I will have to add a if statement in my for loop to check for the last/highest possible value of i to connect the y2z pipe to the zchild. What I am having trouble doing it connecting the children to each other within the for loop. How would I go about creating a new pipe for each child from the last child?

sehe
  • 374,641
  • 47
  • 450
  • 633
user975044
  • 375
  • 4
  • 11
  • 26
  • 5
    Wow, 18 lines of _just_ `#includes` really do take 80% of screen real estate. Have you heard of _minimal working example_? – sehe Nov 01 '11 at 21:44
  • 3
    Please don't `#include` both C and C++ versions of headers. Pick either one and stick to it. – Fred Foo Nov 01 '11 at 21:46
  • 1
    I have answered this question for C before: [How do I chain stdout in one child process to stdin in another child in C?](http://stackoverflow.com/questions/7281894/how-do-i-chain-stdout-in-one-child-process-to-stdin-in-another-child-in-c/7282296#7282296) That might be of help for you, since it doesn't look like you're really using C++ (hint: using `cout` doesn't make it C++). – R. Martinho Fernandes Nov 01 '11 at 21:53
  • 1
    There, FTFY: 14 redundant includes gone (g++, linux). I'm afraid the rest of it is beyond my repair skills – sehe Nov 01 '11 at 21:58
  • thanks, sehe(can you tell I have little to no experience with c/c++ lol) and Fernandes thanks this is basically what I am trying to accomplish I will see if I can something like this working with my program. – user975044 Nov 01 '11 at 22:08
  • 2
    @sehe: I like how your name looks like "hehe" when you write funny comments – Lightness Races in Orbit Nov 01 '11 at 22:32

2 Answers2

1

Maybe this will help. Notice how I call pipe() inside my for loop, so I don't have to think of new "x2y", "y2z", "z2omega", etc, etc names for the pipe pairs.

Also notice how I used a variable prevfd from outside the for loop to carry the previous iterations's pipe file descriptor into the next iteration. And how it points to "/dev/null" to start with.

Finally, notice how I call wait() precisely as many times as I need to, in a loop, rather than writing it 3 (or 4 or 5 or ... 1,397) times.

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio>
#include <sys/wait.h>

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

  int prevfd;

  prevfd = open("/dev/null", O_RDONLY);

  if(prevfd < 0) {
    perror("/dev/null");
    exit(1);
  }

  for(int i = 1; i < argc; ++i) {
    int pipefd[2];
    int kid;

    if(i != argc-1 && pipe(pipefd)) {
      perror("pipe");
      break;
    }

    if(!fork()) {
      dup2(prevfd, 0);
      close(prevfd);
      if(i != argc-1) {
        dup2(pipefd[1], 1);
        close(pipefd[0]);
        close(pipefd[1]);
      }
      execl(argv[i], argv[i], (char*)0);
      perror(argv[i]);
      exit(1);
    }

    close(prevfd);
    prevfd = pipefd[0];
    close(pipefd[1]);
  }
  while(wait((int*)0) != -1)
    ;
  return 0;
}
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
0

You need a separate pipe between each pair of connected processes.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547