tags:

views:

52

answers:

1

I've got a little C server that needs to accept a connection and fork a child process. I need the stderr of the child process to go to an already existing named pipe, the stdout of the child to go to the stdout of the parent, and the stdin of the child tp come from the same place as the stdin of the parent.

My initial attempts involved popen() but I could never seem to get quite what I wanted.

Finally, this particular solution only needs to work in Solaris. Thanks.

EDIT: Updated the question in hopes of more accurately portraying what I'm trying to accomplish. Thanks for being patient with me.

EDIT2: I also need the parent to get the return value of the child process and then do something with it if that makes any difference.

+3  A: 

You might be using the wrong function - popen() is used when you want the invoking program either to write to the forked process's standard input or read from its standard output. It seems you want neither. It also takes two arguments.

Your requirements are also somewhat contradictory:

  • I want it to (ideally) inherit stdin and stdout from the parent

  • any input to the parent goes to the child and any output from the child goes back to the parent

  • but at a minimum, I'd like it to inherit stdin and write stdout to a named pipe

The first option is easy - it requires no special coding. Any data supplied to the stdin of the parent will also be available on the stdin of the child (but only one of the two processes will get to read it). The child's stdout will normally go to the same place as the parent's stdout. If you want the parent to read the child's stdout, then you do need a pipe - and popen() is then appropriate, but the 'at minimum' stuff is confusing.

So, let's define what you really want?

Option 1

  1. The standard error of the child should go to a named pipe.
  2. The standard output of the child should be read by the invoking process.
  3. The standard input of the child should come from the same place as the standard input of the parent.
  4. The named pipe already exists.

Hence:

FILE *fp = popen("/run/my/command -with arguments 2>/my/other/pipe", "r");

Note that the child will be hung until a process opens '/my/other/pipe' for reading; that in turn means that if the parent process reads from fp, it too will be hung until some other process opens '/my/other/pipe' for reading.

Option 2

  1. The standard error of the child should go to a named pipe.
  2. The standard output of the child should go to the standard output of the parent.
  3. The standard input of the child should come from the same place as the standard input of the parent.
  4. The named pipe already exists.

Now popen() is not appropriate, and we get into naked `fork & exec' code. What follows is more pseudo-code than operational C.

if ((pid = fork() < 0)
    error
else if (pid > 0)
{
    /* Parent - might wait for child to complete */
}
else
{
    int fd = open("/my/other/pipe", O_WRONLY|O_NONBLOCK);
    if (fd < 0)
        error
    dup2(fd, 2);  /* There is a symbolic name for stderr too */
    close(fd);    /* Do not want this open any more */
    char *cmd[4] = { "/bin/sh", "-c", "/run/my/command -with arguments", 0 };
    execv(cmd[0], cmd);
    error - if execv returns, it failed!
}

If you're totally confident no-one has pulled any stunts on you like closing stdout, you can avoid using dup2() by closing stderr (fd = 2) before calling open(). However, if you do that, you can't report any errors any more - because you closed stderr. So, I would do it as shown.

If you have a different requirement, state what you want to achieve.


As noted by p2vb, if you want the parent to wait for the child to finish, then simply using system() may be sufficient. If the parent should continue while the child is running, you might try system() where the command string ends with an ampersand (&) to put the child into the background, or you might use the code outlined in Option 2 above.

Using system(), the parent will have little chance to read the /my/other/pipe which gets the standard error from the child. You could easily deadlock if the child produces a lot.

Also, be careful with your FD_CLOEXEC flag - set it on files that you don't want the child modifying. On Linux, you can use the O_CLOEXEC flag on the open() call; with Solaris, you have to set it via fcntl() - carefully.

Jonathan Leffler
Option 2 is EXACTLY what I'm trying to achieve. My apologies for not stating that more clearly in my question; my knowledge in this area is fuzzy at best, so it's hard for me to accurate express what I'm trying to accomplish.
Morinar
If that's what you want to do, another option would be to just fork() and then system(), using the shell redirection feature to redirect stderr into your named pipe.
pv2b
Tons of great info here and I learned a lot. Thanks all around for the help.
Morinar