module Rund

Public Instance Methods

daemonize!() click to toggle source
# File lib/rund.rb, line 7
def daemonize!
  # Keeep $stderr IO to display error and debug messages until
  # daemonizing is finished
  keep_local_stderr

  File.umask(0000)
  Process.daemon(true, true)
  change_group if @group
  change_user  if @user
  write_pid    if @pid_file
  change_dir
  redirect_input
  redirect_output

  close_local_stderr
end
debug=(flag) click to toggle source
# File lib/rund.rb, line 48
def debug=(flag)
  @debug = !!flag
end
group=(group) click to toggle source
# File lib/rund.rb, line 44
def group=(group)
  @group = group
end
input_file=(path) click to toggle source
# File lib/rund.rb, line 36
def input_file=(path)
  @input_file = clean_path(path)
end
log_file=(path) click to toggle source
# File lib/rund.rb, line 24
def log_file=(path)
  @log_file = clean_path(path)
end
pid_file=(path) click to toggle source
# File lib/rund.rb, line 28
def pid_file=(path)
  @pid_file = clean_path(path)
end
run_dir=(path) click to toggle source
# File lib/rund.rb, line 32
def run_dir=(path)
  @run_dir = clean_path(path)
end
user=(user) click to toggle source
# File lib/rund.rb, line 40
def user=(user)
  @user = user
end

Private Instance Methods

change_dir() click to toggle source
# File lib/rund.rb, line 98
def change_dir
  @run_dir ||= "/"
  debug { "Changing to #{@run_dir} as run dir" }
  Dir.chdir(@run_dir)
end
change_group() click to toggle source
# File lib/rund.rb, line 67
def change_group
  error("Not running as root. Cannot change group of process.") if Process.uid != 0
  begin
    group = (@group.is_a? Integer) ? Etc.getgrgid(@group) : Etc.getgrnam(@group)
  rescue ArgumentError
    error("Group #{@group} does not exist")
  end

  debug { "Changing group to #{group.name} GID=#{group.gid}" }

  begin
    Process::GID.change_privilege(group.gid)
  rescue Errno::EPERM
    error("Cannot change to group #{group.name}. Permission denied.")
  end
end
change_user() click to toggle source
# File lib/rund.rb, line 54
def change_user
  error("Not running as root. Cannot change user of process.") if Process.uid != 0
  begin
    user = (@user.is_a? Integer) ? Etc.getpwuid(@user) : Etc.getpwnam(@user)
  rescue ArgumentError
    error("User #{@user} does not exist")
  end

  debug { "Changing user to #{user.name} UID=#{user.uid}" }

  Process::UID.change_privilege(user.uid)
end
check_pid() click to toggle source
# File lib/rund.rb, line 163
def check_pid
  case pid_status
  when :running, :not_owned
    error("A process is already running with pid #{pid}")
  when :dead
    debug { "Deleting stale pid file" }
    File.delete(@pid_file)
  end
end
clean_path(path) click to toggle source
# File lib/rund.rb, line 93
def clean_path(path)
  path = path.to_s.strip
  path.empty? ? nil : File.absolute_path(path)
end
close_local_stderr() click to toggle source
# File lib/rund.rb, line 89
def close_local_stderr
  @stderr.close
end
debug(msg = nil) { || ... } click to toggle source
# File lib/rund.rb, line 182
def debug(msg = nil)
  return unless @debug
  msg = yield if block_given?
  @stderr << "DEBUG: #{msg}\n"
end
error(msg) click to toggle source
# File lib/rund.rb, line 177
def error(msg)
  @stderr << "ERROR: #{msg}\n"
  exit 1
end
keep_local_stderr() click to toggle source
# File lib/rund.rb, line 84
def keep_local_stderr
  @stderr = $stderr.dup
  @stderr.sync
end
pid() click to toggle source
# File lib/rund.rb, line 173
def pid
  @pid ||= File.read(@pid_file).strip.to_i
end
pid_status() click to toggle source
# File lib/rund.rb, line 151
def pid_status
  return :exited unless File.exists?(@pid_file)
  return :dead if pid == 0

  Process.kill(0, pid) # check process status
  :running
rescue Errno::ESRCH
  :dead
rescue Errno::EPERM
  :not_owned
end
redirect_input() click to toggle source
# File lib/rund.rb, line 125
def redirect_input
  @input_file ||= "/dev/null"
  $stdin.reopen(@input_file, "r")
end
redirect_output() click to toggle source
# File lib/rund.rb, line 104
def redirect_output
  @log_file ||= "/dev/null"
  debug { "Logging to #{@log_file}" }

  log_dir = File.dirname(@log_file)
  error("Log file dir does not exist #{log_dir}") unless File.exists?(log_dir)

  begin
    unless File.exists?(@log_file)
      FileUtils.touch(@log_file)
      File.chmod(0644, @log_file)
    end
  rescue Errno::EACCES
    error("Cannot write log file #{@log_file}. Permission denied.")
  end

  $stderr.reopen(@log_file, "a")
  $stdout.reopen($stderr)
  $stdout.sync = $stderr.sync = true
end
write_pid() click to toggle source
# File lib/rund.rb, line 130
def write_pid
  debug { "Writing pid to #{@pid_file}" }

  begin
    File.open(@pid_file, File::CREAT | File::EXCL | File::WRONLY) {|f| f.write("#{Process.pid}") }
  rescue Errno::EACCES
    error("Cannot write pid file #{@pid_file}. Permission denied.")
  rescue Errno::EEXIST
    check_pid
    retry
  end

  at_exit do
    begin
      File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
    rescue Errno::EACCES
      debug { "Cannot delete pid file #{@pid_file}. Permission denied." }
    end
  end
end