class Main::Daemon

Public Class Methods

commands() click to toggle source
# File lib/main/daemon.rb, line 113
def Daemon.commands
  instance_methods.map{|method| method.to_s =~ /\Acmd_(.*)/ && $1}.compact
end
new(main) click to toggle source
# File lib/main/daemon.rb, line 26
def initialize(main)
  @main = main
  @script = @main.script
end

Public Instance Methods

alive?() click to toggle source
# File lib/main/daemon.rb, line 363
def alive?
  pid = Integer(IO.read(@pid_file)) rescue nil
  alive = !!pid

  if pid
    alive =
      begin
        Process.kill(0, pid)
        true
      rescue Errno::ESRCH
        false 
      end
  end

  alive
end
cmd(cmd, &block) click to toggle source
# File lib/main/daemon.rb, line 64
def cmd(cmd, &block)
  setup!

  process_cmd!(cmd)
end
cmd_dir() click to toggle source
# File lib/main/daemon.rb, line 308
def cmd_dir
  puts(@daemon_dir)
  exit(42)
end
cmd_info() click to toggle source
# File lib/main/daemon.rb, line 130
def cmd_info
  info =
    {
      'main'      => @main.program,
      'script'    => @script,
      'dotdir'    => @dotdir
    }

  %w[
    daemon_dir

    lock_file
    log_file
    pid_file
    stdin_file
    stdout_file
    stderr_file
  ].each do |key|
    value = instance_variable_get("@#{ key }")
    info[key] = value
  end

  STDERR.puts(info.to_yaml)

  exit(42)
end
cmd_log() click to toggle source
# File lib/main/daemon.rb, line 303
def cmd_log
  puts(@log_file)
  exit(42)
end
cmd_pid() click to toggle source
# File lib/main/daemon.rb, line 244
def cmd_pid
  pid = Integer(IO.read(@pid_file)) rescue nil

  if pid
    begin
      Process.kill(0, pid)
      puts(pid)
      exit(0)
    rescue Errno::ESRCH
      exit(1)
    end
  else
    exit(1)
  end

  exit(1)
end
cmd_ping() click to toggle source
# File lib/main/daemon.rb, line 262
def cmd_ping
  pid = Integer(IO.read(@pid_file)) rescue nil

  if pid
    signaled = false

    begin
      Process.kill('SIGALRM', pid)
      signaled = true
    rescue Object
      nil
    end

    if signaled
      STDOUT.puts(pid)
      exit
    end
  end
end
cmd_restart() click to toggle source
# File lib/main/daemon.rb, line 223
def cmd_restart
  42.times do
    begin
      cmd_stop
      break
    rescue Object => e
      if alive?
        sleep(rand)
      else
        break
      end
    end
  end

  abort("could not stop #{ @script }!") if alive?

  sleep(rand)

  cmd_start
end
cmd_run() click to toggle source
# File lib/main/daemon.rb, line 282
def cmd_run
  lock!(:complain => true)

  pid!

  log!

  exec!
end
cmd_signal() click to toggle source
# File lib/main/daemon.rb, line 292
def cmd_signal
  pid = Integer(IO.read(@pid_file)) rescue nil
  if pid
    signal = ARGV.shift || 'SIGALRM'
    Process.kill(signal, pid)
    puts(pid)
    exit(0)
  end
  exit(42)
end
cmd_start() click to toggle source
# File lib/main/daemon.rb, line 157
def cmd_start
  lock!(:complain => true)

  daemonize!{|pid| puts(pid)}

  redirect_io!

  pid!

  log!

  exec!
end
cmd_stop() click to toggle source
# File lib/main/daemon.rb, line 171
def cmd_stop
  pid = Integer(IO.read(@pid_file)) rescue nil

  if pid
    alive = true

    %w( QUIT TERM ).each do |signal|
      begin
        Process.kill(signal, pid)
      rescue Errno::ESRCH
        nil
      end

      42.times do
        begin
          Process.kill(0, pid)
          sleep(rand)
        rescue Errno::ESRCH
          alive = false
          puts(pid)
          exit(0)
        end
      end
    end

    if alive
      begin
        Process.kill(-9, pid)
        sleep(rand)
      rescue Errno::ESRCH
        nil
      end

      begin
        Process.kill(0, pid)
      rescue Errno::ESRCH
        puts(pid)
        exit(0)
      end
    end
  end
  
  exit(1)
