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.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -0
  3. data/Gemfile +20 -5
  4. data/LICENSE.md +22 -0
  5. data/README.md +158 -19
  6. data/Rakefile +56 -3
  7. data/config/paths.yml +15 -0
  8. data/cuboid.gemspec +61 -23
  9. data/lib/cuboid.rb +96 -4
  10. data/lib/cuboid/application.rb +326 -0
  11. data/lib/cuboid/application/parts/data.rb +18 -0
  12. data/lib/cuboid/application/parts/report.rb +29 -0
  13. data/lib/cuboid/application/parts/state.rb +274 -0
  14. data/lib/cuboid/application/runtime.rb +25 -0
  15. data/lib/cuboid/banner.rb +13 -0
  16. data/lib/cuboid/data.rb +86 -0
  17. data/lib/cuboid/data/application.rb +52 -0
  18. data/lib/cuboid/error.rb +9 -0
  19. data/lib/cuboid/option_group.rb +129 -0
  20. data/lib/cuboid/option_groups.rb +8 -0
  21. data/lib/cuboid/option_groups/datastore.rb +23 -0
  22. data/lib/cuboid/option_groups/dispatcher.rb +38 -0
  23. data/lib/cuboid/option_groups/output.rb +14 -0
  24. data/lib/cuboid/option_groups/paths.rb +184 -0
  25. data/lib/cuboid/option_groups/report.rb +39 -0
  26. data/lib/cuboid/option_groups/rpc.rb +105 -0
  27. data/lib/cuboid/option_groups/scheduler.rb +27 -0
  28. data/lib/cuboid/option_groups/snapshot.rb +13 -0
  29. data/lib/cuboid/option_groups/system.rb +10 -0
  30. data/lib/cuboid/options.rb +254 -0
  31. data/lib/cuboid/processes.rb +13 -0
  32. data/lib/cuboid/processes/dispatchers.rb +140 -0
  33. data/lib/cuboid/processes/executables/base.rb +54 -0
  34. data/lib/cuboid/processes/executables/dispatcher.rb +5 -0
  35. data/lib/cuboid/processes/executables/instance.rb +12 -0
  36. data/lib/cuboid/processes/executables/rest_service.rb +13 -0
  37. data/lib/cuboid/processes/executables/scheduler.rb +5 -0
  38. data/lib/cuboid/processes/helpers.rb +4 -0
  39. data/lib/cuboid/processes/helpers/dispatchers.rb +23 -0
  40. data/lib/cuboid/processes/helpers/instances.rb +39 -0
  41. data/lib/cuboid/processes/helpers/processes.rb +23 -0
  42. data/lib/cuboid/processes/helpers/schedulers.rb +23 -0
  43. data/lib/cuboid/processes/instances.rb +203 -0
  44. data/lib/cuboid/processes/manager.rb +262 -0
  45. data/lib/cuboid/processes/schedulers.rb +128 -0
  46. data/lib/cuboid/report.rb +220 -0
  47. data/lib/cuboid/rest/server.rb +165 -0
  48. data/lib/cuboid/rest/server/instance_helpers.rb +99 -0
  49. data/lib/cuboid/rest/server/routes/dispatcher.rb +41 -0
  50. data/lib/cuboid/rest/server/routes/grid.rb +41 -0
  51. data/lib/cuboid/rest/server/routes/instances.rb +131 -0
  52. data/lib/cuboid/rest/server/routes/scheduler.rb +140 -0
  53. data/lib/cuboid/rpc/client.rb +3 -0
  54. data/lib/cuboid/rpc/client/base.rb +58 -0
  55. data/lib/cuboid/rpc/client/dispatcher.rb +58 -0
  56. data/lib/cuboid/rpc/client/instance.rb +100 -0
  57. data/lib/cuboid/rpc/client/instance/service.rb +37 -0
  58. data/lib/cuboid/rpc/client/scheduler.rb +46 -0
  59. data/lib/cuboid/rpc/serializer.rb +92 -0
  60. data/lib/cuboid/rpc/server/active_options.rb +38 -0
  61. data/lib/cuboid/rpc/server/application_wrapper.rb +138 -0
  62. data/lib/cuboid/rpc/server/base.rb +63 -0
  63. data/lib/cuboid/rpc/server/dispatcher.rb +317 -0
  64. data/lib/cuboid/rpc/server/dispatcher/node.rb +247 -0
  65. data/lib/cuboid/rpc/server/dispatcher/service.rb +145 -0
  66. data/lib/cuboid/rpc/server/instance.rb +338 -0
  67. data/lib/cuboid/rpc/server/output.rb +92 -0
  68. data/lib/cuboid/rpc/server/scheduler.rb +482 -0
  69. data/lib/cuboid/ruby.rb +4 -0
  70. data/lib/cuboid/ruby/array.rb +17 -0
  71. data/lib/cuboid/ruby/hash.rb +41 -0
  72. data/lib/cuboid/ruby/object.rb +32 -0
  73. data/lib/cuboid/snapshot.rb +186 -0
  74. data/lib/cuboid/state.rb +94 -0
  75. data/lib/cuboid/state/application.rb +309 -0
  76. data/lib/cuboid/state/options.rb +27 -0
  77. data/lib/cuboid/support.rb +11 -0
  78. data/lib/cuboid/support/buffer.rb +3 -0
  79. data/lib/cuboid/support/buffer/autoflush.rb +61 -0
  80. data/lib/cuboid/support/buffer/base.rb +91 -0
  81. data/lib/cuboid/support/cache.rb +7 -0
  82. data/lib/cuboid/support/cache/base.rb +226 -0
  83. data/lib/cuboid/support/cache/least_cost_replacement.rb +77 -0
  84. data/lib/cuboid/support/cache/least_recently_pushed.rb +21 -0
  85. data/lib/cuboid/support/cache/least_recently_used.rb +31 -0
  86. data/lib/cuboid/support/cache/preference.rb +31 -0
  87. data/lib/cuboid/support/cache/random_replacement.rb +20 -0
  88. data/lib/cuboid/support/crypto.rb +2 -0
  89. data/lib/cuboid/support/crypto/rsa_aes_cbc.rb +86 -0
  90. data/lib/cuboid/support/database.rb +5 -0
  91. data/lib/cuboid/support/database/base.rb +177 -0
  92. data/lib/cuboid/support/database/categorized_queue.rb +195 -0
  93. data/lib/cuboid/support/database/hash.rb +300 -0
  94. data/lib/cuboid/support/database/queue.rb +149 -0
  95. data/lib/cuboid/support/filter.rb +3 -0
  96. data/lib/cuboid/support/filter/base.rb +110 -0
  97. data/lib/cuboid/support/filter/set.rb +29 -0
  98. data/lib/cuboid/support/glob.rb +27 -0
  99. data/lib/cuboid/support/mixins.rb +8 -0
  100. data/lib/cuboid/support/mixins/observable.rb +99 -0
  101. data/lib/cuboid/support/mixins/parts.rb +20 -0
  102. data/lib/cuboid/support/mixins/profiler.rb +93 -0
  103. data/lib/cuboid/support/mixins/spec_instances.rb +65 -0
  104. data/lib/cuboid/support/mixins/terminal.rb +57 -0
  105. data/lib/cuboid/system.rb +119 -0
  106. data/lib/cuboid/system/platforms.rb +84 -0
  107. data/lib/cuboid/system/platforms/linux.rb +26 -0
  108. data/lib/cuboid/system/platforms/mixins/unix.rb +46 -0
  109. data/lib/cuboid/system/platforms/osx.rb +25 -0
  110. data/lib/cuboid/system/platforms/windows.rb +81 -0
  111. data/lib/cuboid/system/slots.rb +143 -0
  112. data/lib/cuboid/ui/output.rb +52 -0
  113. data/lib/cuboid/ui/output_interface.rb +43 -0
  114. data/lib/cuboid/ui/output_interface/abstract.rb +68 -0
  115. data/lib/cuboid/ui/output_interface/controls.rb +84 -0
  116. data/lib/cuboid/ui/output_interface/error_logging.rb +119 -0
  117. data/lib/cuboid/ui/output_interface/implemented.rb +58 -0
  118. data/lib/cuboid/ui/output_interface/personalization.rb +62 -0
  119. data/lib/cuboid/utilities.rb +155 -0
  120. data/lib/cuboid/version.rb +4 -3
  121. data/lib/version +1 -0
  122. data/logs/placeholder +0 -0
  123. data/spec/cuboid/application/parts/data_spec.rb +12 -0
  124. data/spec/cuboid/application/parts/report_spec.rb +6 -0
  125. data/spec/cuboid/application/parts/state_spec.rb +192 -0
  126. data/spec/cuboid/application/runtime_spec.rb +21 -0
  127. data/spec/cuboid/application_spec.rb +37 -0
  128. data/spec/cuboid/data/application_spec.rb +22 -0
  129. data/spec/cuboid/data_spec.rb +47 -0
  130. data/spec/cuboid/error_spec.rb +23 -0
  131. data/spec/cuboid/option_groups/datastore_spec.rb +54 -0
  132. data/spec/cuboid/option_groups/dispatcher_spec.rb +12 -0
  133. data/spec/cuboid/option_groups/output_spec.rb +11 -0
  134. data/spec/cuboid/option_groups/paths_spec.rb +184 -0
  135. data/spec/cuboid/option_groups/report_spec.rb +26 -0
  136. data/spec/cuboid/option_groups/rpc_spec.rb +53 -0
  137. data/spec/cuboid/option_groups/snapshot_spec.rb +26 -0
  138. data/spec/cuboid/option_groups/system.rb +12 -0
  139. data/spec/cuboid/options_spec.rb +218 -0
  140. data/spec/cuboid/report_spec.rb +221 -0
  141. data/spec/cuboid/rest/server_spec.rb +1205 -0
  142. data/spec/cuboid/rpc/client/base_spec.rb +151 -0
  143. data/spec/cuboid/rpc/client/dispatcher_spec.rb +13 -0
  144. data/spec/cuboid/rpc/client/instance_spec.rb +38 -0
  145. data/spec/cuboid/rpc/server/active_options_spec.rb +21 -0
  146. data/spec/cuboid/rpc/server/base_spec.rb +60 -0
  147. data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +222 -0
  148. data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +112 -0
  149. data/spec/cuboid/rpc/server/dispatcher_spec.rb +317 -0
  150. data/spec/cuboid/rpc/server/instance_spec.rb +307 -0
  151. data/spec/cuboid/rpc/server/output_spec.rb +32 -0
  152. data/spec/cuboid/rpc/server/scheduler_spec.rb +400 -0
  153. data/spec/cuboid/ruby/array_spec.rb +77 -0
  154. data/spec/cuboid/ruby/hash_spec.rb +63 -0
  155. data/spec/cuboid/ruby/object_spec.rb +22 -0
  156. data/spec/cuboid/snapshot_spec.rb +123 -0
  157. data/spec/cuboid/state/application_spec.rb +538 -0
  158. data/spec/cuboid/state/options_spec.rb +37 -0
  159. data/spec/cuboid/state_spec.rb +53 -0
  160. data/spec/cuboid/support/buffer/autoflush_spec.rb +78 -0
  161. data/spec/cuboid/support/buffer/base_spec.rb +193 -0
  162. data/spec/cuboid/support/cache/least_cost_replacement_spec.rb +61 -0
  163. data/spec/cuboid/support/cache/least_recently_pushed_spec.rb +90 -0
  164. data/spec/cuboid/support/cache/least_recently_used_spec.rb +80 -0
  165. data/spec/cuboid/support/cache/preference_spec.rb +37 -0
  166. data/spec/cuboid/support/cache/random_replacement_spec.rb +42 -0
  167. data/spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb +28 -0
  168. data/spec/cuboid/support/database/categorized_queue_spec.rb +327 -0
  169. data/spec/cuboid/support/database/hash_spec.rb +204 -0
  170. data/spec/cuboid/support/database/scheduler_spec.rb +325 -0
  171. data/spec/cuboid/support/filter/set_spec.rb +19 -0
  172. data/spec/cuboid/support/glob_spec.rb +75 -0
  173. data/spec/cuboid/support/mixins/observable_spec.rb +95 -0
  174. data/spec/cuboid/system/platforms/linux_spec.rb +31 -0
  175. data/spec/cuboid/system/platforms/osx_spec.rb +32 -0
  176. data/spec/cuboid/system/platforms/windows_spec.rb +41 -0
  177. data/spec/cuboid/system/slots_spec.rb +202 -0
  178. data/spec/cuboid/system_spec.rb +105 -0
  179. data/spec/cuboid/utilities_spec.rb +131 -0
  180. data/spec/spec_helper.rb +46 -0
  181. data/spec/support/factories/placeholder +0 -0
  182. data/spec/support/factories/scan_report.rb +18 -0
  183. data/spec/support/fixtures/empty/placeholder +0 -0
  184. data/spec/support/fixtures/executables/node.rb +50 -0
  185. data/spec/support/fixtures/mock_app.rb +61 -0
  186. data/spec/support/fixtures/mock_app/test_service.rb +64 -0
  187. data/spec/support/fixtures/services/echo.rb +64 -0
  188. data/spec/support/helpers/framework.rb +3 -0
  189. data/spec/support/helpers/matchers.rb +5 -0
  190. data/spec/support/helpers/misc.rb +3 -0
  191. data/spec/support/helpers/paths.rb +15 -0
  192. data/spec/support/helpers/request_helpers.rb +38 -0
  193. data/spec/support/helpers/requires.rb +8 -0
  194. data/spec/support/helpers/resets.rb +52 -0
  195. data/spec/support/helpers/web_server.rb +15 -0
  196. data/spec/support/lib/factory.rb +107 -0
  197. data/spec/support/lib/web_server_client.rb +41 -0
  198. data/spec/support/lib/web_server_dispatcher.rb +25 -0
  199. data/spec/support/lib/web_server_manager.rb +118 -0
  200. data/spec/support/logs/placeholder +0 -0
  201. data/spec/support/pems/cacert.pem +37 -0
  202. data/spec/support/pems/client/cert.pem +37 -0
  203. data/spec/support/pems/client/foo-cert.pem +39 -0
  204. data/spec/support/pems/client/foo-key.pem +51 -0
  205. data/spec/support/pems/client/key.pem +51 -0
  206. data/spec/support/pems/server/cert.pem +37 -0
  207. data/spec/support/pems/server/key.pem +51 -0
  208. data/spec/support/reports/placeholder +0 -0
  209. data/spec/support/shared/application.rb +10 -0
  210. data/spec/support/shared/component.rb +31 -0
  211. data/spec/support/shared/component/options/base.rb +187 -0
  212. data/spec/support/shared/option_group.rb +98 -0
  213. data/spec/support/shared/support/cache.rb +419 -0
  214. data/spec/support/shared/support/filter.rb +143 -0
  215. data/spec/support/shared/system/platforms/base.rb +25 -0
  216. data/spec/support/shared/system/platforms/mixins/unix.rb +37 -0
  217. data/spec/support/snapshots/placeholder +0 -0
  218. metadata +566 -21
  219. data/.gitignore +0 -8
  220. data/bin/console +0 -15
  221. 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