class Arachni::Processes::Manager
Helper for managing processes.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Constants
- RUNNER
Attributes
pids[R]
@return [Array<Integer>] PIDs of all running processes.
Public Class Methods
method_missing( sym, *args, &block )
click to toggle source
Calls superclass method
# File lib/arachni/processes/manager.rb, line 224 def self.method_missing( sym, *args, &block ) if instance.respond_to?( sym ) instance.send( sym, *args, &block ) else super( sym, *args, &block ) end end
new()
click to toggle source
# File lib/arachni/processes/manager.rb, line 26 def initialize @pids = [] @discard_output = true end
respond_to?( m )
click to toggle source
Calls superclass method
# File lib/arachni/processes/manager.rb, line 232 def self.respond_to?( m ) super( m ) || instance.respond_to?( m ) end
Public Instance Methods
<<( pid )
click to toggle source
@param [Integer] pid
Adds a PID to the {#pids} and detaches the process.
@return [Integer] ‘pid`
# File lib/arachni/processes/manager.rb, line 35 def <<( pid ) @pids << pid Process.detach pid pid end
alive?( pid )
click to toggle source
@param [Integer] pid @return [Boolean]
`true` if the process is alive, `false` otherwise.
# File lib/arachni/processes/manager.rb, line 67 def alive?( pid ) # Windows is not big on POSIX so try it its own way if possible. if Arachni.windows? begin alive = false wmi = WIN32OLE.connect( 'winmgmts://' ) processes = wmi.ExecQuery( "select ProcessId from win32_process where ProcessID='#{pid}'" ) processes.each do |proc| proc.ole_free alive = true end processes.ole_free wmi.ole_free return alive rescue WIN32OLERuntimeError end end !!(Process.kill( 0, pid ) rescue false) end
discard_output()
click to toggle source
# File lib/arachni/processes/manager.rb, line 117 def discard_output @discard_output = true end
discard_output?()
click to toggle source
# File lib/arachni/processes/manager.rb, line 121 def discard_output? @discard_output end
kill( pid )
click to toggle source
@param [Integer] pid
PID of the process to kill.
# File lib/arachni/processes/manager.rb, line 43 def kill( pid ) Timeout.timeout 10 do while sleep 0.1 do begin Process.kill( Arachni.windows? ? 'KILL' : 'TERM', pid ) # Either kill was successful or we don't have enough perms or # we hit a reused PID for someone else's process, either way, # consider the process gone. rescue Errno::ESRCH, Errno::EPERM, # Don't kill ourselves. SignalException @pids.delete pid return end end end rescue Timeout::Error end
kill_many( pids )
click to toggle source
@param [Array<Integer>] pids
PIDs of the process to {Arachni::Processes::Manager#kill}.
# File lib/arachni/processes/manager.rb, line 91 def kill_many( pids ) pids.each { |pid| kill pid } end
kill_reactor()
click to toggle source
Stops the Reactor.
# File lib/arachni/processes/manager.rb, line 102 def kill_reactor Reactor.stop rescue nil end
killall()
click to toggle source
Kills all {#pids processes}.
# File lib/arachni/processes/manager.rb, line 96 def killall kill_many @pids.dup @pids.clear end
preserve_output()
click to toggle source
Overrides the default setting of discarding process outputs.
# File lib/arachni/processes/manager.rb, line 109 def preserve_output @discard_output = false end
preserve_output?()
click to toggle source
# File lib/arachni/processes/manager.rb, line 113 def preserve_output? !discard_output? end
spawn( executable, options = {} )
click to toggle source
@param [String] executable
Name of the executable Ruby script found in {OptionGroups::Paths#executables} without the '.rb' extension.
@param [Hash] options
Options to pass to the script -- can be retrieved from `$options`.
@return [Integer]
PID of the process.
# File lib/arachni/processes/manager.rb, line 133 def spawn( executable, options = {} ) fork = options.delete(:fork) fork = false if fork.nil? stdin = options.delete(:stdin) stdout = options.delete(:stdout) stderr = options.delete(:stderr) new_pgroup = options.delete(:new_pgroup) spawn_options = {} if new_pgroup if Arachni.windows? spawn_options[:new_pgroup] = new_pgroup else spawn_options[:pgroup] = new_pgroup end end spawn_options[:in] = stdin if stdin spawn_options[:out] = stdout if stdout spawn_options[:err] = stderr if stderr options[:ppid] = Process.pid arachni_options = Options.to_h.merge( options.delete(:options) || {} ) # Paths are not included in RPC nor Hash representations as they're # considered local, in this case though they're necessary to provide # the same environment the processes. arachni_options[:paths] = Options.paths.to_h encoded_arachni_options = Base64.strict_encode64( Marshal.dump( arachni_options ) ) executable = "#{Options.paths.executables}/#{executable}.rb" encoded_options = Base64.strict_encode64( Marshal.dump( options ) ) argv = [executable, encoded_options] # Process.fork is faster, less stressful to the CPU and lets the parent # and child share the same RAM due to copy-on-write support on Ruby 2.0.0. # It is, however, not available when running on Windows nor JRuby so # have a fallback ready. if fork && Process.respond_to?( :fork ) pid = Process.fork do $stdin = spawn_options[:in] if spawn_options[:in] if spawn_options[:out] $stdout = spawn_options[:out] elsif discard_output? $stdout.reopen( Arachni.null_device, 'w' ) end if spawn_options[:err] $stderr = spawn_options[:err] elsif discard_output? $stderr.reopen( Arachni.null_device, 'w' ) end # Careful, Framework.reset will remove objects from Data # structures which off-load to disk, those files however belong # to our parent and should not be touched, thus, we remove # any references to them. Data.framework.page_queue.disk.clear Data.framework.url_queue.disk.clear Data.framework.rpc.distributed_page_queue.disk.clear # Provide a clean slate. Framework.reset Reactor.stop ENV['arachni_options'] = encoded_arachni_options ARGV.replace( argv ) load RUNNER end else # It's very, **VERY** important that we use this argument format as # it bypasses the OS shell and we can thus count on a 1-to-1 process # creation and that the PID we get will be for the actual process. pid = Process.spawn( { 'arachni_options' => encoded_arachni_options }, RbConfig.ruby, RUNNER, *(argv + [spawn_options]) ) end self << pid pid end