dia 1.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{LICENSE → COPYING} +0 -0
- data/README.mkd +67 -0
- data/dia.gemspec +49 -0
- data/lib/dia.rb +11 -8
- data/lib/dia/application.rb +49 -0
- data/lib/dia/exception_struct.rb +7 -0
- data/lib/dia/exceptions.rb +5 -0
- data/lib/dia/functions.rb +8 -0
- data/lib/dia/profiles.rb +2 -6
- data/lib/dia/ruby_block.rb +319 -0
- data/lib/dia/shared_features.rb +91 -0
- data/test/setup.rb +5 -4
- data/test/suite/lib/dia/exception_struct.rb +40 -0
- data/test/suite/lib/dia/ruby_block.rb +561 -0
- data/test/suite/lib/dia/shared_features.rb +236 -0
- metadata +31 -42
- data/.yardopts +0 -4
- data/HACKING.md +0 -13
- data/NEWS.md +0 -61
- data/README.md +0 -170
- data/TODO.md +0 -8
- data/lib/dia/commonapi.rb +0 -7
- data/lib/dia/sandbox.rb +0 -208
- data/test/suite/check_if_sandbox_is_alive_test.rb +0 -29
- data/test/suite/exception()_test.rb +0 -55
- data/test/suite/exception_raised?_test.rb +0 -22
- data/test/suite/exceptions_test.rb +0 -12
- data/test/suite/exit_status_test.rb +0 -16
- data/test/suite/passing_parameters_to_constructer_test.rb +0 -34
- data/test/suite/run_block_in_sandbox_test.rb +0 -132
- data/test/suite/terminate_sandbox_test.rb +0 -29
data/{LICENSE → COPYING}
RENAMED
File without changes
|
data/README.mkd
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Dia
|
2
|
+
Through the use of technology found on Apple's Leopard and Snow Leopard
|
3
|
+
operating systems, Dia can create dynamic and robust sandbox environments
|
4
|
+
for applications and for blocks of ruby code. The Ruby API was designed to be
|
5
|
+
simple, and a joy to use. I hope you feel the same way :-)
|
6
|
+
|
7
|
+
## Quick Example
|
8
|
+
|
9
|
+
* RubyBlock
|
10
|
+
|
11
|
+
require('rubygems')
|
12
|
+
require('dia')
|
13
|
+
require('open-uri')
|
14
|
+
|
15
|
+
sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
16
|
+
open('http://www.google.com')
|
17
|
+
end
|
18
|
+
|
19
|
+
sandbox.rescue_exception = true
|
20
|
+
sandbox.run
|
21
|
+
puts "Exception : #{sandbox.exception.klass}"
|
22
|
+
puts "Message : #{sandbox.exception.message}"
|
23
|
+
|
24
|
+
* Application
|
25
|
+
|
26
|
+
require('rubygems')
|
27
|
+
require('dia')
|
28
|
+
|
29
|
+
sandbox = Dia::Application.new(Dia::Profiles::NO_INTERNET,
|
30
|
+
'/path/to/firefox')
|
31
|
+
|
32
|
+
sandbox.run_nonblock
|
33
|
+
sandbox.terminate
|
34
|
+
|
35
|
+
## Documentation
|
36
|
+
|
37
|
+
* [API Documentation](http://yardoc.org/docs/robgleeson-Dia/)
|
38
|
+
Written using YARD, the API documentation makes a great reference.
|
39
|
+
*The API documentation linked is for the latest stable release*
|
40
|
+
|
41
|
+
* [Mailing list](http://groups.google.com/group/ruby-dia)
|
42
|
+
Troubleshoot your problems with other Dia users on the Google Groups mailing list.
|
43
|
+
|
44
|
+
* Wiki documentation
|
45
|
+
*Work in progress*
|
46
|
+
|
47
|
+
## Supported Rubies.
|
48
|
+
|
49
|
+
The following Ruby implementations have had the test suite run against them, and
|
50
|
+
reported a 100% success rate.
|
51
|
+
|
52
|
+
* MRI
|
53
|
+
* 1.8.7-p299
|
54
|
+
* 1.9.1-p378
|
55
|
+
* 1.9.2-rc1
|
56
|
+
* REE
|
57
|
+
* Ruby Enterprise Edition 2010.02 (1.8.7-p253)
|
58
|
+
|
59
|
+
MacRuby is not supported because it does not support Kernel.fork, and it won't add support
|
60
|
+
for fork anytime soon(if ever).
|
61
|
+
JRuby has experimental support for fork, but I haven't tried it.
|
62
|
+
|
63
|
+
## Bugs
|
64
|
+
Bug reports are _very_ welcome, and can be reported through the
|
65
|
+
[issue tracker](http://github.com/robgleeson/dia/issues).
|
66
|
+
|
67
|
+
|
data/dia.gemspec
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require('./lib/dia')
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
|
6
|
+
s.name = %q{dia}
|
7
|
+
s.version = Dia::VERSION
|
8
|
+
s.authors = ["Robert Gleeson"]
|
9
|
+
s.email = %q{rob@flowof.info}
|
10
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
11
|
+
|
12
|
+
s.description = %q{Through the use of technology found on Apple's Leopard and Snow Leopard
|
13
|
+
operating systems, Dia can create dynamic and robust sandbox environments
|
14
|
+
for applications and for blocks of ruby code.
|
15
|
+
The Ruby API was designed to be simple, and a joy to use.
|
16
|
+
I hope you feel the same way :-)}
|
17
|
+
|
18
|
+
s.summary = %q{Through the use of technology found on Apple's Leopard and Snow Leopard
|
19
|
+
operating systems, Dia can create dynamic and robust sandbox environments
|
20
|
+
for applications and for blocks of ruby code.
|
21
|
+
The Ruby API was designed to be simple, and a joy to use.
|
22
|
+
I hope you feel the same way :-)}
|
23
|
+
|
24
|
+
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
s.files = Dir["lib/**/*.rb"] + Dir["test/**/*.rb"] + %w(COPYING README.mkd dia.gemspec)
|
27
|
+
s.test_files = Dir["test/**/*.rb"]
|
28
|
+
|
29
|
+
|
30
|
+
s.add_runtime_dependency(%q<ffi>, ["= 0.6.2"])
|
31
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
32
|
+
s.has_rdoc = %q{yard}
|
33
|
+
|
34
|
+
s.post_install_message = <<-MESSAGE
|
35
|
+
--------------------------------------------------------------------
|
36
|
+
Dia (#{Dia::VERSION})
|
37
|
+
|
38
|
+
Thanks for installing Dia, #{Dia::VERSION}!
|
39
|
+
|
40
|
+
>=2.0.0 releases include public API changes that are not backward
|
41
|
+
compatiable with older releases. Be sure to check the docs!
|
42
|
+
|
43
|
+
[Github] http://github.com/robgleeson/dia
|
44
|
+
[API Documentation] http://yardoc.org/docs/robgleeson-dia/
|
45
|
+
[Mailing List (new)] http://groups.google.com/group/ruby-dia
|
46
|
+
--------------------------------------------------------------------
|
47
|
+
MESSAGE
|
48
|
+
|
49
|
+
end
|
data/lib/dia.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
gem
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
gem('ffi', '0.6.2')
|
2
|
+
require('ffi')
|
3
|
+
require('ostruct')
|
4
|
+
require(File.expand_path('dia/shared_features' , File.dirname(__FILE__)))
|
5
|
+
require(File.expand_path('dia/functions' , File.dirname(__FILE__)))
|
6
|
+
require(File.expand_path('dia/profiles' , File.dirname(__FILE__)))
|
7
|
+
require(File.expand_path('dia/ruby_block' , File.dirname(__FILE__)))
|
8
|
+
require(File.expand_path('dia/application' , File.dirname(__FILE__)))
|
9
|
+
require(File.expand_path('dia/exceptions' , File.dirname(__FILE__)))
|
10
|
+
require(File.expand_path('dia/exception_struct' , File.dirname(__FILE__)))
|
6
11
|
|
7
12
|
module Dia
|
8
|
-
VERSION = '
|
9
|
-
class SandboxException < StandardError; end
|
13
|
+
VERSION = '2.0.0'
|
10
14
|
end
|
11
|
-
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Dia
|
2
|
+
|
3
|
+
class Application
|
4
|
+
include Dia::SharedFeatures
|
5
|
+
|
6
|
+
|
7
|
+
# @param [String] Profile Accepts one of five profiles found under the {Dia::Profiles}
|
8
|
+
# module.
|
9
|
+
#
|
10
|
+
# @param [String] Application Accepts a path to an application.
|
11
|
+
#
|
12
|
+
# @raise [ArgumentError] It may raise an ArgumentError if it isn't possible to use a
|
13
|
+
# particular profile with this type of sandbox.
|
14
|
+
def initialize(profile, app)
|
15
|
+
@profile = profile
|
16
|
+
@app = app
|
17
|
+
raise(ArgumentError, "Dia::Profiles::NO_OS_SERVICES is not applicable to the Application " \
|
18
|
+
"sandbox.") if Dia::Profiles::NO_OS_SERVICES == @profile
|
19
|
+
end
|
20
|
+
|
21
|
+
# This method will spawn a child process, initialize a sandbox environment, and call exec()
|
22
|
+
# to start your application.
|
23
|
+
#
|
24
|
+
# @return [Fixnum] Returns the Process ID(PID) of the child process used to execute an
|
25
|
+
# application in a sandbox.
|
26
|
+
def run
|
27
|
+
@pid = fork do
|
28
|
+
initialize_sandbox
|
29
|
+
exec(@app)
|
30
|
+
end
|
31
|
+
|
32
|
+
_, @exit_status = Process.wait2(@pid)
|
33
|
+
@pid
|
34
|
+
end
|
35
|
+
|
36
|
+
# An identical but non-blocking form of {#run}.
|
37
|
+
def run_nonblock
|
38
|
+
@pid = fork do
|
39
|
+
initialize_sandbox
|
40
|
+
exec(@app)
|
41
|
+
end
|
42
|
+
|
43
|
+
@exit_status = Process.detach(@pid)
|
44
|
+
@pid
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module Dia
|
2
|
+
# @attr_reader [String] klass Returns Exception#class as a String.
|
3
|
+
# @attr_reader [String] message Returns Exception#message as a String.
|
4
|
+
# @attr_reader [String] backtrace Returns Exception#backtrace as a String.
|
5
|
+
class ExceptionStruct < Struct.new(:klass, :message, :backtrace)
|
6
|
+
end
|
7
|
+
end
|
data/lib/dia/profiles.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module Dia
|
2
|
-
|
3
2
|
module Profiles
|
4
|
-
extend
|
5
|
-
ffi_lib(%w(
|
3
|
+
extend(FFI::Library)
|
4
|
+
ffi_lib(%w(system))
|
6
5
|
|
7
6
|
NO_INTERNET = attach_variable(:kSBXProfileNoInternet,
|
8
7
|
:string).read_string
|
@@ -14,8 +13,5 @@ module Dia
|
|
14
13
|
:string).read_string
|
15
14
|
NO_OS_SERVICES = attach_variable(:kSBXProfilePureComputation,
|
16
15
|
:string).read_string
|
17
|
-
|
18
16
|
end
|
19
|
-
|
20
17
|
end
|
21
|
-
|
@@ -0,0 +1,319 @@
|
|
1
|
+
module Dia
|
2
|
+
|
3
|
+
class RubyBlock
|
4
|
+
|
5
|
+
require('io/wait')
|
6
|
+
require('stringio')
|
7
|
+
include Dia::SharedFeatures
|
8
|
+
|
9
|
+
|
10
|
+
# @param [String] Profile Accepts one of five profiles which can be found
|
11
|
+
# under the {Dia::Profiles} module.
|
12
|
+
#
|
13
|
+
# @param [Proc] Block Accepts a block or Proc object as its second argument.
|
14
|
+
#
|
15
|
+
# @raise [ArgumentError] It will raise an ArgumentError if a profile and block
|
16
|
+
# isn't supplied to the constructor
|
17
|
+
#
|
18
|
+
# @return [Dia::RubyBlock] Returns an instance of Dia::RubyBlock.
|
19
|
+
def initialize(profile, &block)
|
20
|
+
raise(ArgumentError, "It is required that a block be passed to the constructor.\n" \
|
21
|
+
"Please consult the documentation.") unless block_given?
|
22
|
+
@profile = profile
|
23
|
+
@proc = block
|
24
|
+
@rescue = false
|
25
|
+
@redirect_stdout = false
|
26
|
+
@redirect_stderr = false
|
27
|
+
@pipes = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# When the "capture stdout" feature is enabled, this method will return the contents
|
32
|
+
# of the standard output stream for the child process used to execute your sandbox.
|
33
|
+
#
|
34
|
+
# @return [String, nil] Returns the contents of stdout.
|
35
|
+
# Returns nil when no data is available on stdout, or when the
|
36
|
+
# "capture stdout" feature is disabled.
|
37
|
+
#
|
38
|
+
# @see #redirect_stdout= This feature is disabled by default. See how to enable it.
|
39
|
+
#
|
40
|
+
def stdout
|
41
|
+
if pipes_readable?(@pipes[:stdout_reader], @pipes[:stdout_writer])
|
42
|
+
@pipes[:stdout_writer].close
|
43
|
+
@stdout = @pipes[:stdout_reader].read
|
44
|
+
@pipes[:stdout_reader].close
|
45
|
+
end
|
46
|
+
@stdout
|
47
|
+
end
|
48
|
+
|
49
|
+
# This method can enable or disable a feature that will capture standard output
|
50
|
+
# in the child process that is spawned to execute a sandbox.
|
51
|
+
#
|
52
|
+
# @param [Boolean] Boolean Accepts a true(-ish) or false(-ish) value.
|
53
|
+
# @return [Boolean] Returns the calling argument.
|
54
|
+
# @see #stdout See #stdout for accessing the contents of stdout.
|
55
|
+
def redirect_stdout=(boolean)
|
56
|
+
@redirect_stdout = boolean
|
57
|
+
end
|
58
|
+
|
59
|
+
# This method will tell you if standatd output is being redirected in the child
|
60
|
+
# process used to execute your sandbox.
|
61
|
+
#
|
62
|
+
# @see #redirect_stdout= See how to enable the "redirect stdout" feature.
|
63
|
+
#
|
64
|
+
# @return [Boolean] Returns true or false.
|
65
|
+
def redirect_stdout?
|
66
|
+
!!@rescue_stdout
|
67
|
+
end
|
68
|
+
|
69
|
+
# This method will tell you if standard error is being redirected in the child process
|
70
|
+
# used to execute your sandbox.
|
71
|
+
#
|
72
|
+
# @see #redirect_stderr= See how to enable the "redirect stderr" feature.
|
73
|
+
# @return [Boolean] returns true or false.
|
74
|
+
def redirect_stderr?
|
75
|
+
!!@rescue_stderr
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method can enable or disable a feature that will capture standard error output
|
79
|
+
# in the child process that is spawned to execute a sandbox.
|
80
|
+
#
|
81
|
+
# @param [Boolean] Boolean Accepts a true(-ish) or false(-ish) value.
|
82
|
+
# @return [Boolean] Returns the calling argument.
|
83
|
+
# @see #stdout See #stderr for accessing the contents of stderr.
|
84
|
+
def redirect_stderr=(boolean)
|
85
|
+
@redirect_stderr = boolean
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# When the "capture stderr" feature is enabled, this method will return the contents
|
90
|
+
# of the standard error stream for the child process used to execute your sandbox.
|
91
|
+
#
|
92
|
+
# @return [String, nil] Returns the contents of stderr.
|
93
|
+
# Returns nil when no data is available on stderr, or when the
|
94
|
+
# "capture stderr" feature is disabled.
|
95
|
+
#
|
96
|
+
# @see #redirect_stderr= This feature is disabled by default. See how to enable it.
|
97
|
+
#
|
98
|
+
def stderr
|
99
|
+
if pipes_readable?(@pipes[:stderr_reader], @pipes[:stderr_writer])
|
100
|
+
@pipes[:stderr_writer].close
|
101
|
+
@stderr = @pipes[:stderr_reader].read
|
102
|
+
@pipes[:stderr_reader].close
|
103
|
+
end
|
104
|
+
@stderr
|
105
|
+
end
|
106
|
+
|
107
|
+
# This method will tell you if an exception has been raised in the child process
|
108
|
+
# used to execute your sandbox.
|
109
|
+
# The "capture exception" feature must be enabled for this method to ever
|
110
|
+
# return true.
|
111
|
+
#
|
112
|
+
# @see #rescue_exception= See #rescue_exception= for enabling the capture
|
113
|
+
# of raised exceptions in your sandbox.
|
114
|
+
#
|
115
|
+
# @see #exception See the #exception method for accessing an
|
116
|
+
# exception raised in your sandbox.
|
117
|
+
#
|
118
|
+
# @return [Boolean] Returns true or false.
|
119
|
+
def exception_raised?
|
120
|
+
!!exception
|
121
|
+
end
|
122
|
+
|
123
|
+
# This method will tell you if the {#rescue_exception=} feature is enabled by
|
124
|
+
# returning a boolean.
|
125
|
+
#
|
126
|
+
# @see #rescue_exception= See #rescue_exception= for enabling the capture
|
127
|
+
# of raised exceptions in your sandbox.
|
128
|
+
#
|
129
|
+
# @see #exception See the #exception method for accessing an
|
130
|
+
# exception raised in your sandbox.
|
131
|
+
#
|
132
|
+
# @return [Boolean] Returns true or false.
|
133
|
+
# @since 2.0.0
|
134
|
+
def rescue_exception?
|
135
|
+
!!@rescue
|
136
|
+
end
|
137
|
+
|
138
|
+
# This method can enable or disable a feature that will try to capture
|
139
|
+
# raised exceptions in the child process that is spawned to execute a sandbox.
|
140
|
+
#
|
141
|
+
# @param [Boolean] Boolean Accepts a true(-ish) or false(-ish) value.
|
142
|
+
#
|
143
|
+
# @return [Boolean] Returns the calling argument.
|
144
|
+
#
|
145
|
+
# @see #exception See #exception for information on how to access
|
146
|
+
# the data of an exception raised in your sandbox.
|
147
|
+
#
|
148
|
+
# @since 2.0.0
|
149
|
+
def rescue_exception=(boolean)
|
150
|
+
@rescue = boolean
|
151
|
+
end
|
152
|
+
|
153
|
+
# When the "capture exceptions" feature is enabled and an exception has been raised in
|
154
|
+
# the child process used to execute your sandbox, this method will return a subclass
|
155
|
+
# of Struct whose attributes represent the exception data.
|
156
|
+
#
|
157
|
+
# Every call {#run} or {#run_nonblock} will reset the instance variable referencing the
|
158
|
+
# object storing exception data to nil.
|
159
|
+
#
|
160
|
+
# @return [Dia::ExceptionStruct, nil] Returns an instance of {Dia::ExceptionStruct} or nil
|
161
|
+
# when there is no exception available.
|
162
|
+
#
|
163
|
+
# @see #rescue_exception= The "capture exception" feature is disabled by default.
|
164
|
+
# See how to enable it.
|
165
|
+
#
|
166
|
+
# @see Dia::ExceptionStruct The documentation for Dia::ExceptionStruct.
|
167
|
+
#
|
168
|
+
# @since 1.5
|
169
|
+
def exception
|
170
|
+
if pipes_readable?(@pipes[:exception_reader], @pipes[:exception_writer])
|
171
|
+
@pipes[:exception_writer].close
|
172
|
+
@e = ExceptionStruct.new *Marshal.load(@pipes[:exception_reader].read).values_at(:klass,
|
173
|
+
:message,
|
174
|
+
:backtrace)
|
175
|
+
@pipes[:exception_reader].close
|
176
|
+
end
|
177
|
+
@e
|
178
|
+
end
|
179
|
+
|
180
|
+
# The run method will spawn a child process to execute the block supplied to the constructer
|
181
|
+
# in a sandbox.
|
182
|
+
# This method will block. See {#run_nonblock} for the non-blocking form of
|
183
|
+
# this method.
|
184
|
+
#
|
185
|
+
# @param [Arguments] Arguments A variable amount of arguments that will
|
186
|
+
# be passed onto the block supplied to the
|
187
|
+
# constructer. Optional.
|
188
|
+
#
|
189
|
+
# @raise [SystemCallError] It may raise a number of subclasses of SystemCallError
|
190
|
+
# in a child process if a sandbox violates imposed
|
191
|
+
# restrictions.
|
192
|
+
#
|
193
|
+
# @raise [Dia::SandboxException] It may raise
|
194
|
+
# {Dia::Exceptions::SandboxException}
|
195
|
+
# in a child process if it was not possible
|
196
|
+
# to initialize a sandbox environment.
|
197
|
+
#
|
198
|
+
# @return [Fixnum] The Process ID(PID) that the sandbox has
|
199
|
+
# been launched under.
|
200
|
+
def run(*args)
|
201
|
+
launch(*args)
|
202
|
+
|
203
|
+
# parent ..
|
204
|
+
_, @exit_status = Process.wait2(@pid)
|
205
|
+
@pid
|
206
|
+
end
|
207
|
+
|
208
|
+
# An identical, but non-blocking form of {#run}.
|
209
|
+
def run_nonblock(*args)
|
210
|
+
launch(*args)
|
211
|
+
|
212
|
+
@exit_status = Process.detach(@pid)
|
213
|
+
@pid
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
# @api private
|
218
|
+
def launch(*args)
|
219
|
+
@e = @stdout = nil
|
220
|
+
close_pipes_if_needed
|
221
|
+
open_pipes_if_needed
|
222
|
+
|
223
|
+
@pid = fork do
|
224
|
+
redirect(:stdout) if @redirect_stdout
|
225
|
+
redirect(:stderr) if @redirect_stderr
|
226
|
+
if @rescue
|
227
|
+
begin
|
228
|
+
initialize_sandbox
|
229
|
+
@proc.call(*args)
|
230
|
+
rescue SystemExit, SignalException, NoMemoryError => e
|
231
|
+
raise(e)
|
232
|
+
rescue Exception => e
|
233
|
+
begin
|
234
|
+
write_exception(e)
|
235
|
+
rescue SystemExit, SignalException, NoMemoryError => e
|
236
|
+
raise(e)
|
237
|
+
rescue Exception => e
|
238
|
+
write_exception(e)
|
239
|
+
end
|
240
|
+
ensure
|
241
|
+
write_stdout_and_stderr_if_needed
|
242
|
+
close_pipes_if_needed
|
243
|
+
end
|
244
|
+
else
|
245
|
+
begin
|
246
|
+
initialize_sandbox
|
247
|
+
@proc.call(*args)
|
248
|
+
ensure
|
249
|
+
write_stdout_and_stderr_if_needed
|
250
|
+
close_pipes_if_needed
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# @api private
|
257
|
+
def write_stdout_and_stderr_if_needed
|
258
|
+
if @redirect_stdout
|
259
|
+
$stdout.rewind
|
260
|
+
@pipes[:stdout_reader].close
|
261
|
+
@pipes[:stdout_writer].write($stdout.read)
|
262
|
+
@pipes[:stdout_writer].close
|
263
|
+
end
|
264
|
+
|
265
|
+
if @redirect_stderr
|
266
|
+
$stderr.rewind
|
267
|
+
@pipes[:stderr_reader].close
|
268
|
+
@pipes[:stderr_writer].write($stderr.read)
|
269
|
+
@pipes[:stderr_writer].close
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# @api private
|
274
|
+
def close_pipes_if_needed
|
275
|
+
@pipes.each do |key, pipe|
|
276
|
+
if !pipe.nil? && !pipe.closed?
|
277
|
+
pipe.close
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# @api private
|
283
|
+
def open_pipes_if_needed
|
284
|
+
@pipes[:exception_reader], @pipes[:exception_writer] = IO.pipe if @rescue
|
285
|
+
@pipes[:stdout_reader] , @pipes[:stdout_writer] = IO.pipe if @redirect_stdout
|
286
|
+
@pipes[:stderr_reader] , @pipes[:stderr_writer] = IO.pipe if @redirect_stderr
|
287
|
+
end
|
288
|
+
|
289
|
+
# @api private
|
290
|
+
def write_exception(e)
|
291
|
+
@pipes[:exception_writer].write(Marshal.dump({ :klass => e.class.to_s ,
|
292
|
+
:backtrace => e.backtrace.join("\n"),
|
293
|
+
:message => e.message.to_s }) )
|
294
|
+
end
|
295
|
+
|
296
|
+
# @api private
|
297
|
+
def pipes_readable?(reader, writer)
|
298
|
+
(reader && writer) &&
|
299
|
+
(!reader.closed? && !writer.closed?) &&
|
300
|
+
(reader.ready?)
|
301
|
+
end
|
302
|
+
|
303
|
+
# @api private
|
304
|
+
def redirect(symbol)
|
305
|
+
level = $VERBOSE
|
306
|
+
$VERBOSE = nil
|
307
|
+
if symbol == :stdout
|
308
|
+
$stdout = StringIO.new
|
309
|
+
Object.const_set(:STDOUT, $stdout)
|
310
|
+
else
|
311
|
+
$stderr = StringIO.new
|
312
|
+
Object.const_set(:STDERR, $stderr)
|
313
|
+
end
|
314
|
+
$VERBOSE = level
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|