lib/dante/runner.rb in dante-0.0.4 vs lib/dante/runner.rb in dante-0.1.0

- old
+ new

@@ -13,12 +13,11 @@ =end module Dante class Runner - # Signal to application that the process is shutting down - class Abort < Exception; end + MAX_START_TRIES = 5 attr_accessor :options, :name, :description class << self def run(*args, &block) @@ -27,10 +26,11 @@ end def initialize(name, defaults={}, &block) @name = name @startup_command = block + @debug = defaults.delete(:debug) || true @options = { :host => '0.0.0.0', :pid_path => "/var/run/#{@name}.pid" }.merge(defaults) end @@ -42,40 +42,83 @@ end # Executes the runner based on options # @runner.execute # @runner.execute { ... } - def execute(&block) + def execute(opts={}, &block) parse_options + self.options.merge!(opts) if options.include?(:kill) - kill_pid(options[:kill] || '*') + self.stop else # create process + self.stop if options.include?(:restart) Process.euid = options[:user] if options[:user] Process.egid = options[:group] if options[:group] @startup_command = block if block_given? options[:daemonize] ? daemonize : start end end + def daemonize + return log("Process is already started") if self.daemon_running? # daemon already started + + # Start process + pid = fork do + exit if fork + Process.setsid + exit if fork + store_pid(Process.pid) + File.umask 0000 + STDIN.reopen "/dev/null" + STDOUT.reopen "/dev/null", "a" + STDERR.reopen STDOUT + start + end + # Ensure process is running + if until_true(MAX_START_TRIES) { self.daemon_running? } + log "Daemon has started successfully" + true + else # Failed to start + log "Daemonized process couldn't be started" + false + end + end + def start - puts "Starting #{@name} service..." + log "Starting #{@name} service..." trap("INT") { - stop + interrupt exit } trap("TERM"){ - stop + interrupt exit } @startup_command.call(self.options) if @startup_command end - def stop - raise Abort + # Stops a daemonized process + def stop(kill_arg=nil) + if self.daemon_running? + kill_pid(kill_arg || options[:kill]) + until_true(MAX_START_TRIES) { self.daemon_stopped? } + else # not running + log "No #{@name} processes are running" + false + end + end + + def restart + self.stop + self.start + end + + def interrupt + raise Interrupt sleep(1) end def parse_options headline = [@name, @description].compact.join(" - ") @@ -119,41 +162,57 @@ instance_exec(opts, &@with_options) if @with_options end.parse! options end - private + protected def store_pid(pid) - FileUtils.mkdir_p(File.dirname(options[:pid_path])) - File.open(options[:pid_path], 'w'){|f| f.write("#{pid}\n")} + FileUtils.mkdir_p(File.dirname(options[:pid_path])) + File.open(options[:pid_path], 'w'){|f| f.write("#{pid}\n")} end - def kill_pid(k) + def kill_pid(k='*') Dir[options[:pid_path]].each do |f| begin pid = IO.read(f).chomp.to_i FileUtils.rm f Process.kill('INT', pid) - puts "killed PID: #{pid} at #{f}" + log "Stopped PID: #{pid} at #{f}" rescue => e - puts "Failed to kill! #{k}: #{e}" + log "Failed to stop! #{k}: #{e}" end end end - def daemonize - pid = fork do - exit if fork - Process.setsid - exit if fork - store_pid(Process.pid) - File.umask 0000 - STDIN.reopen "/dev/null" - STDOUT.reopen "/dev/null", "a" - STDERR.reopen STDOUT - start + # Runs until the block condition is met or the timeout_seconds is exceeded + # until_true(10) { ...return_condition... } + def until_true(timeout_seconds, interval=1, &block) + elapsed_seconds = 0 + while elapsed_seconds < timeout_seconds && block.call != true + elapsed_seconds += interval + sleep(interval) end + elapsed_seconds < timeout_seconds + end + + # Returns true if process is not running + def daemon_stopped? + ! self.daemon_running? + end + + # Returns running for the daemonized process + # self.daemon_running? + def daemon_running? + return false unless File.exist?(options[:pid_path]) + Process.kill 0, File.read(options[:pid_path]).to_i + true + rescue Errno::ESRCH + false + end + + def log(message) + puts message if @debug end end end \ No newline at end of file