class Pidly::Control
Pidly
daemon control
Attributes
Public Class Methods
Initialize control object
@param [Hash] options The options to create a controller with.
@option options [String] :name Daemon name
@option options [String] :path Path to create the log/pids directory
@option options [String] :pid_file Pid file path
@option options [String] :log_file Log file path
@option options [true, false] :sync_log Synchronize log files
@option options [true, false] :allow_multiple
Allow multiple daemons of the same type
@option options [true, false] :sync_log Synchronize log files
@option options [String] :signal Trap signal
@option options [Integer] :timeout Timeout for Process#wait
@option options [true, false] :verbose Display daemon messages
@option options [true, false] :logger Enable daemon logging
@return [Control] Control
object
@raise [RuntimeError]
Raise exception if path does not exist
@raise [RuntimeError]
Raise exception if path is not readable or writable.
# File lib/pidly/control.rb, line 63 def initialize(options={}) @messages = [] @error_count = 0 @name = options.fetch(:name) @path = if options.has_key?(:path) Pathname.new(options.fetch(:path)) else Pathname.new('/tmp') end unless @path.directory? raise('Path does not exist or is not a directory.') end unless @path.readable? && @path.writable? raise('Path must be readable and writable.') end @pid_file = if options.has_key?(:pid_file) Pathname.new(options.fetch(:pid_file)) else Pathname.new(File.join(@path.to_s, 'pids', @name + '.pid')) end @log_file = if options.has_key?(:log_file) Pathname.new(options.fetch(:log_file)) else Pathname.new(File.join(@path.to_s, 'logs', @name + '.log')) end @pid = fetch_pid @sync_log = options.fetch(:sync_log, true) @allow_multiple = options.fetch(:allow_multiple, false) @signal = options.fetch(:signal, "TERM") @timeout = options.fetch(:timeout, 10) @verbosity = options.fetch(:verbose, true) @logger = options.fetch(:logger, true) validate_callbacks! validate_files_and_paths! end
Public Instance Methods
Clean
Remove all files created by the daemon.
# File lib/pidly/control.rb, line 293 def clean! FileUtils.rm(@log_file) FileUtils.rm(@pid_file) rescue Errno::ENOENT end
Fetch Class Variable
@param [Symbol] name Callback name
@return [Symbol, Proc, nil]
Returns either a method or block of code to be executed when the callback is called. If no value is accossiated with the call variable `nil` will be returned to the caller.
# File lib/pidly/control.rb, line 309 def fetch_class_var(name) Control.instance_eval do return nil unless class_variable_defined?(:"@@#{name}") class_variable_get(:"@@#{name}") end end
Kill
@param [String] remove_pid_file Remove the daemon pid file
# File lib/pidly/control.rb, line 261 def kill(remove_pid_file=true) if running? log(:info, "Killing \"#{@name}\" (PID: #{@pid})") execute_callback(:kill) Process.kill 9, @pid end FileUtils.rm(@pid_file) if remove_pid_file rescue Errno::ENOENT end
Restart
Restart the daemon
# File lib/pidly/control.rb, line 252 def restart stop; sleep 1 while running?; start end
Running?
@return [true, false] Return the running status of the daemon.
# File lib/pidly/control.rb, line 277 def running? Process.kill 0, @pid true rescue Errno::ESRCH false rescue Errno::EPERM true rescue false end
Start
Validate callbacks and start daemon
# File lib/pidly/control.rb, line 140 def start unless @allow_multiple if running? log(:error, "\"#{@name}\" is already running (PID: #{@pid})") return end end @pid = fork do begin Process.setsid open(@pid_file, 'w') do |f| f << Process.pid @pid = Process.pid end execute_callback(:before_start) Dir.chdir @path.to_s File.umask 0000 if @logger log = File.new(@log_file, "a") log.sync = @sync_log STDIN.reopen "/dev/null" STDOUT.reopen log STDERR.reopen STDOUT end trap("TERM") do stop end execute_callback(:start) rescue RuntimeError => message STDERR.puts message STDERR.puts message.backtrace execute_callback(:error) rescue => message STDERR.puts message STDERR.puts message.backtrace execute_callback(:error) end end rescue => message STDERR.puts message STDERR.puts message.backtrace execute_callback(:error) end
Status
Return current daemon status and pid
@return [String] Status
# File lib/pidly/control.rb, line 239 def status if running? log(:info, "\"#{@name}\" is running (PID: #{@pid})") else log(:info, "\"#{@name}\" is NOT running") end end
Stop
Stop daemon and remove pid file
# File lib/pidly/control.rb, line 202 def stop if running? Process.kill(@signal, @pid) FileUtils.rm(@pid_file) execute_callback(:stop) begin Process.wait(@pid) rescue Errno::ECHILD end @timeout.downto(0) do sleep 1 exit unless running? end Process.kill 9, @pid if running? execute_callback(:after_stop) else FileUtils.rm(@pid_file) if File.exists?(@pid_file) log(:info, "\"#{@name}\" PID file not found.") end rescue Errno::ENOENT end
Private Instance Methods
# File lib/pidly/control.rb, line 346 def execute_callback(callback_name) @error_count += 1 if callback_name == :error if (callback = fetch_class_var(callback_name)) if callback.kind_of?(Symbol) self.send(callback.to_sym) elsif callback.respond_to?(:call) self.instance_eval(&callback) else nil end end end
# File lib/pidly/control.rb, line 362 def fetch_pid IO.read(@pid_file).to_i rescue nil end
# File lib/pidly/control.rb, line 327 def validate_callbacks! parent = self Control.instance_eval do unless class_variable_defined?(:"@@start") raise('You must define a "start" callback.') end class_variables.each do |cvar| value = class_variable_get(:"#{cvar}") next unless value.is_a?(Symbol) next if parent.respond_to?(value) raise("Undefined callback method: #{value}") end end end
# File lib/pidly/control.rb, line 317 def validate_files_and_paths! unless @log_file.directory? FileUtils.mkdir_p(@log_file.dirname) end unless @pid_file.directory? FileUtils.mkdir_p(@pid_file.dirname) end end