ensure
  unless alive?
    begin
      FileUtils.rm_f(@pid_file) rescue nil
    rescue Object
    end
  end
end
cmd_tail() click to toggle source
# File lib/main/daemon.rb, line 313
def cmd_tail
  system("tail -F #{ @stdout_file.inspect } #{ @stderr_file.inspect } #{ @log_file.inspect }")
  exit(42)
end
cmd_usage() click to toggle source
# File lib/main/daemon.rb, line 125
def cmd_usage
  STDERR.puts usage
  exit(42)
end
commands() click to toggle source
# File lib/main/daemon.rb, line 117
def commands
  Daemon.commands
end
daemonize!(options = {}, &block) click to toggle source

daemonize{|pid| puts “the pid of the daemon is #{ pid }”}

# File lib/main/daemon.rb, line 410
def daemonize!(options = {}, &block)
# optional directory and umask
#
  chdir = options[:chdir] || options['chdir'] || @daemon_dir || '.'
  umask = options[:umask] || options['umask'] || 0

# drop to the background avoiding the possibility of zombies..
#
  detach!(&block)

# close all open io handles *except* these ones
#
  keep_ios(STDIN, STDOUT, STDERR, @lock)

# sane directory and umask
#
  Dir::chdir(chdir)
  File::umask(umask)

# global daemon flag
#
  $DAEMON = true
end
detach!(&block) click to toggle source
# File lib/main/daemon.rb, line 434
def detach!(&block)
# setup a pipe to relay the grandchild pid through
#
  a, b = IO.pipe

# in the parent we wait for the pid, wait on our child to avoid zombies, and
# then exit
#
  if fork
    b.close
    pid = Integer(a.read.strip)
    a.close
    block.call(pid) if block
    Process.waitall
    exit!
  end

# the child simply exits so it can be reaped - avoiding zombies.  the pipes
# are inherited in the grandchild
#
  if fork
    exit!
  end

# finally, the grandchild sends it's pid back up the pipe to the parent is
# aware of the pid
#
  a.close
  b.puts(Process.pid)
  b.close

# might as well nohup too...
#
  Process::setsid rescue nil
end
exec!() click to toggle source
# File lib/main/daemon.rb, line 345
def exec!
  ::Kernel.exec(script_start_command)
end
keep_ios(*ios) click to toggle source
# File lib/main/daemon.rb, line 502
def keep_ios(*ios)
  filenos = []

  ios.flatten.compact.each do |io|
    begin
      fileno = io.respond_to?(:fileno) ? io.fileno : Integer(io)
      filenos.push(fileno)
    rescue Object
      next
    end
  end

  ObjectSpace.each_object(IO) do |io|
    begin
      fileno = io.fileno
      next if filenos.include?(fileno)
      io.close unless io.closed?
    rescue Object
      next
    end
  end
end
lock!(options = {}) click to toggle source
# File lib/main/daemon.rb, line 318
def lock!(options = {})
  complain = options['complain'] || options[:complain]
  fd = open(@lock_file, 'r+')
  status = fd.flock(File::LOCK_EX|File::LOCK_NB)

  unless status == 0
    if complain
      pid = Integer(IO.read(@pid_file)) rescue '?'
      warn("instance(#{ pid }) is already running!")
    end
    exit(42)
  end
  @lock = fd # prevent garbage collection from closing the file!
  at_exit{ unlock! }
end
log!() click to toggle source
# File lib/main/daemon.rb, line 355
def log!
  logger.info("DAEMON START - #{ Process.pid }")

  at_exit do
    logger.info("DAEMON STOP - #{ Process.pid }") rescue nil
  end
end
logger() click to toggle source
# File lib/main/daemon.rb, line 387
def logger
  @logger ||= (
    require 'logger' unless defined?(Logger)

    if @log_file
      number_rolled = 7
      megabytes     = 2 ** 20
      max_size      = 42 * megabytes

      ::Logger.new(@log_file, number_rolled, max_size)
    else
      ::Logger.new(STDERR)
    end
  )
end
logger=(logger) click to toggle source
# File lib/main/daemon.rb, line 403
def logger=(logger)
  @logger = logger
end
pid!() click to toggle source
# File lib/main/daemon.rb, line 338
def pid!
  open(@pid_file, 'w+') do |fd|
    fd.puts(Process.pid)
  end
  at_exit{ FileUtils.rm_f(@pid_file) }
end
process_cmd!(cmd) click to toggle source
# File lib/main/daemon.rb, line 70
def process_cmd!(cmd)
  case cmd.to_s
    when /USAGE/i
      cmd_usage

    when /INFO/i
      cmd_info

    when /RESTART/i
      cmd_restart

    when /START/i
      cmd_start

    when /STOP/i
      cmd_stop

    when /PING/i
      cmd_ping

    when /RUN/i
      cmd_run

    when /PID/i
      cmd_pid

    when /SIGNAL/i
      cmd_signal

    when /LOG/i
      cmd_log

    when /DIR/i
      cmd_dir

    when /TAIL/i
      cmd_tail

    else
      cmd_usage
  end
end
redirect_io!(options = {}) click to toggle source
# File lib/main/daemon.rb, line 470
def redirect_io!(options = {})
  stdin = options[:stdin] || @stdin_file
  stdout = options[:stdout] || @stdout_file
  stderr = options[:stderr] || @stderr_file

  {
    STDIN => stdin, STDOUT => stdout, STDERR => stderr
  }.each do |io, file|
    opened = false

    fd =
      case
        when file.is_a?(IO)
          file
        when file.to_s == 'null'
          opened = true
          open('/dev/null', 'ab+')
        else
          opened = true
          open(file, 'ab+')
      end

    begin
      fd.sync = true rescue nil
      fd.truncate(0) rescue nil
      io.reopen(fd)
    ensure
      fd.close rescue nil if opened
    end
  end
end
script_start_command() click to toggle source
# File lib/main/daemon.rb, line 349
def script_start_command
  argv = @main.argv.dup
  argv.shift if argv.first == "--"
  "#{ which_ruby } #{ @script.inspect } #{ argv.map{|arg| arg.inspect}.join(' ') }"
end
setup!() click to toggle source
# File lib/main/daemon.rb, line 31
def setup!
  @dotdir = @main.dotdir

  @dirname = File.expand_path(File.dirname(@script))
  @basename = File.basename(@script)
  @script_dir = File.expand_path(File.dirname(@script))

  @daemon_dir = File.join(@dotdir, 'daemon')

  @lock_file = File.join(@daemon_dir, 'lock')
  @log_file = File.join(@daemon_dir, 'log')
  @pid_file = File.join(@daemon_dir, 'pid')
  @stdin_file = File.join(@daemon_dir, 'stdin')
  @stdout_file = File.join(@daemon_dir, 'stdout')
  @stderr_file = File.join(@daemon_dir, 'stderr')

  FileUtils.mkdir_p(@daemon_dir) rescue nil

  %w( lock log pid stdin stdout stderr ).each do |which|
    file = instance_variable_get("@#{ which }_file")
    FileUtils.touch(file)
  end

  @started_at = Time.now

  @ppid = Process.pid

  STDOUT.sync = true
  STDERR.sync = true

  self
end
unlock!() click to toggle source
# File lib/main/daemon.rb, line 334
def unlock!
  @lock.flock(File::LOCK_UN|File::LOCK_NB) if @lock
end
usage() click to toggle source
# File lib/main/daemon.rb, line 121
def usage
  "#{ main.program } daemon #{ commands.join('|') }"
end
which_ruby() click to toggle source
# File lib/main/daemon.rb, line 380
def which_ruby
  c = ::RbConfig::CONFIG
  ruby = File::join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
  raise "ruby @ #{ ruby } not executable!?" unless test(?e, ruby)
  ruby
end