-1

I want to communicate with a child process like the following:

int main(int argc, char *argv[])
{
    int bak, temp;
    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[0]);
    dup2(STDOUT_FILENO, fd[1]);
    fflush(stdout);
    bak = dup(1);
    temp = open("/dev/null", O_WRONLY);
    dup2(temp, 1);
    close(temp  );
    Mat frame;
    std::vector<uchar> buf;
    namedWindow( "Camera", WINDOW_AUTOSIZE );
    VideoCapture cam(0 + CAP_V4L);
    sleep(1);
    if (!cam.isOpened())
    {
        cout << "\nCould not open reference " << 0 << endl;
        return -1;
    }
    for (int i=0; i<30; i++)
    {
        cam>>frame;
    }
    //cout<<"\nCamera initialized\n";
    /*Set the normal STDOUT back*/
    fflush(stdout);
    dup2(bak, 1);
    close(bak);

    imencode(".png",frame, buf);
    cout<<buf.size()<<endl;
    ssize_t written= 0;
    size_t s = 128;
    while (written<buf.size())
    {
     written += write(fd[1], buf.size()+written, s);

    }
    cout<<'\0';
    return 0;

}

The process corresponding to the compilation of the source code above is called from the parent with popen.

Note that I am writing to the std out that has been duplicated with a pipe.

The parent will read the data and resend them to UDP socket.

If I do something like this:

#define BUFLEN 128
FILE *fp;
char buf[BUFLEN];
if ((fp = popen("path/to/exec", "r")) != NULL)
{
    while((fgets(buf, BUFLEN, fp)!=NULL))
    {
        sendto(sockfd, buf, strlen(buf),0, addr, alen);
    }
}

the program is working i.e. the receiver of sendto will receive the data.

I tried to use a pipe as done in the child process:

    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[1]);
    dup2(STDIN_FILENO, fd[0]);
    if ((fp = popen("path/to/exec", "r")) != NULL)
    {
        while((read(fd[0], buf, BUFLEN) > 0)
        {
            sendto(sockfd, buf, strlen(buf),0, addr, alen);
        }
    }

but with this are not sent.

So how to use pipe in this case to achieve the same behaviour of the first case? Should I do dup2(STDIN_FILENO, fd[0]); or dup2(STDOUT_FILENO, fd[0]);?

I am using the sandard(s) since the file descriptors are inherited by the child process so should not require any other effort. That is why I thought I can use pipe but is that so?

roschach
  • 8,390
  • 14
  • 74
  • 124
  • 1
    I don't understand what you're trying to achieve here. The point of `popen()` is to allow you to *avoid* managing pipes and file descriptors directly. If you want more flexibility or a different interface, then you can always perform the pipe management and fork / exec manually, in the usual way. But what is mixing the two supposed to do for you? – John Bollinger Feb 03 '19 at 18:41
  • Yes I know and I told also in the question that `popen` works but I want to understand why the other way (with `pipe`) it is not working. That's all I am trying to achieving. – roschach Feb 04 '19 at 13:38
  • 1
    BTW there's a lot of code here that isn't at all relevant to your pipe issue, and apart from seeing the problem with your two pipes, I can't really tell what you're trying to achieve. Both your child samples _also_ use `popen`, which is kind of confusing, and I don't know what specifically you mean by "this are not sent." What isn't sent, where, and how did you tell, and what did you _expect_ to be sent? – Useless Feb 04 '19 at 15:23
  • Why the downvote? – roschach Feb 10 '19 at 13:34

2 Answers2

2

In the parent:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[0]);

you get a pipe, and then immediately close one end of it. This pipe is now useless, because no-one will ever be able to recover the closed end, and so no data can flow through it. You have converted a pipe into a hollow cylinder sealed at one end.

Then in the child:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[1]);

you create another unrelated pipe, and seal this at the other end. The two pipes are not connected, and now you have two separate hollow cyclinders, each sealed at one end. Nothing can flow through either of them.

