class Qs::Process

Constants

HALT
RESTART
STOP
WAIT_FOR_SIGNALS_TIMEOUT

Attributes

daemon[R]
name[R]
pid_file[R]
restart_cmd[R]
signal_io[R]

Public Class Methods

new(daemon, options = nil) click to toggle source
# File lib/qs/process.rb, line 17
def initialize(daemon, options = nil)
  options ||= {}
  @daemon = daemon
  @name   = "qs: #{@daemon.process_label}"
  @logger = @daemon.logger

  @pid_file    = PIDFile.new(@daemon.pid_file)
  @signal_io   = IOPipe.new
  @restart_cmd = RestartCmd.new

  skip_daemonize = ignore_if_blank(ENV['QS_SKIP_DAEMONIZE'])
  @daemonize = !!options[:daemonize] && !skip_daemonize
end

Public Instance Methods

daemonize?() click to toggle source
# File lib/qs/process.rb, line 54
def daemonize?; @daemonize; end
run() click to toggle source
# File lib/qs/process.rb, line 31
def run
  ::Process.daemon(true) if self.daemonize?
  log "Starting Qs daemon for #{@daemon.name}"

  $0 = @name
  @pid_file.write
  log "PID: #{@pid_file.pid}"

  @signal_io.setup
  trap_signals(@signal_io)

  start_daemon(@daemon)

  signal = catch(:signal) do
    wait_for_signals(@signal_io, @daemon)
  end
  @signal_io.teardown

  run_restart_cmd(@daemon, @restart_cmd) if signal == RESTART
ensure
  @pid_file.remove
end

Private Instance Methods

handle_signal(signal, daemon) click to toggle source
# File lib/qs/process.rb, line 90
def handle_signal(signal, daemon)
  log "Got '#{signal}' signal"
  case signal
  when HALT
    daemon.halt(true)
  when STOP, RESTART
    daemon.stop(true)
  end
  throw :signal, signal
end
ignore_if_blank(value, &block) click to toggle source
# File lib/qs/process.rb, line 110
def ignore_if_blank(value, &block)
  block ||= proc{ |v| v }
  block.call(value) if value && !value.empty?
end
log(message) click to toggle source
# File lib/qs/process.rb, line 106
def log(message)
  @logger.info "[Qs] #{message}"
end
run_restart_cmd(daemon, restart_cmd) click to toggle source
# File lib/qs/process.rb, line 101
def run_restart_cmd(daemon, restart_cmd)
  log "Restarting #{daemon.name} daemon"
  restart_cmd.run
end
start_daemon(daemon) click to toggle source
# File lib/qs/process.rb, line 58
def start_daemon(daemon)
  daemon.start
  log "#{daemon.name} daemon started and ready."
rescue StandardError => exception
  log "#{daemon.name} daemon never started."
  raise exception
end
trap_signal(signal, &block) click to toggle source
# File lib/qs/process.rb, line 72
def trap_signal(signal, &block)
  ::Signal.trap(signal, &block)
rescue ArgumentError
  log "'#{signal}' signal not supported"
end
trap_signals(signal_io) click to toggle source
# File lib/qs/process.rb, line 66
def trap_signals(signal_io)
  trap_signal('INT'){  signal_io.write(HALT) }
  trap_signal('TERM'){ signal_io.write(STOP) }
  trap_signal('USR2'){ signal_io.write(RESTART) }
end
wait_for_signals(signal_io, daemon) click to toggle source
# File lib/qs/process.rb, line 78
def wait_for_signals(signal_io, daemon)
  loop do
    ready = signal_io.wait(WAIT_FOR_SIGNALS_TIMEOUT)
    handle_signal(signal_io.read, daemon) if ready

    if !daemon.running?
      log "Daemon crashed, restarting"
      start_daemon(daemon)
    end
  end
end