I have code where I want to encapsulate a particular piece of code into a forked child so that if the code causes issues, it will not affect the parent and the parent will be able to report.
I am not exec
ing after the fork
call. Just plain fork
and then the code runs that would have normally run in the parent.
The code in the child outputs stuff to stdout
and stderr
. Due to issues with interleaving output and stdio
buffering, I was now looking into TLPI for a better solution.
The idea I came up with was roughly:
pipe()
for each streamfork()
close()
read end of pipesdup2()
the write ends onto the file descriptors for stdout
and stderr
respectivelysetbuf(fd, NULL)
to turn off stdio
stream buffering inside the child.close()
write ends of pipesselect
/pselect
/epoll
etc (has to run across Linux, BSDs, AIX, Solaris etc) to watch the read ends of the pipes for new data and when it arrives write()
it straight to the respective file descriptors in the parent process.Now, I presume there is one step missing between the dup2
and the setbuf
inside the child. What is it?
setbuf
takes a FILE*
whereas of course dup2
acts on int
.
freopen
came to mind, but it requires a path. If I merely want to assign a new fileno to the streams, how can I do that?
The extern
variables stdin
, stdout
and stderr
are FILE *
pointers. You can pass these to setbuf
/ setvbuf
. However, note that the child has a separate address space so whilst it will inherit the state of the buffers at the time of the fork()
, both can continue to use the buffers safely, and if you fflush
STDOUT
and STDERR
, the output buffers will be empty anyway.
This is the way I would do it in the child (untested, and please do add some error handling):
void
child ( ... )
{
const char *devnull = "/dev/null";
/* turn off buffering */
setvbuf (stdin, NULL, _IONBF);
setvbuf (stdout, NULL, _IONBF);
setvbuf (stderr, NULL, _IONBF);
for (i = getdtablesize () - 1; i >= 0; i--)
{
if ((i != write_end_of_pipe1) && (i != write_end_of_pipe2))
close (i);
}
i = open (devnull, O_RDWR);
if (i == -1)
{
fprintf (stderr, "Unable to open /dev/null\n");
return;
}
i = open (devnull, O_RDONLY);
if (i != 0)
{
dup2 (i, 0);
close (i);
}
dup2 (write_end_of_pipe1, 1);
close (write_end_of_pipe1);
dup2 (write_end_of_pipe2, 2);
close (write_end_of_pipe2);
return;
}