rexec 1.4.1 → 1.5.0
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.
- data/Gemfile +4 -0
- data/lib/rexec/daemon/base.rb +40 -47
- data/lib/rexec/daemon/controller.rb +23 -25
- data/lib/rexec/daemon/{pidfile.rb → process_file.rb} +7 -7
- data/lib/rexec/version.rb +2 -2
- data/rakefile.rb +10 -0
- data/test/client.rb +14 -14
- data/test/daemon.rb +32 -33
- data/test/helper.rb +5 -0
- data/test/listing_example.rb +15 -14
- data/test/{daemon_test.rb → test_daemon.rb} +20 -22
- data/test/{remote_server_test.rb → test_remote_server.rb} +1 -2
- data/test/test_server.rb +93 -0
- data/test/test_task.rb +168 -0
- metadata +30 -49
- data/test/server_test.rb +0 -94
- data/test/task_test.rb +0 -170
data/Gemfile
ADDED
data/lib/rexec/daemon/base.rb
CHANGED
@@ -35,84 +35,79 @@ module RExec
|
|
35
35
|
# end
|
36
36
|
#
|
37
37
|
# Server.daemonize
|
38
|
+
#
|
39
|
+
# The base directory specifies a path such that:
|
40
|
+
# working_directory = #{@@base_directory}/#{daemon_name}
|
41
|
+
# log_directory = #{working_directory}/log
|
42
|
+
# log_file_path = #{log_directory}/daemon.log
|
43
|
+
# runtime_directory = #{working_directory}/run
|
44
|
+
# process_file_path = #{runtime_directory}/daemon.pid
|
38
45
|
class Base
|
39
|
-
|
40
|
-
@@
|
41
|
-
@@pid_directory = nil
|
46
|
+
# For a system-level daemon you might want to specify "/var"
|
47
|
+
@@base_directory = "."
|
42
48
|
|
43
49
|
# Return the name of the daemon
|
44
50
|
def self.daemon_name
|
45
51
|
return name.gsub(/[^a-zA-Z0-9]+/, '-')
|
46
52
|
end
|
47
53
|
|
48
|
-
#
|
49
|
-
def self.var_directory
|
50
|
-
@@var_directory || File.join("", "var")
|
51
|
-
end
|
52
|
-
|
53
|
-
# The directory the daemon will run in (Dir.chdir)
|
54
|
+
# The directory the daemon will run in.
|
54
55
|
def self.working_directory
|
55
|
-
|
56
|
+
File.join(@@base_directory, daemon_name)
|
56
57
|
end
|
57
58
|
|
58
|
-
# Return the directory to store log files in
|
59
|
+
# Return the directory to store log files in.
|
59
60
|
def self.log_directory
|
60
|
-
|
61
|
+
File.join(working_directory, "log")
|
61
62
|
end
|
62
63
|
|
63
|
-
# Standard log file for
|
64
|
-
def self.
|
65
|
-
File.join(log_directory, "
|
64
|
+
# Standard log file for stdout and stderr.
|
65
|
+
def self.log_file_path
|
66
|
+
File.join(log_directory, "daemon.log")
|
66
67
|
end
|
67
68
|
|
68
|
-
#
|
69
|
-
def self.
|
70
|
-
File.join(
|
69
|
+
# Runtime data directory for the daemon.
|
70
|
+
def self.runtime_directory
|
71
|
+
File.join(working_directory, "run")
|
71
72
|
end
|
72
73
|
|
73
|
-
# Standard location of pid file
|
74
|
-
def self.
|
75
|
-
|
74
|
+
# Standard location of process pid file.
|
75
|
+
def self.process_file_path
|
76
|
+
File.join(runtime_directory, "daemon.pid")
|
76
77
|
end
|
77
78
|
|
78
|
-
#
|
79
|
-
def self.
|
80
|
-
File.
|
81
|
-
|
82
|
-
|
83
|
-
# Mark the error log
|
84
|
-
def self.mark_err_log
|
85
|
-
fp = File.open(err_fn, "a")
|
86
|
-
fp.puts "=== Error Log Opened @ #{Time.now.to_s} ==="
|
87
|
-
fp.close
|
79
|
+
# Mark the output log.
|
80
|
+
def self.mark_log
|
81
|
+
File.open(log_file_path, "a") do |log_file|
|
82
|
+
log_file.puts "=== Log Marked @ #{Time.now.to_s} ==="
|
83
|
+
end
|
88
84
|
end
|
89
85
|
|
90
|
-
# Prints some information relating to daemon startup problems
|
91
|
-
def self.
|
86
|
+
# Prints some information relating to daemon startup problems.
|
87
|
+
def self.tail_log(output)
|
92
88
|
lines = []
|
93
89
|
|
94
|
-
File.open(
|
95
|
-
|
90
|
+
File.open(error_log_path, "r") do |log_file|
|
91
|
+
log_file.seek_end
|
96
92
|
|
97
|
-
|
93
|
+
log_file.reverse_each_line do |line|
|
98
94
|
lines << line
|
99
|
-
break if line.match("===
|
95
|
+
break if line.match("=== Log Marked") || line.match("=== Daemon Exception Backtrace")
|
100
96
|
end
|
101
97
|
end
|
102
98
|
|
103
99
|
lines.reverse_each do |line|
|
104
|
-
|
100
|
+
output.puts line
|
105
101
|
end
|
106
102
|
end
|
107
103
|
|
108
|
-
# Check the last few lines of the log file to find out if
|
109
|
-
# the daemon crashed.
|
104
|
+
# Check the last few lines of the log file to find out if the daemon crashed.
|
110
105
|
def self.crashed?
|
111
|
-
File.open(
|
112
|
-
|
106
|
+
File.open(error_log_path, "r") do |log_file|
|
107
|
+
log_file.seek_end
|
113
108
|
|
114
109
|
count = 2
|
115
|
-
|
110
|
+
log_file.reverse_each_line do |line|
|
116
111
|
return true if line.match("=== Daemon Crashed")
|
117
112
|
|
118
113
|
count -= 1
|
@@ -146,12 +141,10 @@ module RExec
|
|
146
141
|
|
147
142
|
# The main function to setup any environment required by the daemon
|
148
143
|
def self.prefork
|
149
|
-
@@
|
150
|
-
@@log_directory = File.expand_path(@@log_directory) if @@log_directory
|
151
|
-
@@pid_directory = File.expand_path(@@pid_directory) if @@pid_directory
|
144
|
+
@@base_directory = File.expand_path(@@base_directory) if @@base_directory
|
152
145
|
|
153
146
|
FileUtils.mkdir_p(log_directory)
|
154
|
-
FileUtils.mkdir_p(
|
147
|
+
FileUtils.mkdir_p(runtime_directory)
|
155
148
|
end
|
156
149
|
|
157
150
|
# The main function to start the daemon
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require 'rexec/daemon/
|
21
|
+
require 'rexec/daemon/process_file'
|
22
22
|
require 'rexec/task'
|
23
23
|
|
24
24
|
module RExec
|
@@ -28,21 +28,19 @@ module RExec
|
|
28
28
|
|
29
29
|
# This module contains functionality related to starting and stopping the daemon, and code for processing command line input.
|
30
30
|
module Controller
|
31
|
-
# This function is called from the daemon executable. It processes ARGV and checks whether the user is asking for
|
32
|
-
# +start+, +stop+, +restart+ or +status+.
|
31
|
+
# This function is called from the daemon executable. It processes ARGV and checks whether the user is asking for `start`, `stop`, `restart`, `status`.
|
33
32
|
def self.daemonize(daemon)
|
34
|
-
|
35
|
-
case !ARGV.empty? && ARGV[0]
|
33
|
+
case ARGV.shift
|
36
34
|
when 'start'
|
37
35
|
start(daemon)
|
38
36
|
status(daemon)
|
39
37
|
when 'stop'
|
40
38
|
stop(daemon)
|
41
39
|
status(daemon)
|
42
|
-
|
40
|
+
ProcessFile.cleanup(daemon)
|
43
41
|
when 'restart'
|
44
42
|
stop(daemon)
|
45
|
-
|
43
|
+
ProcessFile.cleanup(daemon)
|
46
44
|
start(daemon)
|
47
45
|
status(daemon)
|
48
46
|
when 'status'
|
@@ -57,7 +55,7 @@ module RExec
|
|
57
55
|
def self.start(daemon)
|
58
56
|
puts "Starting daemon..."
|
59
57
|
|
60
|
-
case
|
58
|
+
case ProcessFile.status(daemon)
|
61
59
|
when :running
|
62
60
|
$stderr.puts "Daemon already running!"
|
63
61
|
return
|
@@ -66,26 +64,26 @@ module RExec
|
|
66
64
|
else
|
67
65
|
$stderr.puts "Daemon in unknown state! Will clear previous state and continue."
|
68
66
|
status(daemon)
|
69
|
-
|
67
|
+
ProcessFile.clear(daemon)
|
70
68
|
end
|
71
69
|
|
72
70
|
daemon.prefork
|
73
|
-
daemon.
|
71
|
+
daemon.mark_log
|
74
72
|
|
75
73
|
fork do
|
76
74
|
Process.setsid
|
77
75
|
exit if fork
|
78
76
|
|
79
|
-
|
77
|
+
ProcessFile.store(daemon, Process.pid)
|
80
78
|
|
81
79
|
File.umask 0000
|
82
80
|
Dir.chdir daemon.working_directory
|
83
81
|
|
84
82
|
$stdin.reopen "/dev/null"
|
85
|
-
$stdout.reopen daemon.
|
83
|
+
$stdout.reopen daemon.log_file_path, "a"
|
86
84
|
$stdout.sync = true
|
87
85
|
|
88
|
-
$stderr.reopen
|
86
|
+
$stderr.reopen $stdout
|
89
87
|
$stderr.sync = true
|
90
88
|
|
91
89
|
begin
|
@@ -130,7 +128,7 @@ module RExec
|
|
130
128
|
puts "Waiting for daemon to start..."
|
131
129
|
sleep 0.1
|
132
130
|
timer = TIMEOUT
|
133
|
-
pid =
|
131
|
+
pid = ProcessFile.recall(daemon)
|
134
132
|
|
135
133
|
while pid == nil and timer > 0
|
136
134
|
# Wait a moment for the forking to finish...
|
@@ -140,7 +138,7 @@ module RExec
|
|
140
138
|
# If the daemon has crashed, it is never going to start...
|
141
139
|
break if daemon.crashed?
|
142
140
|
|
143
|
-
pid =
|
141
|
+
pid = ProcessFile.recall(daemon)
|
144
142
|
|
145
143
|
timer -= 1
|
146
144
|
end
|
@@ -148,15 +146,15 @@ module RExec
|
|
148
146
|
|
149
147
|
# Prints out the status of the daemon
|
150
148
|
def self.status(daemon)
|
151
|
-
case
|
149
|
+
case ProcessFile.status(daemon)
|
152
150
|
when :running
|
153
|
-
puts "Daemon status: running pid=#{
|
151
|
+
puts "Daemon status: running pid=#{ProcessFile.recall(daemon)}"
|
154
152
|
when :unknown
|
155
153
|
if daemon.crashed?
|
156
154
|
puts "Daemon status: crashed"
|
157
155
|
|
158
156
|
$stdout.flush
|
159
|
-
daemon.
|
157
|
+
daemon.tail_error_log($stderr)
|
160
158
|
else
|
161
159
|
puts "Daemon status: unknown"
|
162
160
|
end
|
@@ -170,27 +168,27 @@ module RExec
|
|
170
168
|
puts "Stopping daemon..."
|
171
169
|
|
172
170
|
# Check if the pid file exists...
|
173
|
-
|
171
|
+
unless File.file?(daemon.process_file_path)
|
174
172
|
puts "Pid file not found. Is the daemon running?"
|
175
173
|
return
|
176
174
|
end
|
177
175
|
|
178
|
-
pid =
|
176
|
+
pid = ProcessFile.recall(daemon)
|
179
177
|
|
180
178
|
# Check if the daemon is already stopped...
|
181
|
-
unless
|
179
|
+
unless ProcessFile.running(daemon)
|
182
180
|
puts "Pid #{pid} is not running. Has daemon crashed?"
|
183
181
|
return
|
184
182
|
end
|
185
183
|
|
186
|
-
pid =
|
184
|
+
pid = ProcessFile.recall(daemon)
|
187
185
|
Process.kill("INT", pid)
|
188
186
|
sleep 0.1
|
189
187
|
|
190
188
|
# Kill/Term loop - if the daemon didn't die easily, shoot
|
191
189
|
# it a few more times.
|
192
190
|
attempts = 5
|
193
|
-
while
|
191
|
+
while ProcessFile.running(daemon) and attempts > 0
|
194
192
|
sig = (attempts < 2) ? "KILL" : "TERM"
|
195
193
|
|
196
194
|
puts "Sending #{sig} to pid #{pid}..."
|
@@ -201,13 +199,13 @@ module RExec
|
|
201
199
|
end
|
202
200
|
|
203
201
|
# If after doing our best the daemon is still running (pretty odd)...
|
204
|
-
if
|
202
|
+
if ProcessFile.running(daemon)
|
205
203
|
puts "Daemon appears to be still running!"
|
206
204
|
return
|
207
205
|
end
|
208
206
|
|
209
207
|
# Otherwise the daemon has been stopped.
|
210
|
-
|
208
|
+
ProcessFile.clear(daemon)
|
211
209
|
end
|
212
210
|
end
|
213
211
|
end
|
@@ -21,21 +21,21 @@
|
|
21
21
|
module RExec
|
22
22
|
module Daemon
|
23
23
|
# This module controls the storage and retrieval of process id files.
|
24
|
-
module
|
24
|
+
module ProcessFile
|
25
25
|
# Saves the pid for the given daemon
|
26
26
|
def self.store(daemon, pid)
|
27
|
-
File.open(daemon.
|
27
|
+
File.open(daemon.process_file_path, 'w') {|f| f << pid}
|
28
28
|
end
|
29
29
|
|
30
30
|
# Retrieves the pid for the given daemon
|
31
31
|
def self.recall(daemon)
|
32
|
-
IO.read(daemon.
|
32
|
+
IO.read(daemon.process_file_path).to_i rescue nil
|
33
33
|
end
|
34
34
|
|
35
35
|
# Removes the pid saved for a particular daemon
|
36
36
|
def self.clear(daemon)
|
37
|
-
if File.exist? daemon.
|
38
|
-
FileUtils.rm(daemon.
|
37
|
+
if File.exist? daemon.process_file_path
|
38
|
+
FileUtils.rm(daemon.process_file_path)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -58,8 +58,8 @@ module RExec
|
|
58
58
|
# This function returns the status of the daemon. This can be one of +:running+, +:unknown+ (pid file exists but no
|
59
59
|
# corresponding process can be found) or +:stopped+.
|
60
60
|
def self.status(daemon)
|
61
|
-
if File.exist? daemon.
|
62
|
-
return
|
61
|
+
if File.exist? daemon.process_file_path
|
62
|
+
return ProcessFile.running(daemon) ? :running : :unknown
|
63
63
|
else
|
64
64
|
return :stopped
|
65
65
|
end
|
data/lib/rexec/version.rb
CHANGED
data/rakefile.rb
ADDED
data/test/client.rb
CHANGED
@@ -19,18 +19,18 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
$connection.run do |object|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
22
|
+
case(object[0])
|
23
|
+
when :bounce
|
24
|
+
$stderr.puts("Bouncing #{object[1].inspect}...")
|
25
|
+
$connection.send_object(object[1])
|
26
|
+
when :exception
|
27
|
+
$stderr.puts("Raising exception...")
|
28
|
+
raise Exception.new("I love exceptions!")
|
29
|
+
when :stop
|
30
|
+
$stderr.puts("Stopping connection manually...")
|
31
|
+
$connection.stop
|
32
|
+
when :stderr
|
33
|
+
$stderr.puts object[1]
|
34
|
+
$stderr.flush
|
35
|
+
end
|
36
36
|
end
|
data/test/daemon.rb
CHANGED
@@ -20,8 +20,9 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
|
23
|
+
$LOAD_PATH.unshift File.expand_path("../../lib/", __FILE__)
|
24
24
|
|
25
|
+
require 'rubygems'
|
25
26
|
require 'pathname'
|
26
27
|
|
27
28
|
require 'rexec'
|
@@ -33,41 +34,39 @@ require 'xmlrpc/server'
|
|
33
34
|
|
34
35
|
# Very simple XMLRPC daemon
|
35
36
|
class TestDaemon < RExec::Daemon::Base
|
36
|
-
|
37
|
+
@@var_directory = "/tmp/ruby-test/var"
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@@rpc_server = WEBrick::HTTPServer.new(
|
42
|
-
:Port => 11235,
|
43
|
-
:BindAddress => "0.0.0.0",
|
44
|
-
:SSLEnable => true,
|
45
|
-
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
46
|
-
:SSLCertName => [["CN", WEBrick::Utils::getservername]])
|
47
|
-
|
48
|
-
@@listener = XMLRPC::WEBrickServlet.new
|
49
|
-
|
50
|
-
@@listener.add_handler("add") do |amount|
|
51
|
-
@@count ||= 0
|
52
|
-
@@count += amount
|
53
|
-
end
|
54
|
-
|
55
|
-
@@listener.add_handler("total") do
|
56
|
-
@@count
|
57
|
-
end
|
58
|
-
|
59
|
-
@@rpc_server.mount("/RPC2", @@listener)
|
60
|
-
|
61
|
-
$stdout.flush
|
62
|
-
$stderr.flush
|
39
|
+
def self.run
|
40
|
+
puts "Starting server..."
|
63
41
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
42
|
+
@@rpc_server = WEBrick::HTTPServer.new(
|
43
|
+
:Port => 31337,
|
44
|
+
:BindAddress => "0.0.0.0"
|
45
|
+
)
|
46
|
+
|
47
|
+
@@listener = XMLRPC::WEBrickServlet.new
|
48
|
+
|
49
|
+
@@listener.add_handler("add") do |amount|
|
50
|
+
@@count ||= 0
|
51
|
+
@@count += amount
|
52
|
+
end
|
53
|
+
|
54
|
+
@@listener.add_handler("total") do
|
55
|
+
@@count
|
56
|
+
end
|
57
|
+
|
58
|
+
@@rpc_server.mount("/RPC2", @@listener)
|
59
|
+
|
60
|
+
$stdout.flush
|
61
|
+
$stderr.flush
|
62
|
+
|
63
|
+
@@rpc_server.start
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.shutdown
|
68
67
|
puts "Shutting down server..."
|
69
|
-
|
70
|
-
|
68
|
+
@@rpc_server.shutdown
|
69
|
+
end
|
71
70
|
end
|
72
71
|
|
73
72
|
TestDaemon.daemonize
|
data/test/helper.rb
ADDED
data/test/listing_example.rb
CHANGED
@@ -20,19 +20,20 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
require '
|
23
|
+
require 'helper'
|
24
|
+
|
24
25
|
require 'rexec'
|
25
26
|
|
26
27
|
CLIENT = <<EOF
|
27
28
|
|
28
29
|
$connection.run do |path|
|
29
|
-
|
30
|
+
listing = []
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
IO.popen("ls -la " + path.dump, "r+") do |ls|
|
33
|
+
listing = ls.readlines
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
+
$connection.send_object(listing)
|
36
37
|
end
|
37
38
|
|
38
39
|
EOF
|
@@ -41,14 +42,14 @@ command = ARGV[0] || "ruby"
|
|
41
42
|
|
42
43
|
puts "Starting server..."
|
43
44
|
RExec::start_server(CLIENT, command) do |conn, pid|
|
44
|
-
|
45
|
-
|
45
|
+
puts "Sending path..."
|
46
|
+
conn.send_object("/")
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
puts "Waiting for response..."
|
49
|
+
listing = conn.receive_object
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
puts "Received listing:"
|
52
|
+
listing.each do |entry|
|
53
|
+
puts "\t#{entry}"
|
54
|
+
end
|
54
55
|
end
|
@@ -20,30 +20,28 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
require '
|
23
|
+
require 'helper'
|
24
|
+
|
24
25
|
require 'pathname'
|
25
26
|
require 'xmlrpc/client'
|
26
|
-
require 'test/unit'
|
27
27
|
|
28
28
|
class DaemonTest < Test::Unit::TestCase
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
assert_equal 10, total
|
48
|
-
end
|
29
|
+
DAEMON = (Pathname.new(__FILE__).dirname + "./daemon.rb").realpath
|
30
|
+
|
31
|
+
def setup
|
32
|
+
system(DAEMON.to_s, "start")
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
system(DAEMON.to_s, "stop")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_connection
|
40
|
+
rpc = XMLRPC::Client.new_from_uri("http://localhost:31337")
|
41
|
+
rpc.call("add", 10)
|
42
|
+
|
43
|
+
total = rpc.call("total")
|
44
|
+
|
45
|
+
assert_equal 10, total
|
46
|
+
end
|
49
47
|
end
|
data/test/test_server.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'helper'
|
24
|
+
|
25
|
+
require 'fileutils'
|
26
|
+
require 'pathname'
|
27
|
+
require 'rexec'
|
28
|
+
|
29
|
+
class ServerTest < Test::Unit::TestCase
|
30
|
+
def test_local_execution
|
31
|
+
code = Pathname.new(__FILE__).dirname + "./client.rb"
|
32
|
+
sobj = [1, 2, "three", 4]
|
33
|
+
stderr_text = "There was no error.. maybe?"
|
34
|
+
connection_started = false
|
35
|
+
object_received = false
|
36
|
+
|
37
|
+
RExec::start_server(code.read, "ruby", :passthrough => []) do |conn, task|
|
38
|
+
connection_started = true
|
39
|
+
conn.send_object([:bounce, sobj])
|
40
|
+
|
41
|
+
assert_equal sobj, conn.receive_object
|
42
|
+
|
43
|
+
assert_raises(Exception) do
|
44
|
+
conn.send_object([:exception])
|
45
|
+
obj = conn.receive_object
|
46
|
+
|
47
|
+
puts "Received object which should have been exception: #{obj.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
conn.dump_errors
|
51
|
+
conn.send_object([:stderr, stderr_text])
|
52
|
+
|
53
|
+
puts "Attemping to read from #{conn.error.to_i}..."
|
54
|
+
assert_equal stderr_text, conn.error.readline.chomp
|
55
|
+
|
56
|
+
conn.stop
|
57
|
+
end
|
58
|
+
|
59
|
+
assert(connection_started, "Connection started")
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_shell_execution
|
63
|
+
connection_started = false
|
64
|
+
code = Pathname.new(__FILE__).dirname + "client.rb"
|
65
|
+
|
66
|
+
test_obj = [1, 2, 3, 4, "five"]
|
67
|
+
|
68
|
+
RExec::start_server(code.read, "/bin/sh -c ruby", :passthrough => []) do |conn, task|
|
69
|
+
connection_started = true
|
70
|
+
conn.send_object([:bounce, test_obj])
|
71
|
+
|
72
|
+
assert_equal test_obj, conn.receive_object
|
73
|
+
|
74
|
+
conn.stop
|
75
|
+
end
|
76
|
+
|
77
|
+
assert(connection_started, "Connection started")
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_shell_execution_non_block
|
81
|
+
connection_started = false
|
82
|
+
code = Pathname.new(__FILE__).dirname + "client.rb"
|
83
|
+
|
84
|
+
test_obj = [1, 2, 3, 4, "five"]
|
85
|
+
|
86
|
+
conn, task = RExec::start_server(code.read, "/bin/sh -c ruby", :passthrough => [])
|
87
|
+
conn.send_object([:bounce, test_obj])
|
88
|
+
|
89
|
+
assert_equal test_obj, conn.receive_object
|
90
|
+
|
91
|
+
conn.stop
|
92
|
+
end
|
93
|
+
end
|
data/test/test_task.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'helper'
|
24
|
+
|
25
|
+
require 'fileutils'
|
26
|
+
require 'pathname'
|
27
|
+
require 'rexec'
|
28
|
+
require 'timeout'
|
29
|
+
|
30
|
+
class TaskTest < Test::Unit::TestCase
|
31
|
+
TASK_PATH = Pathname.new(__FILE__).dirname + "./task.rb"
|
32
|
+
TEXT = "The quick brown fox jumped over the lazy dog."
|
33
|
+
STDOUT_TEXT = "STDOUT: " + TEXT
|
34
|
+
STDERR_TEXT = "STDERR: " + TEXT
|
35
|
+
|
36
|
+
def test_script_execution
|
37
|
+
RExec::Task.open(TASK_PATH) do |task|
|
38
|
+
task.input.puts(TEXT)
|
39
|
+
task.input.close
|
40
|
+
|
41
|
+
assert_equal STDOUT_TEXT, task.output.readline.chomp
|
42
|
+
assert_equal STDERR_TEXT, task.error.readline.chomp
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_ruby_execution
|
47
|
+
RExec::Task.open(["ruby", '-']) do |task|
|
48
|
+
task.input.puts(TASK_PATH.read)
|
49
|
+
task.input.puts("\004")
|
50
|
+
|
51
|
+
task.input.puts(TEXT)
|
52
|
+
task.input.close
|
53
|
+
|
54
|
+
assert_equal STDOUT_TEXT, task.output.readline.chomp
|
55
|
+
assert_equal STDERR_TEXT, task.error.readline.chomp
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_spawn_child
|
60
|
+
rd, wr = IO.pipe
|
61
|
+
pid = RExec::Task.spawn_child do
|
62
|
+
rd.close
|
63
|
+
wr.write(TEXT)
|
64
|
+
wr.close
|
65
|
+
exit(10)
|
66
|
+
end
|
67
|
+
|
68
|
+
wr.close
|
69
|
+
|
70
|
+
pid, status = Process.wait2(pid)
|
71
|
+
|
72
|
+
assert_equal rd.read, TEXT
|
73
|
+
assert_equal status, status
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_spawn_daemon
|
77
|
+
rd, wr = IO.pipe
|
78
|
+
|
79
|
+
# We launch one daemon to start another. The first daemon will exit, but the second will keep on running.
|
80
|
+
ppid = RExec::Task.spawn_daemon do
|
81
|
+
RExec::Task.spawn_daemon do
|
82
|
+
rd.close
|
83
|
+
sleep 0.5
|
84
|
+
wr.puts(TEXT, Process.pid)
|
85
|
+
wr.close
|
86
|
+
sleep 0.5
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
wr.close
|
91
|
+
|
92
|
+
Timeout::timeout(5) do
|
93
|
+
until !RExec::Task.running?(ppid) do
|
94
|
+
sleep(0.1)
|
95
|
+
end
|
96
|
+
|
97
|
+
text = rd.readline.chomp
|
98
|
+
pid = rd.readline.chomp.to_i
|
99
|
+
|
100
|
+
assert_raises(EOFError) do
|
101
|
+
rd.readline
|
102
|
+
end
|
103
|
+
|
104
|
+
assert_equal text, TEXT
|
105
|
+
assert RExec::Task.running?(pid)
|
106
|
+
|
107
|
+
until !RExec::Task.running?(pid) do
|
108
|
+
sleep(0.1)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_task
|
114
|
+
test_results = Proc.new do |input, output, error|
|
115
|
+
input.puts TEXT
|
116
|
+
input.flush
|
117
|
+
|
118
|
+
assert_equal output.readline.chomp, STDOUT_TEXT
|
119
|
+
assert_equal error.readline.chomp, STDERR_TEXT
|
120
|
+
end
|
121
|
+
|
122
|
+
RExec::Task.open(TASK_PATH) do |task|
|
123
|
+
test_results.call(task.input, task.output, task.error)
|
124
|
+
end
|
125
|
+
|
126
|
+
rd, wr = IO.pipe
|
127
|
+
|
128
|
+
RExec::Task.open(TASK_PATH, :in => rd) do |task|
|
129
|
+
test_results.call(wr, task.output, task.error)
|
130
|
+
end
|
131
|
+
|
132
|
+
assert rd.closed?
|
133
|
+
|
134
|
+
assert_raises(Errno::EPIPE) do
|
135
|
+
wr.puts "The pipe is closed on the other side.."
|
136
|
+
end
|
137
|
+
|
138
|
+
in_rd, in_wr = IO.pipe
|
139
|
+
out_rd, out_wr = IO.pipe
|
140
|
+
err_rd, err_wr = IO.pipe
|
141
|
+
|
142
|
+
spawn_child_daemon = Proc.new do
|
143
|
+
task = RExec::Task.open(TASK_PATH, :in => in_rd, :out => out_wr, :err => err_wr, :daemonize => true)
|
144
|
+
end
|
145
|
+
|
146
|
+
task = RExec::Task.open(spawn_child_daemon, :daemonize => true)
|
147
|
+
|
148
|
+
until !task.running? do
|
149
|
+
sleep 0.1
|
150
|
+
end
|
151
|
+
|
152
|
+
test_results.call(in_wr, out_rd, err_rd)
|
153
|
+
|
154
|
+
assert !task.running?
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_task_passthrough
|
158
|
+
script = "echo " + "Hello World!".dump + " | #{TASK_PATH.realpath.to_s.dump}"
|
159
|
+
|
160
|
+
RExec::Task.open(script, :passthrough => :all) do
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
[$stdin, $stdout, $stderr].each do |io|
|
165
|
+
assert !io.closed?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
metadata
CHANGED
@@ -1,38 +1,29 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rexec
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.5.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
- 1
|
10
|
-
version: 1.4.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Samuel Williams
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2011-08-09 00:00:00 Z
|
12
|
+
date: 2012-10-15 00:00:00.000000000 Z
|
19
13
|
dependencies: []
|
20
|
-
|
21
14
|
description:
|
22
|
-
email: samuel
|
23
|
-
executables:
|
15
|
+
email: samuel@oriontransfer.org
|
16
|
+
executables:
|
24
17
|
- daemon-exec
|
25
18
|
extensions: []
|
26
|
-
|
27
19
|
extra_rdoc_files: []
|
28
|
-
|
29
|
-
files:
|
20
|
+
files:
|
30
21
|
- bin/daemon-exec
|
31
22
|
- lib/rexec/client.rb
|
32
23
|
- lib/rexec/connection.rb
|
33
24
|
- lib/rexec/daemon/base.rb
|
34
25
|
- lib/rexec/daemon/controller.rb
|
35
|
-
- lib/rexec/daemon/
|
26
|
+
- lib/rexec/daemon/process_file.rb
|
36
27
|
- lib/rexec/daemon.rb
|
37
28
|
- lib/rexec/environment.rb
|
38
29
|
- lib/rexec/priviledges.rb
|
@@ -44,49 +35,39 @@ files:
|
|
44
35
|
- lib/rexec.rb
|
45
36
|
- test/client.rb
|
46
37
|
- test/daemon.rb
|
47
|
-
- test/
|
38
|
+
- test/helper.rb
|
48
39
|
- test/interrupt.rb
|
49
40
|
- test/listing_example.rb
|
50
|
-
- test/remote_server_test.rb
|
51
|
-
- test/server_test.rb
|
52
41
|
- test/task.rb
|
53
|
-
- test/
|
42
|
+
- test/test_daemon.rb
|
43
|
+
- test/test_remote_server.rb
|
44
|
+
- test/test_server.rb
|
45
|
+
- test/test_task.rb
|
54
46
|
- README.md
|
47
|
+
- rakefile.rb
|
48
|
+
- Gemfile
|
55
49
|
homepage: http://www.oriontransfer.co.nz/gems/rexec
|
56
50
|
licenses: []
|
57
|
-
|
58
51
|
post_install_message:
|
59
52
|
rdoc_options: []
|
60
|
-
|
61
|
-
require_paths:
|
53
|
+
require_paths:
|
62
54
|
- lib
|
63
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
56
|
none: false
|
65
|
-
requirements:
|
66
|
-
- -
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
|
69
|
-
|
70
|
-
- 0
|
71
|
-
version: "0"
|
72
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
62
|
none: false
|
74
|
-
requirements:
|
75
|
-
- -
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
|
78
|
-
segments:
|
79
|
-
- 0
|
80
|
-
version: "0"
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
81
67
|
requirements: []
|
82
|
-
|
83
68
|
rubyforge_project:
|
84
|
-
rubygems_version: 1.8.
|
69
|
+
rubygems_version: 1.8.24
|
85
70
|
signing_key:
|
86
71
|
specification_version: 3
|
87
|
-
summary: RExec
|
88
|
-
test_files:
|
89
|
-
- test/daemon_test.rb
|
90
|
-
- test/remote_server_test.rb
|
91
|
-
- test/server_test.rb
|
92
|
-
- test/task_test.rb
|
72
|
+
summary: RExec assists with and manages the execution of child and daemon tasks.
|
73
|
+
test_files: []
|
data/test/server_test.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
# THE SOFTWARE.
|
22
|
-
|
23
|
-
require 'rubygems'
|
24
|
-
|
25
|
-
require 'test/unit'
|
26
|
-
require 'fileutils'
|
27
|
-
require 'pathname'
|
28
|
-
require 'rexec'
|
29
|
-
|
30
|
-
class ServerTest < Test::Unit::TestCase
|
31
|
-
def test_local_execution
|
32
|
-
code = Pathname.new(__FILE__).dirname + "./client.rb"
|
33
|
-
sobj = [1, 2, "three", 4]
|
34
|
-
stderr_text = "There was no error.. maybe?"
|
35
|
-
connection_started = false
|
36
|
-
object_received = false
|
37
|
-
|
38
|
-
RExec::start_server(code.read, "ruby", :passthrough => []) do |conn, task|
|
39
|
-
connection_started = true
|
40
|
-
conn.send_object([:bounce, sobj])
|
41
|
-
|
42
|
-
assert_equal sobj, conn.receive_object
|
43
|
-
|
44
|
-
assert_raises(Exception) do
|
45
|
-
conn.send_object([:exception])
|
46
|
-
obj = conn.receive_object
|
47
|
-
|
48
|
-
puts "Received object which should have been exception: #{obj.inspect}"
|
49
|
-
end
|
50
|
-
|
51
|
-
conn.dump_errors
|
52
|
-
conn.send_object([:stderr, stderr_text])
|
53
|
-
|
54
|
-
puts "Attemping to read from #{conn.error.to_i}..."
|
55
|
-
assert_equal stderr_text, conn.error.readline.chomp
|
56
|
-
|
57
|
-
conn.stop
|
58
|
-
end
|
59
|
-
|
60
|
-
assert(connection_started, "Connection started")
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_shell_execution
|
64
|
-
connection_started = false
|
65
|
-
code = Pathname.new(__FILE__).dirname + "client.rb"
|
66
|
-
|
67
|
-
test_obj = [1, 2, 3, 4, "five"]
|
68
|
-
|
69
|
-
RExec::start_server(code.read, "/bin/sh -c ruby", :passthrough => []) do |conn, task|
|
70
|
-
connection_started = true
|
71
|
-
conn.send_object([:bounce, test_obj])
|
72
|
-
|
73
|
-
assert_equal test_obj, conn.receive_object
|
74
|
-
|
75
|
-
conn.stop
|
76
|
-
end
|
77
|
-
|
78
|
-
assert(connection_started, "Connection started")
|
79
|
-
end
|
80
|
-
|
81
|
-
def test_shell_execution_non_block
|
82
|
-
connection_started = false
|
83
|
-
code = Pathname.new(__FILE__).dirname + "client.rb"
|
84
|
-
|
85
|
-
test_obj = [1, 2, 3, 4, "five"]
|
86
|
-
|
87
|
-
conn, task = RExec::start_server(code.read, "/bin/sh -c ruby", :passthrough => [])
|
88
|
-
conn.send_object([:bounce, test_obj])
|
89
|
-
|
90
|
-
assert_equal test_obj, conn.receive_object
|
91
|
-
|
92
|
-
conn.stop
|
93
|
-
end
|
94
|
-
end
|
data/test/task_test.rb
DELETED
@@ -1,170 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
# THE SOFTWARE.
|
22
|
-
|
23
|
-
require 'rubygems'
|
24
|
-
|
25
|
-
require 'test/unit'
|
26
|
-
require 'fileutils'
|
27
|
-
require 'pathname'
|
28
|
-
require 'rexec'
|
29
|
-
|
30
|
-
require 'timeout'
|
31
|
-
|
32
|
-
class TaskTest < Test::Unit::TestCase
|
33
|
-
TASK_PATH = Pathname.new(__FILE__).dirname + "./task.rb"
|
34
|
-
TEXT = "The quick brown fox jumped over the lazy dog."
|
35
|
-
STDOUT_TEXT = "STDOUT: " + TEXT
|
36
|
-
STDERR_TEXT = "STDERR: " + TEXT
|
37
|
-
|
38
|
-
def test_script_execution
|
39
|
-
RExec::Task.open(TASK_PATH) do |task|
|
40
|
-
task.input.puts(TEXT)
|
41
|
-
task.input.close
|
42
|
-
|
43
|
-
assert_equal STDOUT_TEXT, task.output.readline.chomp
|
44
|
-
assert_equal STDERR_TEXT, task.error.readline.chomp
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_ruby_execution
|
49
|
-
RExec::Task.open("ruby") do |task|
|
50
|
-
task.input.puts(TASK_PATH.read)
|
51
|
-
task.input.puts("\004")
|
52
|
-
|
53
|
-
task.input.puts(TEXT)
|
54
|
-
task.input.close
|
55
|
-
|
56
|
-
assert_equal STDOUT_TEXT, task.output.readline.chomp
|
57
|
-
assert_equal STDERR_TEXT, task.error.readline.chomp
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_spawn_child
|
62
|
-
rd, wr = IO.pipe
|
63
|
-
pid = RExec::Task.spawn_child do
|
64
|
-
rd.close
|
65
|
-
wr.write(TEXT)
|
66
|
-
wr.close
|
67
|
-
exit(10)
|
68
|
-
end
|
69
|
-
|
70
|
-
wr.close
|
71
|
-
|
72
|
-
pid, status = Process.wait2(pid)
|
73
|
-
|
74
|
-
assert_equal rd.read, TEXT
|
75
|
-
assert_equal status, status
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_spawn_daemon
|
79
|
-
rd, wr = IO.pipe
|
80
|
-
|
81
|
-
# We launch one daemon to start another. The first daemon will exit, but the second will keep on running.
|
82
|
-
ppid = RExec::Task.spawn_daemon do
|
83
|
-
RExec::Task.spawn_daemon do
|
84
|
-
rd.close
|
85
|
-
sleep 0.5
|
86
|
-
wr.puts(TEXT, Process.pid)
|
87
|
-
wr.close
|
88
|
-
sleep 0.5
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
wr.close
|
93
|
-
|
94
|
-
Timeout::timeout(5) do
|
95
|
-
until !RExec::Task.running?(ppid) do
|
96
|
-
sleep(0.1)
|
97
|
-
end
|
98
|
-
|
99
|
-
text = rd.readline.chomp
|
100
|
-
pid = rd.readline.chomp.to_i
|
101
|
-
|
102
|
-
assert_raises(EOFError) do
|
103
|
-
rd.readline
|
104
|
-
end
|
105
|
-
|
106
|
-
assert_equal text, TEXT
|
107
|
-
assert RExec::Task.running?(pid)
|
108
|
-
|
109
|
-
until !RExec::Task.running?(pid) do
|
110
|
-
sleep(0.1)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def test_task
|
116
|
-
test_results = Proc.new do |input, output, error|
|
117
|
-
input.puts TEXT
|
118
|
-
input.flush
|
119
|
-
|
120
|
-
assert_equal output.readline.chomp, STDOUT_TEXT
|
121
|
-
assert_equal error.readline.chomp, STDERR_TEXT
|
122
|
-
end
|
123
|
-
|
124
|
-
RExec::Task.open(TASK_PATH) do |task|
|
125
|
-
test_results.call(task.input, task.output, task.error)
|
126
|
-
end
|
127
|
-
|
128
|
-
rd, wr = IO.pipe
|
129
|
-
|
130
|
-
RExec::Task.open(TASK_PATH, :in => rd) do |task|
|
131
|
-
test_results.call(wr, task.output, task.error)
|
132
|
-
end
|
133
|
-
|
134
|
-
assert rd.closed?
|
135
|
-
|
136
|
-
assert_raises(Errno::EINVAL) do
|
137
|
-
wr.puts "The pipe is closed on the other side.."
|
138
|
-
end
|
139
|
-
|
140
|
-
in_rd, in_wr = IO.pipe
|
141
|
-
out_rd, out_wr = IO.pipe
|
142
|
-
err_rd, err_wr = IO.pipe
|
143
|
-
|
144
|
-
spawn_child_daemon = Proc.new do
|
145
|
-
task = RExec::Task.open(TASK_PATH, :in => in_rd, :out => out_wr, :err => err_wr, :daemonize => true)
|
146
|
-
end
|
147
|
-
|
148
|
-
task = RExec::Task.open(spawn_child_daemon, :daemonize => true)
|
149
|
-
|
150
|
-
until !task.running? do
|
151
|
-
sleep 0.1
|
152
|
-
end
|
153
|
-
|
154
|
-
test_results.call(in_wr, out_rd, err_rd)
|
155
|
-
|
156
|
-
assert !task.running?
|
157
|
-
end
|
158
|
-
|
159
|
-
def test_task_passthrough
|
160
|
-
script = "echo " + "Hello World!".dump + " | #{TASK_PATH.realpath.to_s.dump}"
|
161
|
-
|
162
|
-
RExec::Task.open(script, :passthrough => :all) do
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
[$stdin, $stdout, $stderr].each do |io|
|
167
|
-
assert !io.closed?
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|