class ProcessManager::Daemon::Master

Attributes

children[RW]

Public Class Methods

abort() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 300
def self.abort
  Kernel.abort
end
child_class() click to toggle source

please override

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 327
def self.child_class
  ::ProcessManager::Daemon::Child
end
clean_stale_pid() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 304
def self.clean_stale_pid
  puts "Pidfile #{pid_file} present but no matching process running - cleaning up"
  ::FileUtils.rm(pid_file)
end
description(pid = $$) click to toggle source

please override

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 322
def self.description(pid = $$)
  "master #{pid}"
end
find_pid() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 309
def self.find_pid
  File.read(pid_file).chomp.to_i rescue nil
end
log_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 90
def self.log_file
  File.join(ProcessManager::Config.config[:log_dir], "#{ProcessManager::Config.config[:program_name]}.#{pid_description}.log")
end
new() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 12
def initialize
  @children = {}
  ProcessManager.set_program_name(description)
  ensure_validate_configuration
  dropped_privileges = drop_privileges
  ProcessManager::Log.init(log_file)
  if dropped_privileges
    ProcessManager::Log.info("Dropped privileges to group: #{Etc.getgrgid(Process.gid).name} (gid = #{Process.gid})")
    ProcessManager::Log.info("Dropped privileges to user: #{Etc.getpwuid(Process.uid).name} (uid = #{Process.uid})")
  end
  after_initialize
end
pid_description() click to toggle source

please override

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 82
def self.pid_description
  "ProcessManager"
end
pid_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 65
def self.pid_file
  File.join(ProcessManager::Config.config[:pid_dir], "#{ProcessManager::Config.config[:program_name]}.#{self.pid_description}.pid")
end
pid_lock_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 69
def self.pid_lock_file
  File.join(ProcessManager::Config.config[:pid_dir], "#{ProcessManager::Config.config[:program_name]}.pid.lock")
end
restart() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 45
def self.restart
  stop
  sleep 1
  start
end
start() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 34
def self.start
  pid = fork do
    new.start
  end
  Process.detach pid
end
status() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 51
def self.status
  if pid = find_pid
    if ProcessManager::process_running?(pid)
      pid
    else
      clean_stale_pid
      nil
    end
  else
    # does not run
    nil
  end
end
stop() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 41
def self.stop
  new.stop
end

Public Instance Methods

after_initialize() click to toggle source

please override

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 26
def after_initialize
  # hook
end
child_class() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 317
def child_class
  self.class.child_class
end
cleanup_and_exit() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 218
def cleanup_and_exit
  SimplePid.cleanup!(pid_file)
  @file_lock.close
  exit
end
cleanup_dead_child(dead_child) click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 242
def cleanup_dead_child(dead_child)
  ProcessManager::Log.info "#{description}: been told to replace child #{dead_child.inspect}"
  # delete given child
  if index = children.key(dead_child)
    children.delete(index)
  end

  # check all other children
  children.each do |child_index, child_pid|
    begin
      dead_child = Process.waitpid(child_pid, Process::WNOHANG)
      if index = children.key(dead_child)
        children.delete(index)
      end
    rescue Errno::ECHILD
    end
  end

  replace_terminated_children
end
description(pid = $$) click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 313
def description(pid = $$)
  self.class.description(pid)
end
drop_privileges() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 98
def drop_privileges

  runas_user = ProcessManager::Config.config[:user]
  return false if runas_user.blank?

  if runas_user == Etc.getpwuid(Process.uid).name
    return false
  elsif Process.uid != 0
    raise "Can't drop privileges as unprivileged user. Please run this command as a privileged user."
  end

  if runas_user.present?
    uid = Etc.getpwnam(runas_user).uid
    if (group = ProcessManager::Config.config[:group]) && group.present?
      gid = Etc.getgrnam(group).gid
    else
      gid = Etc.getpwuid(uid).gid
    end
    Process.initgroups(runas_user, gid)
    Process::GID.change_privilege(gid)
    Process::UID.change_privilege(uid)
    true
  end
  false
rescue Exception => e
  $stderr.puts "Failed to drop privileges: #{e.class} - #{e.message} - #{e.backtrace.join("\n")}"
  exit 1
end
ensure_validate_configuration() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 293
def ensure_validate_configuration
  if (errors = ProcessManager::Config.validate_config)
    errors.each{|error| puts error}
  end
  cleanup_and_exit unless errors.empty?
end
handle_chld() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 224
def handle_chld
  if child = reap_child
    ProcessManager::Log.info "#{description}: Received CHLD - cleaning dead child process"
    cleanup_dead_child(child)
  else
    ProcessManager::Log.debug "#{description}: Received CHLD - ignoring as it looks like a child of a child"
  end
