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
- The standard error of the child should go to a named pipe.
- The standard output of the child should be read by the invoking process.
- The standard input of the child should come from the same place as the standard input of the parent.
- 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
- The standard error of the child should go to a named pipe.
- The standard output of the child should go to the standard output of the parent.
- The standard input of the child should come from the same place as the standard input of the parent.
- 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.