class Clacks::Command

Constants

PROC_ARGV
PROC_NAME

Public Class Methods

new(args) click to toggle source
# File lib/clacks/command.rb, line 8
def initialize(args)
  @options = { config_file: 'config/clacks.rb' }

  opts = OptionParser.new do |opts|
    opts.banner = "Usage: #{PROC_NAME} [options]"

    if Clacks.rails_env?
      opts.separator "Rails options:"
      opts.on("-E", "--env RAILS_ENV", "use RAILS_ENV for defaults (default: development)") do |e|
        ENV['RAILS_ENV'] = e
      end
    end

    opts.separator "Ruby options:"

    opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do
      $DEBUG = true
    end

    opts.on("-w", "--warn", "turn warnings on for your script") do
      $-w = true
    end

    opts.separator "Clacks options:"

    opts.on("-c", "--config-file FILE", "Clacks-specific config file (default: #{@options[:config_file]})") do |f|
      @options[:config_file] = f
    end

    opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
      @options[:daemonize] = !!d
    end

    opts.on("-P", "--pid FILE", "file to store PID (default: clacks.pid)") { |f|
      @options[:pid] = f
    }

    opts.separator "Common options:"

    opts.on_tail("-h", "--help", "Show this message") do
      puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
      exit
    end

    opts.on_tail("-v", "--version", "Show version") do
      puts "#{PROC_NAME} v#{Clacks::VERSION}"
      exit
    end
  end
  @args = opts.parse!(args)
end

Public Instance Methods

exec() click to toggle source
# File lib/clacks/command.rb, line 60
def exec
  daemonize if @options[:daemonize]

  Clacks.require_rails if Clacks.rails_env?

  Clacks.config = config = Clacks::Configurator.new(@options[:config_file])
  unless config[:pop3] || config[:imap]
    $stderr.puts "Either a POP3 or an IMAP server must be configured"
    exit!(1)
  end

  reopen_io($stdout, config[:stdout_path])
  reopen_io($stderr, config[:stderr_path])

  unless config[:pid]
    config.pid(@options[:pid] || 'clacks.pid')
  end
  pid = config[:pid]
  if wpid = running?(pid)
    $stderr.puts "#{Clacks::Command::PROC_NAME} already running with pid: #{wpid} (or stale #{pid})"
    exit!(1)
  end
  write_pid(pid)

  config[:after_initialize].call if config[:after_initialize]

  proc_name('master')

  setup_signal_handling

  @service = Clacks::Service.new
  @service.run
end

Private Instance Methods

daemonize(safe = true) click to toggle source

See Stevens's “Advanced Programming in the UNIX Environment” chapter 13

# File lib/clacks/command.rb, line 97
def daemonize(safe = true)
  $stdin.reopen '/dev/null'

  # Fork and have the parent exit.
  # This makes the shell or boot script think the command is done.
  # Also, the child process is guaranteed not to be a process group
  # leader (a prerequisite for setsid next)
  exit if fork

  # Call setsid to create a new session. This does three things:
  # - The process becomes a session leader of a new session
  # - The process becomes the process group leader of a new process group
  # - The process has no controlling terminal
  Process.setsid

  # Fork again and have the parent exit.
  # This guarantes that the daemon is not a session leader nor can
  # it acquire a controlling terminal (under SVR4)
  exit if fork

  unless safe
    ::Dir.chdir('/')
    ::File.umask(0000)
  end

  cfg_defaults = Clacks::Configurator::DEFAULTS
  cfg_defaults[:stdout_path] ||= "/dev/null"
  cfg_defaults[:stderr_path] ||= "/dev/null"
end
proc_name(tag) click to toggle source
# File lib/clacks/command.rb, line 149
def proc_name(tag)
  $0 = [ Clacks::Command::PROC_NAME, tag, Clacks::Command::PROC_ARGV ].join(' ')
end
reopen_io(io, path) click to toggle source

Redirect file descriptors inherited from the parent.

# File lib/clacks/command.rb, line 128
def reopen_io(io, path)
  io.reopen(::File.open(path, "ab")) if path
  io.sync = true
end
rotate_logs() click to toggle source
# File lib/clacks/command.rb, line 166
def rotate_logs
  reopen_io($stdout, Clacks.config[:stdout_path])
  reopen_io($stderr, Clacks.config[:stderr_path])
end
running?(path) click to toggle source

Read the working pid from the pid file.

# File lib/clacks/command.rb, line 134
def running?(path)
  wpid = ::File.read(path).to_i
  return if wpid <= 0
  Process.kill(0, wpid)
  wpid
rescue Errno::EPERM, Errno::ESRCH, Errno::ENOENT
  # noop
end
setup_signal_handling() click to toggle source
# File lib/clacks/command.rb, line 153
def setup_signal_handling
  graceful_exit_signal = (Signal.list.keys & ['INT', 'QUIT', 'TERM']).sort.last
  Signal.trap(graceful_exit_signal) do
    Thread.new { Clacks.logger.info 'Exiting...' }
    @service.stop if @service
  end unless graceful_exit_signal.nil?

  Signal.trap('USR1') do
    Thread.new { Clacks.logger.info 'USR1 signal received. Rotating logs.' }
    rotate_logs
  end if Signal.list['USR1']
end
write_pid(pid) click to toggle source

Write the pid.

# File lib/clacks/command.rb, line 144
def write_pid(pid)
  ::File.open(pid, 'w') { |f| f.write("#{Process.pid}") }
  at_exit { ::File.delete(pid) if ::File.exist?(pid) rescue nil }
end