class Snipr::ProcessSignaller

Class that can send signals to targetted processes or their parent processes and invoke callbacks around the actions. Delegates process location to a ProcessLocator. Is configured using a block on initialization as follows:

signaller = ProcessSignaller.new do |signaller|

signaller.include       /resque/
signaller.exclude       /scheduler/
signaller.signal        "USR1"
signaller.target_parent false
singaller.dry_run

signaller.on_no_processes do
  puts "No processes"
end

signaller.before_signal do |signal, process|
  puts "Sending #{signal} to #{process.pid}"
end

signaller.after_signal do |signal, process|
  puts "Sent #{signal} to #{process.pid}"
end

signaller.on_error do |e, signal, process|
  puts "Ooops, got #{e} sending #{signal} to #{process.pid}"
end

end

signaller.send_signals

Attributes

locator[W]
signal[R]

Public Class Methods

new(&block) click to toggle source
# File lib/snipr/process_signaller.rb, line 44
def initialize(&block)
  @locator = ProcessLocator.new
  on_no_processes {}
  before_signal {}
  after_signal {}
  on_error {|e, process, signal| raise e}
  block.call(self)
end

Public Instance Methods

after_signal(&callback) click to toggle source

Callback invoked immediately after sending a signal to a process. Will send both the signal and the KernelProcess object as returned by the locator.

# File lib/snipr/process_signaller.rb, line 108
def after_signal(&callback)
  @after_signal = callback
end
before_signal(&callback) click to toggle source

Callback invoked immediately before sending a signal to a process. Will send both the signal and the KernelProcess object as returned by the locator.

# File lib/snipr/process_signaller.rb, line 100
def before_signal(&callback)
  @before_signal = callback
end
cmd_matches?(process) click to toggle source

Ensure that the PID we are attempting to kill still matches what we expect This is not used when pkill option is set

# File lib/snipr/process_signaller.rb, line 156
def cmd_matches?(process)
   process.command == Snipr.exec_cmd("ps -p #{process.pid} -o 'command='").first.strip
end
dry_run() click to toggle source

Invoke if you want to have callbacks invoked, but not actually send signals to located processes.

# File lib/snipr/process_signaller.rb, line 131
def dry_run
  @dry_run = true
end
locator(locator=nil) click to toggle source

Specify or access the locator collaborator that is responsible for collecting the processes to operate on

# File lib/snipr/process_signaller.rb, line 86
def locator(locator=nil)
  @locator ||= (locator || ProcessLocator.new)
end
on_error(&callback) click to toggle source

Callback invoked if an error is encountered. If this is within the context of attempting to send a signal to a process, then the exception, signal and KernelProcess object are sent. Otherwise, only the exception is sent.

# File lib/snipr/process_signaller.rb, line 117
def on_error(&callback)
  @on_error = callback
end
on_no_processes(&callback) click to toggle source

Callback invoked when no processes are found

# File lib/snipr/process_signaller.rb, line 92
def on_no_processes(&callback)
  @on_no_processes = callback
end
pkill() click to toggle source

Use pkill to ensure that the process we are attempting to signal matches what we know about it. This only works for the targetted process, not the parent.

# File lib/snipr/process_signaller.rb, line 139
def pkill
  @pkill = which("pkill")
  raise "pkill not found in path or is not executable!" unless @pkill
end
ppid_matches?(process) click to toggle source
# File lib/snipr/process_signaller.rb, line 160
def ppid_matches?(process)
  Integer(process.ppid) == Integer(Snipr.exec_cmd("ps -p #{process.pid} -o 'ppid='").first.strip)
end
process_matches?(process) click to toggle source
# File lib/snipr/process_signaller.rb, line 164
def process_matches?(process)
  cmd_matches?(process) && ppid_matches?(process)
end
send_signals() click to toggle source

Send the specified signal to all located processes, invoking callbacks as appropriate.

# File lib/snipr/process_signaller.rb, line 56
def send_signals
  processes = @locator.locate

  if processes.empty?
    @on_no_processes.call
  else
    processes.each do |process|
      signal_process(process)
    end
  end
rescue StandardError => e
  @on_error.call(e)
end
target_parent(flag=false) click to toggle source

Set to true if the signal should be sent to the parent of any located processes. Defaults to false.

# File lib/snipr/process_signaller.rb, line 124
def target_parent(flag=false)
  @target_parent = flag
end
which(cmd) click to toggle source

Determine if a command exists in our PATH

# File lib/snipr/process_signaller.rb, line 145
def which(cmd)
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    location = File.join(path, cmd)
    return location if File.executable?(location)
  end
  return nil
end

Private Instance Methods

signal_process(process) click to toggle source
# File lib/snipr/process_signaller.rb, line 169
def signal_process(process)
  @before_signal.call(@signal, process)
  unless @dry_run
    if @target_parent
      Process.kill(@signal, process.ppid) if process_matches?(process)
    else
      if @pkill
        system("#{@pkill} --signal #{@signal} -P #{process.ppid} -f \"^#{Regexp.escape(process.command)}\"")
      else
        Process.kill(@signal, process.pid) if process_matches?(process)
      end
    end
  end
  @after_signal.call(@signal, process)
rescue StandardError => e
  @on_error.call(e, @signal, process)
end