rexec 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|