From: Brian Candler Date: 2003-05-13T07:39:34+09:00 Subject: Re: RCR for child execution On Tue, May 13, 2003 at 05:45:45AM +0900, Simon Strandgaard wrote: > BOOM = system+backquote outputs to stdout/stderr, interfering with the > output of the C/C++ application. > > This interference is what to avoid. I want to be able to > redirect Rubys output elsewhere: logfile/statusbox/text-buffer. But the process you run with system/backquote is not Ruby, and therefore its output is not Ruby's output. It's a completely different program, run using fork + exec, but run in the same Unix environment as your main program (as it should be: fork simply duplicates the existing process) You can redirect Ruby's output elsewhere ($defout), but it's the child's output that you are asking about. Now, you can always replace 'system' with a function with runs a child, captures its output, and forwards it to a Ruby IO object. Matz suggested putting something like 'system2' in the the Ruby library for this purpose. But then you say that you want to do this without modifying existing Ruby code. You can always replace 'system' and 'backtick' with your own versions if you wish. But I still don't think it makes sense. The environment in which a Unix program runs looks, at its simplest, like this: MAIN +---------------------------------+ | | | FD | stdin -------> | 0 | stdout<------- | 1 | stderr<------- | 2 | | | | | +---------------------------------+ It can ignore stdin/stdout/stderr of course - as it would if it were a GUI program, say. Now, from this main program, you want to call a function, which happens to be written in Ruby. It must be true that either: * This function makes no use of stdin/stdout/stderr at all. In that case there is no problem, agreed? * This function *does* communicate on stdin/stdout/stderr. So I think you are saying in the second case you want to run it in a 'sandbox', a simulated Unix environment where it gets its own private stdin/stdout/stderr: +---------------------------------+ | | | +-----------+ | stdin -------> | 0 obj0-->| 0 Sandbox | | stdout<------- | 1 obj1<--| 1 | | stderr<------- | 2 obj2<--| 2 | | | +-----------+ | | | +---------------------------------+ I think you are asking for the Ruby interpreter to be the sandbox itself, and to trap every single possible case where output might be written to stdout/stderr, and have it redirect to Ruby IO calls instead. In the general case, this is simply not possible whilst the Sandbox is in the same process as you, because it shares fd0/1/2 with the the main program (because both Ruby and C *are* part of the same program), and therefore the program has a way to write to fd0/1/2. Rewriting 'system' and backtick would catch some cases, but certainly not all. The only way to guarantee trapping of all output on fd 1 in the Sandbox into some string object 'obj1' is to do it at the Unix API layer. This means creating a new pipe and pass one end of it to the code running in the Sandbox as fd 1. This then *forces* you to use a separate process, because it must be possible for the Sandbox to be doing work and writing to one end of the pipe whilst the main program is reading the other end of the pipe. (You could also use native threads, but Ruby doesn't play safely with threads) So to summarise: Case 1: the Ruby code doesn't use stdout/stderr at all Solution 1: Trivial, nothing to do. (Most well-written libraries will be in this category) Case 2: the Ruby code writes to stdout/stderr from Ruby, but does not in turn run any C or external programs which would write directly to Unix fd 1/2 (which includes exec'ing another program using fork/exec, system, backtick etc) Solution 2: Just reset $defout and now $deferr (thanks, Matz!) Case 3: the Ruby code calls other programs (via embedded C calls or exec) which in turn write directly to Unix stdout/stderr Solution 3a: Modify the Ruby code so that it captures that output itself at the appropriate points (e.g. using IO.popen) if possible or Solution 3b: When you call the Ruby code, first fork a subprocess which gets a whole new stdin/stdout/stderr on pipes (and therefore any C or external programs it calls will also communicate on those pipes). That's the solution 'capture_output' I gave before. > The problem is fairly simple, but I doubt anyone understands me ??? If I do understand you, then you are asking for something impossible. I think you would realise this if you understood the Unix execution environment a little better. Regards, Brian.