Search code examples
rubydaemonsthor

How can I create a daemon with Thor (ruby)?


I would like to use the popular Thor gem to create a daemonized task. My Thor class looks like this:

require 'rubygems'
require 'daemons'
require 'thor'

class CLI < Thor
  desc "start", "Startup the App"
  method_option :daemonize, :aliases => "-d", :default => false, :type => :boolean, :banner => "Run as daemon"
  def start
    run_app(options[:daemonize])
  end

  desc "stop", "Stop the daemon"
  def stop
    stop_app
  end

  no_tasks {
    def run_app(run_as_daemon)
      # Run the application code
      Daemons.daemonize if run_as_daemon
      # loop until stopped or interrupted
      # ...
    end

    def stop_app
      #stop the app
    end
  }
end

So here I've setup a basic thor class with two tasks, start and stop. I'm also, currently using the Daemons gem, but that isn't required. The part that I'm struggling with is that when this app runs as "run_thor_app.rb start" everything runs just fine. Obviously the stop task isn't needed in this instance. But when I run "run_thor_app.rb start -d" the app runs until Daemons.daemonize runs and then it quits. Checking the running processes shows that nothing is running in the background.

Even if something were running, I wouldn't know how to approach the stop task. For example, how do you detect that the app is running as a daemon and stop it. I've looked at Daemons::Monitor, but the documentation isn't clear on how that works and when I tried it, it didn't work.

It seems to me that this would be a good use case for something that is built into Thor, but searching through the code on github hasn't revealed anything to me. Maybe I just missed it somewhere. In any case, I think it would be good to document a best practice or a pattern for handling daemons with Thor for others to reference.


Solution

  • The way you usually manage daemon processes is by having them write their PID in a file. This makes it possible for another process to discover the daemon's PID, and kill it (or send some other signal).

    Your code should work. I tried a bare bones script that used the deamons gem, and it took me a few tries until I found the deamonized process. I figured it would get the same name as the parent process, or something similar, but instead it's name was "self". Remember that the daemonized process will no longer write to STDOUT.

    Anyway, try this:

    # set up everything
    # then daemonize
    Daemons.daemonize
    # and write a pid file
    File.open('/tmp/mydaemon.pid', 'w') { |f| f.puts(Process.pid) }
    loop do
      # do something
      # this loop is important, if the script ends the daemon dies
    end
    

    and check the /tmp/mydaemon.pid file for the PID. Then run ps ax | grep x where x is the PID. Run cat /tmp/mydaemon.pid | xargs kill` to kill the daemon.

    I think the daemons' gem has some helpers for managing PidFiles, check out PidFile in http://rubydoc.info/gems/daemons/1.1.0/frames