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 ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem "rake"
@@ -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
- @@var_directory = nil
40
- @@log_directory = nil
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
- # Base directory for daemon log files / run files
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
- var_directory
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
- @@log_directory || File.join(var_directory, "log", daemon_name)
61
+ File.join(working_directory, "log")
61
62
  end
62
63
 
63
- # Standard log file for errors
64
- def self.err_fn
65
- File.join(log_directory, "stderr.log")
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
- # Standard log file for normal output
69
- def self.log_fn
70
- File.join(log_directory, "stdout.log")
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.pid_directory
75
- @@pid_directory || File.join(var_directory, "run", daemon_name)
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
- # Standard pid file
79
- def self.pid_fn
80
- File.join(pid_directory, "#{daemon_name}.pid")
81
- end
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.tail_err_log(outp)
86
+ # Prints some information relating to daemon startup problems.
87
+ def self.tail_log(output)
92
88
  lines = []
93
89
 
94
- File.open(err_fn, "r") do |fp|
95
- fp.seek_end
90
+ File.open(error_log_path, "r") do |log_file|
91
+ log_file.seek_end
96
92
 
97
- fp.reverse_each_line do |line|
93
+ log_file.reverse_each_line do |line|
98
94
  lines << line
99
- break if line.match("=== Error Log") || line.match("=== Daemon Exception Backtrace")
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
- outp.puts line
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(err_fn, "r") do |fp|
112
- fp.seek_end
106
+ File.open(error_log_path, "r") do |log_file|
107
+ log_file.seek_end
113
108
 
114
109
  count = 2
115
- fp.reverse_each_line do |line|
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
- @@var_directory = File.expand_path(@@var_directory) if @@var_directory
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(pid_directory)
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/pidfile'
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
- #puts "Running in #{WorkingDirectory}, logs in #{LogDirectory}"
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
- PidFile.cleanup(daemon)
40
+ ProcessFile.cleanup(daemon)
43
41
  when 'restart'
44
42
  stop(daemon)
45
- PidFile.cleanup(daemon)
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 PidFile.status(daemon)
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
- PidFile.clear(daemon)
67
+ ProcessFile.clear(daemon)
70
68
  end
71
69
 
72
70
  daemon.prefork
73
- daemon.mark_err_log
71
+ daemon.mark_log
74
72
 
75
73
  fork do
76
74
  Process.setsid
77
75
  exit if fork
78
76
 
79
- PidFile.store(daemon, Process.pid)
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.log_fn, "a"
83
+ $stdout.reopen daemon.log_file_path, "a"
86
84
  $stdout.sync = true
87
85
 
88
- $stderr.reopen daemon.err_fn, "a"
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 = PidFile.recall(daemon)
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 = PidFile.recall(daemon)
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 PidFile.status(daemon)
149
+ case ProcessFile.status(daemon)
152
150
  when :running
153
- puts "Daemon status: running pid=#{PidFile.recall(daemon)}"
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.tail_err_log($stderr)
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
- if !File.file?(daemon.pid_fn)
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 = PidFile.recall(daemon)
176
+ pid = ProcessFile.recall(daemon)
179
177
 
180
178
  # Check if the daemon is already stopped...
181
- unless PidFile.running(daemon)
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 = PidFile.recall(daemon)
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 PidFile.running(daemon) and attempts > 0
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 PidFile.running(daemon)
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
- PidFile.clear(daemon)
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 PidFile
24
+ module ProcessFile
25
25
  # Saves the pid for the given daemon
26
26
  def self.store(daemon, pid)
27
- File.open(daemon.pid_fn, 'w') {|f| f << pid}
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.pid_fn).to_i rescue nil
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.pid_fn
38
- FileUtils.rm(daemon.pid_fn)
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.pid_fn
62
- return PidFile.running(daemon) ? :running : :unknown
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
@@ -21,8 +21,8 @@
21
21
  module RExec
22
22
  module VERSION
23
23
  MAJOR = 1
24
- MINOR = 4
25
- TINY = 1
24
+ MINOR = 5
25
+ TINY = 0
26
26
 
27
27
  STRING = [MAJOR, MINOR, TINY].join('.')
28
28
  end
@@ -0,0 +1,10 @@
1
+
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
10
+
@@ -19,18 +19,18 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  $connection.run do |object|
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
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
@@ -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
- require 'rubygems'
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
- @@var_directory = "/tmp/ruby-test/var"
37
+ @@var_directory = "/tmp/ruby-test/var"
37
38
 
38
- def self.run
39
- puts "Starting server..."
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
- @@rpc_server.start
65
- end
66
-
67
- def self.shutdown
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
- @@rpc_server.shutdown
70
- end
68
+ @@rpc_server.shutdown
69
+ end
71
70
  end
72
71
 
73
72
  TestDaemon.daemonize
@@ -0,0 +1,5 @@
1
+
2
+ $LOAD_PATH.unshift File.expand_path("../../lib/", __FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
@@ -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 'rubygems'
23
+ require 'helper'
24
+
24
25
  require 'rexec'
25
26
 
26
27
  CLIENT = <<EOF
27
28
 
28
29
  $connection.run do |path|
29
- listing = []
30
+ listing = []
30
31
 
31
- IO.popen("ls -la " + path.dump, "r+") do |ls|
32
- listing = ls.readlines
33
- end
32
+ IO.popen("ls -la " + path.dump, "r+") do |ls|
33
+ listing = ls.readlines
34
+ end
34
35
 
35
- $connection.send_object(listing)
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
- puts "Sending path..."
45
- conn.send_object("/")
45
+ puts "Sending path..."
46
+ conn.send_object("/")
46
47
 
47
- puts "Waiting for response..."
48
- listing = conn.receive_object
48
+ puts "Waiting for response..."
49
+ listing = conn.receive_object
49
50
 
50
- puts "Received listing:"
51
- listing.each do |entry|
52
- puts "\t#{entry}"
53
- end
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 'rubygems'
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
- DAEMON = (Pathname.new(__FILE__).dirname + "./daemon.rb").realpath
30
- def setup
31
- system(DAEMON, "start")
32
-
33
- # Daemon takes a moment to become fully operational
34
- sleep(5)
35
- end
36
-
37
- def teardown
38
- system(DAEMON, "stop")
39
- end
40
-
41
- def test_connection
42
- rpc = XMLRPC::Client.new_from_uri("https://localhost:11235")
43
- rpc.call("add", 10)
44
-
45
- total = rpc.call("total")
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
@@ -23,9 +23,8 @@
23
23
  # This script is used to test actual remote connections
24
24
  # e.g. ./test_remote.rb "ssh haru.oriontransfer.org"
25
25
 
26
- require 'rubygems'
26
+ require 'helper'
27
27
 
28
- require 'test/unit'
29
28
  require 'fileutils'
30
29
  require 'pathname'
31
30
  require 'rexec'
@@ -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
@@ -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
- hash: 5
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.williams@oriontransfer.co.nz
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/pidfile.rb
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/daemon_test.rb
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/task_test.rb
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
- hash: 3
69
- segments:
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
- hash: 3
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.7
69
+ rubygems_version: 1.8.24
85
70
  signing_key:
86
71
  specification_version: 3
87
- summary: RExec (Remote Execution) is a tool to facilitate communicating to another ruby process and executing code.
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: []
@@ -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
@@ -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