rubypopen3

Spawn a 'daemon' process from Ruby that doesn't exit when ruby exits


I'd like to spawn a process mycmd from Ruby:

  1. I'd like Ruby to block until mycmd writes BOOTED to stdout.
  2. When Ruby exits mycmd should keep running as a daemon.

I have Ruby blocking until mycmd writes BOOTED, but I can't get mycmd to hang around as a daemon.

stdin, stdout, stderr, process_status = Open3.popen3("mycmd --whatever")
puts "Starting mycmd, pid=#{process_status.pid}"

# Block until mycmd prints "BOOTED":
stdout.each_line do |line|
  break if line.include?("BOOTED")
end

Process.detatch(process_status.pid)

# And now our ruby process exits, killing mycmd as it dies
# How can I keep mycmd running even when Ruby exits?

Things I've tried:

  1. Open3.popen3("nohup mycmd") # mycmd exits with ruby
  2. Open3.popen3("mycmd &") # mycmd exits with ruby
  3. Open3.popen3("mycmd > /dev/null 2>&1") # mycmd does NOT exit when ruby exits (yay), but I can't read from stdout to wait for BOOTED

Solution

  • The likely problem here is that the standard output streamsof your child process are owned by the parent process. Once the parent process is killed, the stdout and stderr descriptors in the child process are invalid, and any attempt to use them will fail with an error such as:

    Broken pipe @ rb_io_flush_raw - <STDOUT>
    

    Since it sounds like you're attempting to launch the child as a daemon, it would be best to avoid trying to directly capture the stdout or stderr of the child. As mentioned in the comments by Casper, setting the child stdout and stderr to a file is reasonable, and this could also be done in the child process via STDOUT.reopen(...)