----extPart_ST_12_56_33_Tuesday_October_26_2004_13396
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,

I have made a class, called CommandRunner, that I have been experiencing
some unusual behaviour with. I have lived with it for around two months
now, hoping I will stumble across a solution eventually. But alas, no.
:( I designed the class to provide the following functions:

    - runs other programs in a child process (with exec()),
    - allows each command-line argument to be explicitly passed in as
      individual arguments of the CommandRunner's constructor, and
    - provides IO streams to the parent for the child's standard out,
      standard error, and standard in streams.
    
As far as I know, the existing available functions (such as Kernel.popen
and Kernel.system) can provide these, but one that does them all does
not exist. For instance, popen gives you only one IO stream for reading
the standard in AND error, and writing standard in; not seperate IO
streams for standard in and error. It also doesn't let you specify
command-line arguments seperately (according to the documentation,
atleast). If one of your arguments have spaces, then I think you will
need to quote it or something - I'm not sure.

Basically, when I run it and redirect output from the script to the
shell, I get additional output from the child process being run by
CommandRunner, when it should not be there.

To better explain, run the command_runner_shell_test.rb (file attached)
as follows, to see the wackiness:

    $ ruby command_runner_shell_test.rb > output.txt
    $ cat output.txt
    Hello, world! 2 times!
    Hello, world! 2 times!
    Hello, world! 2 times!

It should only print two times...

Increase the loop in command_runner_shell_test.rb to five times, and
watch the excess prints grow (what seems to be roughly) exponentially.

Uncomment the second block in the command_runner_shell_test.rb script,
and see it all get jumbled. More wackiness. o_O

All the weird thread and waiting for the 'R' character, trickery is so
that I can guaranty that by the time I am calling the waitpid function,
the process and PID still exist. In other words, it is possible for the
child process to be so quick that it finishes executing before waitpid()
gets called). So on a sidenote, does anyone know a cleaner way of
performing interprocess communication? Like how do I tell the forked,
child process that the parent is ready and waiting.

Another problem is sometimes it just hangs at the 'exec' line. I see the
process and command line being run from 'ps aux', but it doesn't seem to
ever exit. But one problem at a time. I suspect this one might be linked
with the above. Something to do with the file streams not being ready, I
think?

Take a look, and have fun figuring it out. :P

Regards,

Steven
-- 
NAUTRONIX LTD
Marine Technology Solutions

Steven Wong
Undergraduate Software Engineer

Nautronix Ltd ABN 28 009 019 603
108 Marine Terrace, Fremantle, WA 6160, Australia
T +61 (0)8 9431 0000, F +61 (0)8 9430 5901, http://www.nautronix.com

E steven.wong / nautronix.com.au
T +61 (0)8 9431 0024
M +61 (0)413 332 005

--
This email is confidential and intended solely for the use of the individual to whom it is addressed.  
Any views or opinions presented are solely those of the author and do not necessarily represent those of NAUTRONIX LTD.

If you are not the intended recipient, you have received this email in error and use, dissemination, forwarding, printing, or copying of this email is strictly prohibited.  If you have received this email in error please contact the sender.   

Although our computer systems use active virus protection software, and we take various measures to reduce the risk of viruses being transmitted in e-mail messages and attachments sent from this company, we cannot guarantee that such e-mail messages and attachments are free from viruses on receipt.  It is a condition of our using e-mail to correspond with you, that any and all liability on our part arising directly or indirectly out of any virus is excluded.  Please ensure that you run virus checking software on all e-mail messages and attachments before reading them.
----extPart_ST_12_56_33_Tuesday_October_26_2004_13396
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="command_runner.rb"

class CommandRunner
    attr :command
    attr :childPid

    def initialize(*command)
        @command        ommand
        @childPid       il
        @waitingThread  il
        
        @readPipe       il
        @readErrorPipe  il
        @writePipe      il
    end

    def closeWrite
        @writePipe.close
    end

    def kill
        Process.kill("KILL", @childPid)
    end

    def read
        return @readPipe.read
    end

    def readError
        return @readErrorPipe.read
    end

    def run
        parent_to_child_read, parent_to_child_write              O.pipe
        child_to_parent_read, child_to_parent_write              O.pipe
        child_to_parent_error_read, child_to_parent_error_write  O.pipe
        
        @childPid  ork do
            parent_to_child_write.close
            child_to_parent_read.close
            child_to_parent_error_read.close
            
            $stdin.reopen(parent_to_child_read) or
                    raise "Unable to redirect STDIN"
            $stdout.reopen(child_to_parent_write) or
                    raise "Unable to redirect STDOUT"
            $stderr.reopen(child_to_parent_error_write) or
                    raise "Unable to redirect STDERR"

            # Wait until parent is ready before we start doing anything
            if parent_to_child_read.readchar.chr ! R"
                raise "Unexpected input from parent"
            end

            exec(*@command)
        end

        @waitingThread  hread.new do
            return_code  1
            
            begin
                return_code  rocess.waitpid2(@childPid)
            rescue SystemError
                raise "Process finished running already!"
            end
            return_code  eturn_code[1].exitstatus

            return_code
        end

        child_to_parent_write.close
        child_to_parent_error_write.close
        parent_to_child_read.close

        @readPipe       hild_to_parent_read
        @readErrorPipe  hild_to_parent_error_read
        @writePipe      arent_to_child_write

        # Tell child we are ready
        @writePipe.write("R")
        @writePipe.flush
    end

    #--------------------------------------------------------------------------
    # Description: Waits for command to exit
    # Returns    : The return code of the program when it exited
    #--------------------------------------------------------------------------
    def wait
        if not @childPid or not @waitingThread
            raise "Waiting for a process that has not started"
        end
        
        return_value  waitingThread.value
        
        @waitingThread  il
        @childPid  il

        return return_value
    end

    def write(string)
        @writePipe.write(string)
    end
end

----extPart_ST_12_56_33_Tuesday_October_26_2004_13396
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="command_runner_shell_test.rb"

require 'command_runner'

2.times do
    command_runner  ommandRunner.new("/bin/echo", "Hello, world! 2 times!")
    command_runner.run
    puts command_runner.read
end

# Uncomment the following as well for more wackiness

# 3.times do
#     command_runner  ommandRunner.new("/bin/echo", "Hello, world! 3 times!")
#     command_runner.run
#     puts command_runner.read
# end

----extPart_ST_12_56_33_Tuesday_October_26_2004_13396--