cuboid 0.0.0 → 0.0.1alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -0
- data/Gemfile +20 -5
- data/LICENSE.md +22 -0
- data/README.md +158 -19
- data/Rakefile +56 -3
- data/config/paths.yml +15 -0
- data/cuboid.gemspec +61 -23
- data/lib/cuboid.rb +96 -4
- data/lib/cuboid/application.rb +326 -0
- data/lib/cuboid/application/parts/data.rb +18 -0
- data/lib/cuboid/application/parts/report.rb +29 -0
- data/lib/cuboid/application/parts/state.rb +274 -0
- data/lib/cuboid/application/runtime.rb +25 -0
- data/lib/cuboid/banner.rb +13 -0
- data/lib/cuboid/data.rb +86 -0
- data/lib/cuboid/data/application.rb +52 -0
- data/lib/cuboid/error.rb +9 -0
- data/lib/cuboid/option_group.rb +129 -0
- data/lib/cuboid/option_groups.rb +8 -0
- data/lib/cuboid/option_groups/datastore.rb +23 -0
- data/lib/cuboid/option_groups/dispatcher.rb +38 -0
- data/lib/cuboid/option_groups/output.rb +14 -0
- data/lib/cuboid/option_groups/paths.rb +184 -0
- data/lib/cuboid/option_groups/report.rb +39 -0
- data/lib/cuboid/option_groups/rpc.rb +105 -0
- data/lib/cuboid/option_groups/scheduler.rb +27 -0
- data/lib/cuboid/option_groups/snapshot.rb +13 -0
- data/lib/cuboid/option_groups/system.rb +10 -0
- data/lib/cuboid/options.rb +254 -0
- data/lib/cuboid/processes.rb +13 -0
- data/lib/cuboid/processes/dispatchers.rb +140 -0
- data/lib/cuboid/processes/executables/base.rb +54 -0
- data/lib/cuboid/processes/executables/dispatcher.rb +5 -0
- data/lib/cuboid/processes/executables/instance.rb +12 -0
- data/lib/cuboid/processes/executables/rest_service.rb +13 -0
- data/lib/cuboid/processes/executables/scheduler.rb +5 -0
- data/lib/cuboid/processes/helpers.rb +4 -0
- data/lib/cuboid/processes/helpers/dispatchers.rb +23 -0
- data/lib/cuboid/processes/helpers/instances.rb +39 -0
- data/lib/cuboid/processes/helpers/processes.rb +23 -0
- data/lib/cuboid/processes/helpers/schedulers.rb +23 -0
- data/lib/cuboid/processes/instances.rb +203 -0
- data/lib/cuboid/processes/manager.rb +262 -0
- data/lib/cuboid/processes/schedulers.rb +128 -0
- data/lib/cuboid/report.rb +220 -0
- data/lib/cuboid/rest/server.rb +165 -0
- data/lib/cuboid/rest/server/instance_helpers.rb +99 -0
- data/lib/cuboid/rest/server/routes/dispatcher.rb +41 -0
- data/lib/cuboid/rest/server/routes/grid.rb +41 -0
- data/lib/cuboid/rest/server/routes/instances.rb +131 -0
- data/lib/cuboid/rest/server/routes/scheduler.rb +140 -0
- data/lib/cuboid/rpc/client.rb +3 -0
- data/lib/cuboid/rpc/client/base.rb +58 -0
- data/lib/cuboid/rpc/client/dispatcher.rb +58 -0
- data/lib/cuboid/rpc/client/instance.rb +100 -0
- data/lib/cuboid/rpc/client/instance/service.rb +37 -0
- data/lib/cuboid/rpc/client/scheduler.rb +46 -0
- data/lib/cuboid/rpc/serializer.rb +92 -0
- data/lib/cuboid/rpc/server/active_options.rb +38 -0
- data/lib/cuboid/rpc/server/application_wrapper.rb +138 -0
- data/lib/cuboid/rpc/server/base.rb +63 -0
- data/lib/cuboid/rpc/server/dispatcher.rb +317 -0
- data/lib/cuboid/rpc/server/dispatcher/node.rb +247 -0
- data/lib/cuboid/rpc/server/dispatcher/service.rb +145 -0
- data/lib/cuboid/rpc/server/instance.rb +338 -0
- data/lib/cuboid/rpc/server/output.rb +92 -0
- data/lib/cuboid/rpc/server/scheduler.rb +482 -0
- data/lib/cuboid/ruby.rb +4 -0
- data/lib/cuboid/ruby/array.rb +17 -0
- data/lib/cuboid/ruby/hash.rb +41 -0
- data/lib/cuboid/ruby/object.rb +32 -0
- data/lib/cuboid/snapshot.rb +186 -0
- data/lib/cuboid/state.rb +94 -0
- data/lib/cuboid/state/application.rb +309 -0
- data/lib/cuboid/state/options.rb +27 -0
- data/lib/cuboid/support.rb +11 -0
- data/lib/cuboid/support/buffer.rb +3 -0
- data/lib/cuboid/support/buffer/autoflush.rb +61 -0
- data/lib/cuboid/support/buffer/base.rb +91 -0
- data/lib/cuboid/support/cache.rb +7 -0
- data/lib/cuboid/support/cache/base.rb +226 -0
- data/lib/cuboid/support/cache/least_cost_replacement.rb +77 -0
- data/lib/cuboid/support/cache/least_recently_pushed.rb +21 -0
- data/lib/cuboid/support/cache/least_recently_used.rb +31 -0
- data/lib/cuboid/support/cache/preference.rb +31 -0
- data/lib/cuboid/support/cache/random_replacement.rb +20 -0
- data/lib/cuboid/support/crypto.rb +2 -0
- data/lib/cuboid/support/crypto/rsa_aes_cbc.rb +86 -0
- data/lib/cuboid/support/database.rb +5 -0
- data/lib/cuboid/support/database/base.rb +177 -0
- data/lib/cuboid/support/database/categorized_queue.rb +195 -0
- data/lib/cuboid/support/database/hash.rb +300 -0
- data/lib/cuboid/support/database/queue.rb +149 -0
- data/lib/cuboid/support/filter.rb +3 -0
- data/lib/cuboid/support/filter/base.rb +110 -0
- data/lib/cuboid/support/filter/set.rb +29 -0
- data/lib/cuboid/support/glob.rb +27 -0
- data/lib/cuboid/support/mixins.rb +8 -0
- data/lib/cuboid/support/mixins/observable.rb +99 -0
- data/lib/cuboid/support/mixins/parts.rb +20 -0
- data/lib/cuboid/support/mixins/profiler.rb +93 -0
- data/lib/cuboid/support/mixins/spec_instances.rb +65 -0
- data/lib/cuboid/support/mixins/terminal.rb +57 -0
- data/lib/cuboid/system.rb +119 -0
- data/lib/cuboid/system/platforms.rb +84 -0
- data/lib/cuboid/system/platforms/linux.rb +26 -0
- data/lib/cuboid/system/platforms/mixins/unix.rb +46 -0
- data/lib/cuboid/system/platforms/osx.rb +25 -0
- data/lib/cuboid/system/platforms/windows.rb +81 -0
- data/lib/cuboid/system/slots.rb +143 -0
- data/lib/cuboid/ui/output.rb +52 -0
- data/lib/cuboid/ui/output_interface.rb +43 -0
- data/lib/cuboid/ui/output_interface/abstract.rb +68 -0
- data/lib/cuboid/ui/output_interface/controls.rb +84 -0
- data/lib/cuboid/ui/output_interface/error_logging.rb +119 -0
- data/lib/cuboid/ui/output_interface/implemented.rb +58 -0
- data/lib/cuboid/ui/output_interface/personalization.rb +62 -0
- data/lib/cuboid/utilities.rb +155 -0
- data/lib/cuboid/version.rb +4 -3
- data/lib/version +1 -0
- data/logs/placeholder +0 -0
- data/spec/cuboid/application/parts/data_spec.rb +12 -0
- data/spec/cuboid/application/parts/report_spec.rb +6 -0
- data/spec/cuboid/application/parts/state_spec.rb +192 -0
- data/spec/cuboid/application/runtime_spec.rb +21 -0
- data/spec/cuboid/application_spec.rb +37 -0
- data/spec/cuboid/data/application_spec.rb +22 -0
- data/spec/cuboid/data_spec.rb +47 -0
- data/spec/cuboid/error_spec.rb +23 -0
- data/spec/cuboid/option_groups/datastore_spec.rb +54 -0
- data/spec/cuboid/option_groups/dispatcher_spec.rb +12 -0
- data/spec/cuboid/option_groups/output_spec.rb +11 -0
- data/spec/cuboid/option_groups/paths_spec.rb +184 -0
- data/spec/cuboid/option_groups/report_spec.rb +26 -0
- data/spec/cuboid/option_groups/rpc_spec.rb +53 -0
- data/spec/cuboid/option_groups/snapshot_spec.rb +26 -0
- data/spec/cuboid/option_groups/system.rb +12 -0
- data/spec/cuboid/options_spec.rb +218 -0
- data/spec/cuboid/report_spec.rb +221 -0
- data/spec/cuboid/rest/server_spec.rb +1205 -0
- data/spec/cuboid/rpc/client/base_spec.rb +151 -0
- data/spec/cuboid/rpc/client/dispatcher_spec.rb +13 -0
- data/spec/cuboid/rpc/client/instance_spec.rb +38 -0
- data/spec/cuboid/rpc/server/active_options_spec.rb +21 -0
- data/spec/cuboid/rpc/server/base_spec.rb +60 -0
- data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +222 -0
- data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +112 -0
- data/spec/cuboid/rpc/server/dispatcher_spec.rb +317 -0
- data/spec/cuboid/rpc/server/instance_spec.rb +307 -0
- data/spec/cuboid/rpc/server/output_spec.rb +32 -0
- data/spec/cuboid/rpc/server/scheduler_spec.rb +400 -0
- data/spec/cuboid/ruby/array_spec.rb +77 -0
- data/spec/cuboid/ruby/hash_spec.rb +63 -0
- data/spec/cuboid/ruby/object_spec.rb +22 -0
- data/spec/cuboid/snapshot_spec.rb +123 -0
- data/spec/cuboid/state/application_spec.rb +538 -0
- data/spec/cuboid/state/options_spec.rb +37 -0
- data/spec/cuboid/state_spec.rb +53 -0
- data/spec/cuboid/support/buffer/autoflush_spec.rb +78 -0
- data/spec/cuboid/support/buffer/base_spec.rb +193 -0
- data/spec/cuboid/support/cache/least_cost_replacement_spec.rb +61 -0
- data/spec/cuboid/support/cache/least_recently_pushed_spec.rb +90 -0
- data/spec/cuboid/support/cache/least_recently_used_spec.rb +80 -0
- data/spec/cuboid/support/cache/preference_spec.rb +37 -0
- data/spec/cuboid/support/cache/random_replacement_spec.rb +42 -0
- data/spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb +28 -0
- data/spec/cuboid/support/database/categorized_queue_spec.rb +327 -0
- data/spec/cuboid/support/database/hash_spec.rb +204 -0
- data/spec/cuboid/support/database/scheduler_spec.rb +325 -0
- data/spec/cuboid/support/filter/set_spec.rb +19 -0
- data/spec/cuboid/support/glob_spec.rb +75 -0
- data/spec/cuboid/support/mixins/observable_spec.rb +95 -0
- data/spec/cuboid/system/platforms/linux_spec.rb +31 -0
- data/spec/cuboid/system/platforms/osx_spec.rb +32 -0
- data/spec/cuboid/system/platforms/windows_spec.rb +41 -0
- data/spec/cuboid/system/slots_spec.rb +202 -0
- data/spec/cuboid/system_spec.rb +105 -0
- data/spec/cuboid/utilities_spec.rb +131 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/factories/placeholder +0 -0
- data/spec/support/factories/scan_report.rb +18 -0
- data/spec/support/fixtures/empty/placeholder +0 -0
- data/spec/support/fixtures/executables/node.rb +50 -0
- data/spec/support/fixtures/mock_app.rb +61 -0
- data/spec/support/fixtures/mock_app/test_service.rb +64 -0
- data/spec/support/fixtures/services/echo.rb +64 -0
- data/spec/support/helpers/framework.rb +3 -0
- data/spec/support/helpers/matchers.rb +5 -0
- data/spec/support/helpers/misc.rb +3 -0
- data/spec/support/helpers/paths.rb +15 -0
- data/spec/support/helpers/request_helpers.rb +38 -0
- data/spec/support/helpers/requires.rb +8 -0
- data/spec/support/helpers/resets.rb +52 -0
- data/spec/support/helpers/web_server.rb +15 -0
- data/spec/support/lib/factory.rb +107 -0
- data/spec/support/lib/web_server_client.rb +41 -0
- data/spec/support/lib/web_server_dispatcher.rb +25 -0
- data/spec/support/lib/web_server_manager.rb +118 -0
- data/spec/support/logs/placeholder +0 -0
- data/spec/support/pems/cacert.pem +37 -0
- data/spec/support/pems/client/cert.pem +37 -0
- data/spec/support/pems/client/foo-cert.pem +39 -0
- data/spec/support/pems/client/foo-key.pem +51 -0
- data/spec/support/pems/client/key.pem +51 -0
- data/spec/support/pems/server/cert.pem +37 -0
- data/spec/support/pems/server/key.pem +51 -0
- data/spec/support/reports/placeholder +0 -0
- data/spec/support/shared/application.rb +10 -0
- data/spec/support/shared/component.rb +31 -0
- data/spec/support/shared/component/options/base.rb +187 -0
- data/spec/support/shared/option_group.rb +98 -0
- data/spec/support/shared/support/cache.rb +419 -0
- data/spec/support/shared/support/filter.rb +143 -0
- data/spec/support/shared/system/platforms/base.rb +25 -0
- data/spec/support/shared/system/platforms/mixins/unix.rb +37 -0
- data/spec/support/snapshots/placeholder +0 -0
- metadata +566 -21
- data/.gitignore +0 -8
- data/bin/console +0 -15
- data/bin/setup +0 -8
@@ -0,0 +1,262 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'arachni/reactor'
|
3
|
+
|
4
|
+
module Cuboid
|
5
|
+
module Processes
|
6
|
+
|
7
|
+
# Helper for managing processes.
|
8
|
+
#
|
9
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
10
|
+
class Manager
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
RUNNER = "#{File.dirname( __FILE__ )}/executables/base.rb"
|
14
|
+
|
15
|
+
# @return [Array<Integer>] PIDs of all running processes.
|
16
|
+
attr_reader :pids
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
reset
|
20
|
+
end
|
21
|
+
|
22
|
+
def reset
|
23
|
+
@pids = []
|
24
|
+
@discard_output = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Integer] pid
|
28
|
+
# Adds a PID to the {#pids} and detaches the process.
|
29
|
+
#
|
30
|
+
# @return [Integer] `pid`
|
31
|
+
def <<( pid )
|
32
|
+
@pids << pid
|
33
|
+
Process.detach pid
|
34
|
+
pid
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Integer] pid
|
38
|
+
# PID of the process to kill.
|
39
|
+
def kill( pid )
|
40
|
+
fail 'Cannot kill self.' if pid == Process.pid
|
41
|
+
|
42
|
+
Timeout.timeout 10 do
|
43
|
+
while sleep 0.1 do
|
44
|
+
begin
|
45
|
+
Process.kill( Cuboid.windows? ? 'KILL' : 'TERM', pid )
|
46
|
+
|
47
|
+
# Either kill was successful or we don't have enough perms or
|
48
|
+
# we hit a reused PID for someone else's process, either way,
|
49
|
+
# consider the process gone.
|
50
|
+
rescue Errno::ESRCH, Errno::EPERM,
|
51
|
+
# Don't kill ourselves.
|
52
|
+
SignalException
|
53
|
+
|
54
|
+
@pids.delete pid
|
55
|
+
return
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rescue Timeout::Error
|
60
|
+
end
|
61
|
+
|
62
|
+
def find( bin )
|
63
|
+
find_in_path( bin ) || find_in_applications( bin ) ||
|
64
|
+
find_in_program_files( bin )
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_in_path( bin )
|
68
|
+
@find_in_path ||= {}
|
69
|
+
return @find_in_path[bin] if @find_in_path.include?( bin )
|
70
|
+
|
71
|
+
if Cuboid.windows?
|
72
|
+
bin = "#{bin}.exe"
|
73
|
+
end
|
74
|
+
|
75
|
+
ENV['PATH'].split( File::PATH_SEPARATOR ).each do |path|
|
76
|
+
f = File.join( path, bin )
|
77
|
+
return @find_in_path[bin] = f if File.exist?( f )
|
78
|
+
end
|
79
|
+
|
80
|
+
@find_in_path[bin] = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_in_applications( bin )
|
84
|
+
return if !Cuboid.mac?
|
85
|
+
|
86
|
+
@find_in_applications ||= {}
|
87
|
+
return @find_in_applications[bin] if @find_in_applications.include?( bin )
|
88
|
+
|
89
|
+
[
|
90
|
+
'/Applications/*/Contents/MacOS'
|
91
|
+
].each do |root|
|
92
|
+
glob = File.join( root, '**', bin )
|
93
|
+
|
94
|
+
exe = Dir.glob( glob ).find { |f| File.executable?( f ) }
|
95
|
+
return @find_in_applications[bin] = exe if exe
|
96
|
+
end
|
97
|
+
|
98
|
+
@find_in_applications[bin] = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_in_program_files( bin )
|
102
|
+
return if !Cuboid.windows?
|
103
|
+
|
104
|
+
@find_in_program_files ||= {}
|
105
|
+
return @find_in_program_files[bin] if @find_in_program_files.include?( bin )
|
106
|
+
|
107
|
+
[
|
108
|
+
ENV['PROGRAMFILES'] || '\\Program Files',
|
109
|
+
ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
|
110
|
+
ENV['ProgramW6432'] || '\\Program Files'
|
111
|
+
].each do |root|
|
112
|
+
glob = File.join( root, '**', "#{bin}.exe" )
|
113
|
+
glob.tr!( '\\', '/' )
|
114
|
+
|
115
|
+
exe = Dir.glob( glob ).find { |f| File.executable?( f ) }
|
116
|
+
return @find_in_program_files[bin] = exe if exe
|
117
|
+
end
|
118
|
+
|
119
|
+
@find_in_program_files[bin] = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
# @param [Integer] pid
|
123
|
+
# @return [Boolean]
|
124
|
+
# `true` if the process is alive, `false` otherwise.
|
125
|
+
def alive?( pid )
|
126
|
+
# Windows is not big on POSIX so try it its own way if possible.
|
127
|
+
if Cuboid.windows?
|
128
|
+
begin
|
129
|
+
alive = false
|
130
|
+
processes = wmi.ExecQuery( "select ProcessId from win32_process where ProcessID='#{pid}'" )
|
131
|
+
processes.each do |proc|
|
132
|
+
proc.ole_free
|
133
|
+
alive = true
|
134
|
+
end
|
135
|
+
processes.ole_free
|
136
|
+
|
137
|
+
return alive
|
138
|
+
rescue WIN32OLERuntimeError
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
!!(Process.kill( 0, pid ) rescue false)
|
143
|
+
end
|
144
|
+
|
145
|
+
# @param [Array<Integer>] pids
|
146
|
+
# PIDs of the process to {Cuboid::Processes::Manager#kill}.
|
147
|
+
def kill_many( pids )
|
148
|
+
pids.each { |pid| kill pid }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Kills all {#pids processes}.
|
152
|
+
def killall
|
153
|
+
kill_many @pids.dup
|
154
|
+
@pids.clear
|
155
|
+
end
|
156
|
+
|
157
|
+
# Stops the Reactor.
|
158
|
+
def kill_reactor
|
159
|
+
Arachni::Reactor.stop
|
160
|
+
rescue
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
# Overrides the default setting of discarding process outputs.
|
165
|
+
def preserve_output
|
166
|
+
@discard_output = false
|
167
|
+
end
|
168
|
+
|
169
|
+
def preserve_output?
|
170
|
+
!discard_output?
|
171
|
+
end
|
172
|
+
|
173
|
+
def discard_output
|
174
|
+
@discard_output = true
|
175
|
+
end
|
176
|
+
|
177
|
+
def discard_output?
|
178
|
+
@discard_output
|
179
|
+
end
|
180
|
+
|
181
|
+
# @param [String] executable
|
182
|
+
# Name of the executable Ruby script found in {OptionGroups::Paths#executables}
|
183
|
+
# without the '.rb' extension.
|
184
|
+
# @param [Hash] options
|
185
|
+
# Options to pass to the script -- can be retrieved from `$options`.
|
186
|
+
#
|
187
|
+
# @return [Integer]
|
188
|
+
# PID of the process.
|
189
|
+
def spawn( executable, options = {} )
|
190
|
+
fail ArgumentError, 'Fork not supported.' if options.delete(:fork)
|
191
|
+
|
192
|
+
stdin = options.delete(:stdin)
|
193
|
+
stdout = options.delete(:stdout)
|
194
|
+
stderr = options.delete(:stderr)
|
195
|
+
new_pgroup = options.delete(:new_pgroup)
|
196
|
+
|
197
|
+
spawn_options = {}
|
198
|
+
|
199
|
+
if new_pgroup
|
200
|
+
if Cuboid.windows?
|
201
|
+
spawn_options[:new_pgroup] = new_pgroup
|
202
|
+
else
|
203
|
+
spawn_options[:pgroup] = new_pgroup
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
spawn_options[:in] = stdin if stdin
|
208
|
+
spawn_options[:out] = stdout if stdout
|
209
|
+
spawn_options[:err] = stderr if stderr
|
210
|
+
|
211
|
+
options[:ppid] = Process.pid
|
212
|
+
options[:tmpdir] = Options.paths.tmpdir
|
213
|
+
|
214
|
+
cuboid_options = Options.dup.update( options.delete(:options) || {} ).to_h
|
215
|
+
encoded_cuboid_options = Base64.strict_encode64( Marshal.dump( cuboid_options ) )
|
216
|
+
|
217
|
+
if executable.is_a? Symbol
|
218
|
+
executable = "#{Options.paths.executables}/#{executable}.rb"
|
219
|
+
elsif !File.exist?( executable )
|
220
|
+
raise ArgumentError, "Executable does not exist: #{executable}"
|
221
|
+
end
|
222
|
+
|
223
|
+
encoded_options = Base64.strict_encode64( Marshal.dump( options ) )
|
224
|
+
argv = [executable, encoded_options]
|
225
|
+
|
226
|
+
# It's very, **VERY** important that we use this argument format as
|
227
|
+
# it bypasses the OS shell and we can thus count on a 1-to-1 process
|
228
|
+
# creation and that the PID we get will be for the actual process.
|
229
|
+
pid = Process.spawn(
|
230
|
+
{
|
231
|
+
'CUBOID_SPAWN_OPTIONS' => encoded_cuboid_options
|
232
|
+
},
|
233
|
+
RbConfig.ruby,
|
234
|
+
RUNNER,
|
235
|
+
*(argv + [spawn_options])
|
236
|
+
)
|
237
|
+
|
238
|
+
self << pid
|
239
|
+
pid
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.method_missing( sym, *args, &block )
|
243
|
+
if instance.respond_to?( sym )
|
244
|
+
instance.send( sym, *args, &block )
|
245
|
+
else
|
246
|
+
super( sym, *args, &block )
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.respond_to?( m )
|
251
|
+
super( m ) || instance.respond_to?( m )
|
252
|
+
end
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
def wmi
|
257
|
+
@wmi ||= WIN32OLE.connect( 'winmgmts://' )
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Cuboid
|
2
|
+
module Processes
|
3
|
+
|
4
|
+
# Helper for managing {RPC::Server::Scheduler} processes.
|
5
|
+
#
|
6
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
7
|
+
class Schedulers
|
8
|
+
include Singleton
|
9
|
+
include Utilities
|
10
|
+
|
11
|
+
# @return [Array<String>] URLs of all running Queues.
|
12
|
+
attr_reader :list
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@list = []
|
16
|
+
@clients = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Connects to a Scheduler by URL.
|
20
|
+
#
|
21
|
+
# @param [String] url URL of the Scheduler.
|
22
|
+
# @param [Hash] options Options for the RPC client.
|
23
|
+
#
|
24
|
+
# @return [RPC::Client::Scheduler]
|
25
|
+
def connect( url, options = nil )
|
26
|
+
Arachni::Reactor.global.run_in_thread if !Arachni::Reactor.global.running?
|
27
|
+
|
28
|
+
fresh = false
|
29
|
+
if options
|
30
|
+
fresh = options.delete( :fresh )
|
31
|
+
end
|
32
|
+
|
33
|
+
if fresh
|
34
|
+
@clients[url] = RPC::Client::Scheduler.new( url, options )
|
35
|
+
else
|
36
|
+
@clients[url] ||= RPC::Client::Scheduler.new( url, options )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Block] block Block to pass an RPC client for each Scheduler.
|
41
|
+
def each( &block )
|
42
|
+
@list.each do |url|
|
43
|
+
block.call connect( url )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Spawns a {RPC::Server::Scheduler} process.
|
48
|
+
#
|
49
|
+
# @param [Hash] options
|
50
|
+
# To be passed to {Cuboid::Options#set}. Allows `address` instead of
|
51
|
+
# `rpc_server_address` and `port` instead of `rpc_port`.
|
52
|
+
#
|
53
|
+
# @return [RPC::Client::Queue]
|
54
|
+
def spawn( options = {} )
|
55
|
+
fork = options.delete(:fork)
|
56
|
+
|
57
|
+
options = {
|
58
|
+
dispatcher: {
|
59
|
+
url: options[:dispatcher],
|
60
|
+
},
|
61
|
+
rpc: {
|
62
|
+
server_port: options[:port] || Utilities.available_port,
|
63
|
+
server_address: options[:address] || '127.0.0.1',
|
64
|
+
server_external_address: options[:external_address]
|
65
|
+
},
|
66
|
+
paths: {
|
67
|
+
application: options[:application] || Options.paths.application
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
pid = Manager.spawn( :scheduler, options: options, fork: fork )
|
72
|
+
|
73
|
+
url = "#{options[:rpc][:server_address]}:#{options[:rpc][:server_port]}"
|
74
|
+
while sleep( 0.1 )
|
75
|
+
begin
|
76
|
+
connect( url, connection_pool_size: 1, max_retries: 1 ).alive?
|
77
|
+
break
|
78
|
+
rescue => e
|
79
|
+
# ap e
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
@list << url
|
84
|
+
connect( url, fresh: true ).tap { |c| c.pid = pid }
|
85
|
+
end
|
86
|
+
|
87
|
+
# @note Will also kill all Instances started by the Scheduler.
|
88
|
+
#
|
89
|
+
# @param [String] url URL of the Scheduler to kill.
|
90
|
+
def kill( url )
|
91
|
+
scheduler = connect( url )
|
92
|
+
scheduler.clear
|
93
|
+
scheduler.running.each do |id, instance|
|
94
|
+
Manager.kill instance['pid']
|
95
|
+
end
|
96
|
+
Manager.kill scheduler.pid
|
97
|
+
rescue => e
|
98
|
+
#ap e
|
99
|
+
#ap e.backtrace
|
100
|
+
nil
|
101
|
+
ensure
|
102
|
+
@list.delete( url )
|
103
|
+
@clients.delete( url ).close
|
104
|
+
end
|
105
|
+
|
106
|
+
# Kills all {Queues #list}.
|
107
|
+
def killall
|
108
|
+
@list.dup.each do |url|
|
109
|
+
kill url
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.method_missing( sym, *args, &block )
|
114
|
+
if instance.respond_to?( sym )
|
115
|
+
instance.send( sym, *args, &block )
|
116
|
+
else
|
117
|
+
super( sym, *args, &block )
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.respond_to?( m )
|
122
|
+
super( m ) || instance.respond_to?( m )
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require_relative 'rpc/serializer'
|
2
|
+
|
3
|
+
module Cuboid
|
4
|
+
|
5
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
6
|
+
class Report
|
7
|
+
include Utilities
|
8
|
+
|
9
|
+
EXTENSION = 'crf'
|
10
|
+
|
11
|
+
INTEGER_SIZE = 4
|
12
|
+
|
13
|
+
UNPACK = 'N'
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
# {Cuboid::VERSION}
|
17
|
+
attr_accessor :version
|
18
|
+
|
19
|
+
attr_accessor :application
|
20
|
+
|
21
|
+
# @return [Symbol]
|
22
|
+
attr_accessor :status
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
# Scan seed.
|
26
|
+
attr_accessor :seed
|
27
|
+
|
28
|
+
# @return [Hash]
|
29
|
+
# {Options#to_h}
|
30
|
+
attr_accessor :options
|
31
|
+
|
32
|
+
# Arbitrary data from the Application.
|
33
|
+
attr_accessor :data
|
34
|
+
|
35
|
+
# @return [Time]
|
36
|
+
# The date and time when the scan started.
|
37
|
+
attr_accessor :start_datetime
|
38
|
+
|
39
|
+
# @return [Time]
|
40
|
+
# The date and time when the scan finished.
|
41
|
+
attr_accessor :finish_datetime
|
42
|
+
|
43
|
+
def initialize( options = {} )
|
44
|
+
options.each { |k, v| send( "#{k}=", v ) }
|
45
|
+
|
46
|
+
@version ||= Cuboid::VERSION
|
47
|
+
|
48
|
+
@start_datetime ||= Time.now
|
49
|
+
@finish_datetime ||= Time.now
|
50
|
+
end
|
51
|
+
|
52
|
+
# @note If no {#finish_datetime} has been provided, it will use `Time.now`.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
# `{#start_datetime} - {#finish_datetime}` in `00:00:00`
|
56
|
+
# (`hours:minutes:seconds`) format.
|
57
|
+
def delta_time
|
58
|
+
seconds_to_hms( (@finish_datetime || Time.now) - @start_datetime )
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [String] report
|
62
|
+
# Location of the report.
|
63
|
+
#
|
64
|
+
# @return [Hash]
|
65
|
+
# {#summary} associated with the given report.
|
66
|
+
def self.read_summary( report )
|
67
|
+
File.open( report ) do |f|
|
68
|
+
f.seek -INTEGER_SIZE, IO::SEEK_END
|
69
|
+
summary_size = f.read( INTEGER_SIZE ).unpack( 'N' ).first
|
70
|
+
|
71
|
+
f.seek -summary_size-INTEGER_SIZE, IO::SEEK_END
|
72
|
+
summary = RPC::Serializer.load( f.read( summary_size ) ).my_symbolize_keys
|
73
|
+
summary[:application] = ObjectSpace.const_get( summary[:application].to_sym )
|
74
|
+
summary
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Loads and a {#save saved} {Report} object from file.
|
79
|
+
#
|
80
|
+
# @param [String] file
|
81
|
+
# File created by {#save}.
|
82
|
+
#
|
83
|
+
# @return [Report]
|
84
|
+
# Loaded instance.
|
85
|
+
def self.load( file )
|
86
|
+
File.open( file, 'rb' ) do |f|
|
87
|
+
from_rpc_data RPC::Serializer.load( self.crf_without_summary( f ) )
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param [String] location
|
92
|
+
# Location for the {#to_crf dumped} report file.
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
# Absolute location of the report.
|
96
|
+
def save( location = nil )
|
97
|
+
if !location
|
98
|
+
location = default_filename
|
99
|
+
elsif File.directory? location
|
100
|
+
location += "/#{default_filename}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# We do it this way to prevent FS watchers from grabbing files that
|
104
|
+
# are in the process of being written and thus partial.
|
105
|
+
tmp = "#{Options.paths.tmpdir}/#{Utilities.generate_token}"
|
106
|
+
IO.binwrite( tmp, to_crf )
|
107
|
+
FileUtils.mv tmp, location
|
108
|
+
|
109
|
+
File.expand_path( location )
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [String]
|
113
|
+
# Report serialized in the Cuboid Report format.
|
114
|
+
def to_crf
|
115
|
+
crf = RPC::Serializer.dump( self )
|
116
|
+
|
117
|
+
sum = summary
|
118
|
+
sum[:application] = sum[:application].to_s
|
119
|
+
# Append metadata to the end of the dump.
|
120
|
+
metadata = RPC::Serializer.dump( sum )
|
121
|
+
crf << [metadata, metadata.size].pack( "a*#{UNPACK}" )
|
122
|
+
|
123
|
+
crf
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.from_crf( data )
|
127
|
+
from_rpc_data RPC::Serializer.load(
|
128
|
+
self.crf_without_summary( StringIO.new( data ) )
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Hash]
|
133
|
+
# Hash representation of `self`.
|
134
|
+
def to_h
|
135
|
+
h = {
|
136
|
+
application: @application,
|
137
|
+
version: @version,
|
138
|
+
status: @status,
|
139
|
+
seed: @seed,
|
140
|
+
data: @data,
|
141
|
+
options: Cuboid::Options.hash_to_rpc_data( @options ),
|
142
|
+
start_datetime: @start_datetime.to_s,
|
143
|
+
finish_datetime: @finish_datetime.to_s,
|
144
|
+
delta_time: delta_time
|
145
|
+
}
|
146
|
+
end
|
147
|
+
alias :to_hash :to_h
|
148
|
+
|
149
|
+
# @return [Hash]
|
150
|
+
# Summary data of the report.
|
151
|
+
def summary
|
152
|
+
{
|
153
|
+
application: @application,
|
154
|
+
version: @version,
|
155
|
+
status: @status,
|
156
|
+
seed: @seed,
|
157
|
+
start_datetime: @start_datetime.to_s,
|
158
|
+
finish_datetime: @finish_datetime.to_s,
|
159
|
+
delta_time: delta_time
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
# @return [Hash]
|
164
|
+
# Data representing this instance that are suitable the RPC transmission.
|
165
|
+
def to_rpc_data
|
166
|
+
data = {}
|
167
|
+
instance_variables.each do |ivar|
|
168
|
+
data[ivar.to_s.gsub('@','')] = instance_variable_get( ivar )
|
169
|
+
end
|
170
|
+
|
171
|
+
data['application'] = data['application'].to_s
|
172
|
+
data['data'] = @application.serializer.dump( data['data'] )
|
173
|
+
data['options'] = @application.serializer.dump( data['options'] )
|
174
|
+
|
175
|
+
data['start_datetime'] = data['start_datetime'].to_s
|
176
|
+
data['finish_datetime'] = data['finish_datetime'].to_s
|
177
|
+
data
|
178
|
+
end
|
179
|
+
|
180
|
+
# @param [Hash] data {#to_rpc_data}
|
181
|
+
# @return [DOM]
|
182
|
+
def self.from_rpc_data( data )
|
183
|
+
data['start_datetime'] = Time.parse( data['start_datetime'] )
|
184
|
+
data['finish_datetime'] = Time.parse( data['finish_datetime'] )
|
185
|
+
|
186
|
+
data['application'] = ObjectSpace.const_get( data['application'] )
|
187
|
+
data['data'] = data['application'].serializer.load( data['data'] )
|
188
|
+
data['options'] = data['application'].serializer.load( data['options'] )
|
189
|
+
|
190
|
+
new data
|
191
|
+
end
|
192
|
+
|
193
|
+
def ==( other )
|
194
|
+
hash == other.hash
|
195
|
+
end
|
196
|
+
|
197
|
+
def hash
|
198
|
+
h = to_hash
|
199
|
+
[:start_datetime, :finish_datetime, :delta_datetime].each do |k|
|
200
|
+
h.delete k
|
201
|
+
end
|
202
|
+
h.hash
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
def self.crf_without_summary( io )
|
208
|
+
io.seek -INTEGER_SIZE, IO::SEEK_END
|
209
|
+
summary_size = io.read( INTEGER_SIZE ).unpack( UNPACK ).first
|
210
|
+
|
211
|
+
io.rewind
|
212
|
+
io.read( io.size - summary_size - INTEGER_SIZE )
|
213
|
+
end
|
214
|
+
|
215
|
+
def default_filename
|
216
|
+
"Cuboid #{@finish_datetime.to_s.gsub( ':', '_' )}.#{EXTENSION}"
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|