On Sat, May 10, 2003 at 10:26:11PM +0900, Simon Strandgaard wrote:
> I have some more to add to this issue.
> 
> 
> I am embedding ruby into c++ and want to seperate Rubys output from C++.
> 
> AFAIK: When ruby spawns a child-process, then the child inherits 
> filedescriptor 1 & 2 from parent. 
> 
> What im asking for is: is it possible for let the children inherit the
> value of rb_stdout, rb_stderr instead ???

From the Unix point of view, the child can be passed any open *Unix*
filehandles that you like.

For doing this sort of stuff from C, I strongly recommend the book "Advanced
Programming in the Unix Environment" by the late Richard Stevens (Addison
Wesley). It's a classic must-have reference books for anyone writing Unix
programs. In short, you open whatever files/pipes you like, then use the
function dup2() to assign them to fd 0/fd 1/fd 2.

If you look at the source for 'Open3::popen3' you'll see a way that you can
do this from Ruby using IO#reopen. Note that rb_stdout and rb_stderr that
you mention are just the constants STDOUT and STDERR in ruby:

io.c:    rb_define_global_const("STDOUT", rb_stdout);
io.c:    rb_define_global_const("STDERR", rb_stderr);

These objects are wrappers, or containers, which contain the relevant fds,
or in fact FILE* objects which are a stdio abstraction which in turn contain
the fds. Look at prep_stdio in io.c:

prep_stdio(f, mode, klass)
    FILE *f;
{
...
    MakeOpenFile(io, fp);
    fp->f = f;
    fp->mode = mode;
...
}

    rb_stdout = orig_stdout = prep_stdio(stdout, FMODE_WRITABLE, rb_cIO);

So whenever you do STDOUT.reopen you are really acting on the stdio object
"FILE *stdout" which in turn is a wrapper around an open file on fd 1.

Now, if you do
    system("foo")
that's the fd your child will share with the parent process as its stdout.

If you do
    any=%x{foo}
then Ruby opens a new pipe and hands one end of it as fd 1 to the child
process, and reads the local end into a string. However it doesn't touch fd
2, so the child process shares it.

The same applies with IO.popen("foo","r")

In the case of Open3.popen3 you get three separate pipes set up, and one end
of each of those pipes is dup2()'d into fd0, fd1 and fd2 of the child
process before exec'ing.

Is this any clearer??

Regards,

Brian.