If putting something in the first cylinder made it appear in the other, that'd be a pretty good magic trick. Without sleight of hand or cleverly arranged mirrors, the solution is to create one pipe, keep both ends open and push data through it.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Then when I do `write` in the child process what shall I use? – roschach Feb 04 '19 at 15:12
  • You can only write to `fd[1]` and read from `fd[0]`. Your parent has both ends, and the child inherits both ends. It is up to you to choose how to use them. I don't really know what you want to communicate between parent and child processes, and I don't know what you're trying to achieve, so I can't really advise you on how to achieve it. – Useless Feb 04 '19 at 15:17
  • NB. [this](https://stackoverflow.com/a/11461302/212858) shows a trivial example of using `socketpair` in a similar situation - that would allow bidirectional messages, but the code as written would work identically with `pipe`. – Useless Feb 04 '19 at 15:20
  • Another doubt you said those pipes are unrelated but I am duplicating the standard output in those pipes so somehow I am making them related: is not that so? I am not denying the fact they might be completely useless though since I could write directly to std output at this point. – roschach Feb 05 '19 at 11:03
  • You call `pipe` twice, you get two _different_ pipes. Saying they're related because you dup'd one end of one onto stdout, and the other end of the other onto stdin, is like saying two people are related because they sat on the same bus. – Useless Feb 05 '19 at 11:44
  • Your similitude is not explanatory at all but ok: they are not related. – roschach Feb 05 '19 at 11:51
1

The usual way to manually set up a pipe from which a parent process can read a child process's standard output has these general steps:

  • parent creates a pipe by calling pipe()
  • parent fork()s
  • parent closes (clarification: its copy of) the write end of the pipe
  • child dupes the write end of the pipe onto its standard output via dup2()
  • child closes the original file descriptor for the write end of the pipe
  • (optional) child closes (clarification: its copy of) the read end of the pipe
  • child execs the desired command, or else performs the wanted work directly

The parent can then read the child's output from the read end of the pipe.

The popen() function does all of that for you, plus wraps the parent's pipe end in a FILE. Of course, it can and will set up a pipe going in the opposite direction instead if that's what the caller requests.

You need to understand and appreciate that in the procedural scheme presented above, it is important which actions are performed by which process, and in what order relative to other actions in the same process. In particular, the parent must not close the write end of the pipe before the child is launched, because that renders the pipe useless. The child inherits the one-end-closed pipe, through which no data can be conveyed.

With respect to your latter example, note also that redirecting the standard input to the read end of the pipe is not part of the process for either parent or child. The fact that your pipe is half-closed, so that nothing can ever be read from it anyway, is just icing on the cake. Moreover, the parent clobbers its own standard input this way. That's not necessarily wrong, but the parent does not even rely on it.

Overall, however, there is a bigger picture that you seem not to appreciate. Even if you performed the redirection you seem to want in the parent, so that it could be inherited by the child, popen() performs its own redirection to a pipe of its own creation. The FILE * it returns is the means by which you can read the child's output. No previous output redirection you may have performed is relevant (clarification: of the child's standard output).

In principle, an approach similar to yours could be used to create a second redirection going the other way, but at that point the convenience factor of popen() is totally lost. It would be better go take the direct pipe / fork / dup2 / exec route all the way through if you want to redirect the child's input and output.


Applying all that to your first example, you have to appreciate that although a process can redirect its own standard streams, it cannot establish a pipe to its parent process that way. The parent needs to provide the pipe, else it has no knowledge of it. And when a process dupes one file descriptor onto another, that replaces the original with the new, closing the original if it is open. It does not redefine the original. And of course, in this case, too, a pipe is useless once either end is no longer open anywhere.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • So basically if I have understood correctly `fd` in the child process is wrong. Then which what file descriptor shall I pass to `write`? – roschach Feb 04 '19 at 15:11
  • Do you mean in the first code, @FrancescoBoi? In that case, if the parent is launching the child via `popen()`, as in your second code, then the child just writes to its standard output. It does not need to be aware of the redirection at all. That's the whole point of redirection in general, and of the redirection performed by `popen()` in particular. It would be *far* less useful if it depended on the child process doing something special. – John Bollinger Feb 04 '19 at 15:26
  • Doing something special after the `exec`, that is. – John Bollinger Feb 04 '19 at 15:32