end
handle_pid_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 164
def handle_pid_file       
  @file_lock ||= File.open(pid_lock_file, File::RDWR|File::CREAT, 0644)
  lock_acquired = @file_lock.flock(File::LOCK_EX | File::LOCK_NB)
  
  if lock_acquired == false
    ProcessManager::Log.info("Could not acquire lock on #{pid_lock_file} - aborting start!")
    self.class.abort
  
  elsif File.exists?(pid_file)
    pid = self.class.find_pid
    if ProcessManager.process_running?(pid)
      puts "Pidfile #{pid_file} exists and process #{pid} is running - aborting start!"
      ProcessManager::Log.info("Pidfile #{pid_file} exists and process #{pid} is running - aborting start!")
      @file_lock.close
      self.class.abort
    else
      self.class.clean_stale_pid
    end
  end
  ::SimplePid.drop(pid_file)
end
kill_children(sig) click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 284
def kill_children(sig)
  children.each do |index, child_pid|
    begin
      Process.kill(sig, child_pid)
    rescue Errno::ESRCH
    end
  end
end
log_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 94
def log_file
  self.class.log_file
end
pid_description() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 86
def pid_description
  self.class.pid_description
end
pid_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 77
def pid_file
  self.class.pid_file
end
pid_lock_file() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 73
def pid_lock_file
  self.class.pid_lock_file
end
reap_child() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 233
def reap_child
  dead_child = nil
  begin
    dead_child = Process.wait
  rescue Errno::ECHILD
  end
  dead_child
end
replace_terminated_children() click to toggle source

make sure we have again as many child we need

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 264
def replace_terminated_children
  missing_children = ProcessManager::Config.config[:children] - children.values.size
  if missing_children > 0
    ProcessManager::Log.info "#{description}: not enough child processes running - missing at least #{missing_children} - respawning"
    0.upto(ProcessManager::Config.config[:children] - 1).each do |i|
      if children.has_key?(i)
        ProcessManager::Log.debug "#{description}: child #{i+1}/#{ProcessManager::Config.config[:children]} is still there"
      else
        Thread.new(i) do
          # Sleep for 5 seconds before spawning a new child. That way we can avoid fork storm due to child process bootstrap issues.
          sleep(5)
          spawn_child(i)
        end
      end
    end
  else
    ProcessManager::Log.debug "#{description}: no need to replace child processes"
  end
end
spawn_child(index) click to toggle source

spawn a new child and pass down out PID so that it can check if we are alive

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 194
def spawn_child(index)
  master_pid = $$ # need to store in order to pass down to child
  child_pid = fork do
    @file_lock.close
    child_class.new(index, master_pid).start
  end
  children[index] = child_pid
  ProcessManager::Log.info "#{description}: Spawned child #{index + 1}/#{ProcessManager::Config.config[:children]}"
end
spawn_children() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 186
def spawn_children
  ProcessManager::Config.config[:children].times do |i|
    spawn_child(i)
    sleep ProcessManager::Config.config[:wait_between_spawning_children].to_i
  end
end
start() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 127
def start
  @kill_signal_received=0;
  handle_pid_file
  validate_ssl_config
  trap_signals

  spawn_children
  puts "Started #{description} with #{ProcessManager::Config.config[:children]} children"
  ProcessManager::Log.info("Started #{description} with #{ProcessManager::Config.config[:children]} children")

  loop do
    # master does nothing apart from replacing dead children
    # and forwarding signals
    # To do that, it observes a kill_sig_received flag in every loop iteration
    if @kill_signal_received != 0
      ProcessManager::Log.info "#{description}: Received #{@kill_signal_received} - stopping children and shutting down"
      kill_children(@kill_signal_received)
      @kill_signal_received=0
      cleanup_and_exit
    end
    sleep 1
  end
end
stop() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 151
def stop
  if (pid = self.class.find_pid)
    puts "Stopping #{description(pid)}"
    ProcessManager::Log.info("Stopping #{description(pid)}")
    begin
      Process.kill('TERM', pid)
    rescue Errno::ESRCH
    end
  else
    puts "Nothing running that could be stopped"
  end
end
trap_signals() click to toggle source
# File vendor/gems/process_manager/lib/process_manager/master.rb, line 204
def trap_signals
  # The QUIT & INT signals triggers a graceful shutdown.
  # The master shuts down immediately and forwards the signal to each child
  [:INT, :QUIT, :TERM].each do |sig|
    trap(sig) do
      @kill_signal_received=sig
    end
  end

  trap(:CHLD) do
    handle_chld
  end
end
validate_ssl_config() click to toggle source

please override

# File vendor/gems/process_manager/lib/process_manager/master.rb, line 31
def validate_ssl_config
end