Fork me on GitHub

Daemonizing ruby

21 November 2008

This edition of PoolParty internals is going to show you how to run a block of ruby. Considering that you know a little bit of ruby, I’ll be going a little faster in this post.

There are a few commands right now in PoolParty that allow you to daemonize the process with the -d flag. (Future post on extending the OptionParser). This will place the block of code in the binary into the daemonized mode.

From within PoolParty, this command looks like:

      
def daemonize(&block)
        vputs "Daemonizing..."

        pid = fork do
          Signal.trap('HUP', 'IGNORE') # Don't die upon logout
          File.open("/dev/null", "r+") do |devnull|
            $stdout.reopen(devnull)
            $stderr.reopen(devnull)
            $stdin.reopen(devnull) unless @use_stdin
          end
          block.call if block
        end
        Process.detach(pid)
        pid
      end

I’ll walk through this snippet of ruby with you.

First off, vputs is a PoolParty internal to print verbosely, so we can ignore that for now. Basically, it calls put if verbose is set on the containing object. Until the post referenced above is written, just trust that it puts if verbose is set.

Second, we use Kernel.fork to create a subprocess from within ruby. Second, we tell the subprocess not to quit if it receives the linux signal ‘HUP.’ This way, we can safely ignore the process and assume it will continue to run even if we do log out or our OS sends it a HUP signal.

Then we’ll pipe the output to /dev/null so that we don’t see any output on the daemonzied process. It would be odd to daemonize a process and see output after we’ve forked it off.

Finally, we’ll use the block that we’ll send it when calling daemonize before detaching the process from this process. Detaching the process will set the process apart from this parent process and assume that it is it’s own parent process. This way, our process won’t receive any errors if the forked one does.

You can call daemonize with a block, like so

  daemonize do
    sum = (1..12000000).inject(0) do |sum,i|
     sum + i
    end

    File.open("/tmp/sum", "w+") {|f| f << sum}
  end

That completes this edition of inside PoolParty. Stay tuned for the next one!

Code has been attached daemonize.

Update

def daemonize(&block)
  trap("CHLD") {Process.wait(-1, Process::WNOHANG)}
  fork do
    Signal.trap('HUP', 'IGNORE') # Don't die upon logout
    File.open("/dev/null", "r+") do |devnull|
      $stdout.reopen(devnull)
      $stderr.reopen(devnull)
      $stdin.reopen(devnull) unless @use_stdin
    end
    block.call if block
  end
end

This sets the flag ruby to not block if no child is available and waits for any child process of the current execution block (see Process)

Also, one more note: my MacBook is officially dead and en route to Apple, so updates to PoolParty may be slow this week.