Class: Arachni::Processes::Manager

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/arachni/processes/manager.rb

Overview

Helper for managing processes.

Author:

Constant Summary collapse

RUNNER =
"#{File.dirname( __FILE__ )}/executables/base.rb"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeManager

Returns a new instance of Manager.



26
27
28
29
# File 'lib/arachni/processes/manager.rb', line 26

def initialize
    @pids           = []
    @discard_output = true
end

Instance Attribute Details

#pidsArray<Integer> (readonly)

Returns PIDs of all running processes.

Returns:

  • (Array<Integer>)

    PIDs of all running processes.



24
25
26
# File 'lib/arachni/processes/manager.rb', line 24

def pids
  @pids
end

Class Method Details

.method_missing(sym, *args, &block) ⇒ Object



156
157
158
159
160
161
162
# File 'lib/arachni/processes/manager.rb', line 156

def self.method_missing( sym, *args, &block )
    if instance.respond_to?( sym )
        instance.send( sym, *args, &block )
    else
        super( sym, *args, &block )
    end
end

.respond_to?(m) ⇒ Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/arachni/processes/manager.rb', line 164

def self.respond_to?( m )
    super( m ) || instance.respond_to?( m )
end

Instance Method Details

#<<(pid) ⇒ Integer

Returns ‘pid`.

Parameters:

  • pid (Integer)

    Adds a PID to the #pids and detaches the process.

Returns:

  • (Integer)

    ‘pid`



35
36
37
38
39
# File 'lib/arachni/processes/manager.rb', line 35

def <<( pid )
    @pids << pid
    Process.detach pid
    pid
end

#discard_outputObject



87
88
89
# File 'lib/arachni/processes/manager.rb', line 87

def discard_output
    @discard_output = true
end

#discard_output?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/arachni/processes/manager.rb', line 91

def discard_output?
    @discard_output
end

#kill(pid) ⇒ Object

Parameters:

  • pid (Integer)

    PID of the process to kill.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/arachni/processes/manager.rb', line 42

def kill( pid )
    Timeout.timeout 10 do
        while sleep 0.1 do
            begin
                # I'd rather this be an INT but WEBrick's INT traps write to
                # the Logger and multiple INT signals force it to write to a
                # closed logger and crash.
                Process.kill( Gem.win_platform? ? 'QUIT' : 'KILL', pid )
            rescue Errno::ESRCH
                @pids.delete pid
                return
            end
        end
    end
rescue Timeout::Error
end

#kill_many(pids) ⇒ Object

Parameters:

  • pids (Array<Integer>)

    PIDs of the process to #kill.



61
62
63
# File 'lib/arachni/processes/manager.rb', line 61

def kill_many( pids )
    pids.each { |pid| kill pid }
end

#kill_reactorObject

Stops the Reactor.



72
73
74
75
76
# File 'lib/arachni/processes/manager.rb', line 72

def kill_reactor
    Reactor.stop
rescue
    nil
end

#killallObject

Kills all processes.



66
67
68
69
# File 'lib/arachni/processes/manager.rb', line 66

def killall
    kill_many @pids.dup
    @pids.clear
end

#preserve_outputObject

Overrides the default setting of discarding process outputs.



79
80
81
# File 'lib/arachni/processes/manager.rb', line 79

def preserve_output
    @discard_output = false
end

#preserve_output?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/arachni/processes/manager.rb', line 83

def preserve_output?
    !discard_output?
end

#spawn(executable, options = {}) ⇒ Integer

Returns PID of the process.

Parameters:

  • executable (String)

    Name of the executable Ruby script found in OptionGroups::Paths#executables without the ‘.rb’ extension.

  • options (Hash) (defaults to: {})

    Options to pass to the script – can be retrieved from ‘$options`.

Returns:

  • (Integer)

    PID of the process.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/arachni/processes/manager.rb', line 103

def spawn( executable, options = {} )
    fork = options.delete(:fork)
    fork = true if fork.nil?

    options[:options] ||= {}
    options[:options] = Options.to_h.merge( options[: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.
    options[:options][:paths] = Options.paths.to_h

    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
            if discard_output?
                $stdout.reopen( Arachni.null_device, 'w' )
                $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

            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( RbConfig.ruby, RUNNER, *argv )
    end

    self << pid
    pid
end