aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------commonlib0
-rw-r--r--lib/external_command.rb135
-rwxr-xr-xspec/lib/external_command_scripts/output.sh22
-rw-r--r--spec/lib/external_command_spec.rb40
4 files changed, 0 insertions, 197 deletions
diff --git a/commonlib b/commonlib
-Subproject a87ebeae21166b3b4a8a66b32399861fcd6d0c4
+Subproject cf056c6678d59f74fc29eb2b2c1427573fc643a
diff --git a/lib/external_command.rb b/lib/external_command.rb
deleted file mode 100644
index 96292854f..000000000
--- a/lib/external_command.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# Run an external command, capturing its stdout and stderr
-# streams into variables.
-#
-# So it’s rather like the `backtick` built-in, except that:
-# - The command is run as-is, rather than being parsed by the shell;
-# - Standard error is also captured.
-#
-# After the run() method has been called, the instance variables
-# out, err and status contain the contents of the process’s stdout,
-# the contents of its stderr, and the exit status.
-#
-# Example usage:
-# require 'external_command'
-# xc = ExternalCommand("ls", "-l").run()
-# puts "Ran ls -l with exit status #{xc.status}"
-# puts "===STDOUT===\n#{xc.out}"
-# puts "===STDERR===\n#{xc.err}"
-#
-# The out and err attributes are writeable. If you assign
-# a string, after calling the constructor and before calling
-# run(), then the subprocess output/error will be appended
-# to this string.
-
-# <rant author="robin">
-# In any sane language, this would be implemented with a
-# single child process. The parent process would block on
-# select(), and when the child process terminated, the
-# select call would be interrupted by a CHLD signal
-# and return EINTR. Unfortunately Ruby goes out of its
-# way to prevent this from working, automatically restarting
-# the select call if EINTR is returned. Therefore we
-# use a parent-child-grandchild arrangement, where the
-# parent blocks on select() and the child blocks on
-# waitpid(). When the child detects that the grandchild
-# has finished, it writes to a pipe that’s included in
-# the parent’s select() for this purpose.
-# </rant>
-
-class ExternalCommand
- attr_accessor :out, :err
- attr_reader :status
-
- def initialize(cmd, *args)
- @cmd = cmd
- @args = args
-
- # Strings to collect stdout and stderr from the child process
- # These may be replaced by the caller, to append to existing strings.
- @out = ""
- @err = ""
- @fin = ""
- end
-
- def run()
- # Pipes for parent-child communication
- @out_read, @out_write = IO::pipe
- @err_read, @err_write = IO::pipe
- @fin_read, @fin_write = IO::pipe
-
- @pid = fork do
- # Here we’re in the child process.
- child_process
- end
-
- # Here we’re in the parent process.
- parent_process
-
- return self
- end
-
- private
-
- def child_process()
- # Reopen stdout and stderr to point at the pipes
- STDOUT.reopen(@out_write)
- STDERR.reopen(@err_write)
-
- # Close all the filehandles other than the ones we intend to use.
- ObjectSpace.each_object(IO) do |fh|
- fh.close unless (
- [STDOUT, STDERR, @fin_write].include?(fh) || fh.closed?)
- end
-
- Process::waitpid(fork { grandchild_process })
- @fin_write.puts($?.exitstatus.to_s)
-
- exit! 0
- end
-
- def grandchild_process()
- exec(@cmd, *@args)
-
- # This is only reached if the exec fails
- @err_write.print("Failed to exec: #{[@cmd, *@args].join(' ')}")
- exit! 99
- end
-
- def parent_process()
- # Close the writing ends of the pipes
- @out_write.close
- @err_write.close
- @fin_write.close
-
- @fhs = {@out_read => @out, @err_read => @err, @fin_read => @fin}
-
- while @fin.empty?
- ok = read_data
- if !ok
- raise "select() timed out even with a nil (infinite) timeout"
- end
- end
-
- while read_data(0)
- # Pull out any data that’s left in the pipes
- end
-
- Process::waitpid(@pid)
- @status = @fin.to_i
- @out_read.close
- @err_read.close
- end
-
- def read_data(timeout=nil)
- ready_array = IO.select(@fhs.keys, [], [], timeout)
- return false if ready_array.nil?
- ready_array[0].each do |fh|
- begin
- @fhs[fh] << fh.readpartial(8192)
- rescue EOFError
- @fhs.delete fh
- end
- end
- return true
- end
-end
diff --git a/spec/lib/external_command_scripts/output.sh b/spec/lib/external_command_scripts/output.sh
deleted file mode 100755
index 0472c89a3..000000000
--- a/spec/lib/external_command_scripts/output.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-
-out_msg=${1:-out}
-err_msg=${2:-}
-repeats=${3:-10}
-exit_status=${4:-0}
-
-n=0
-while [ "$n" -lt "$repeats" ]
-do
- if [ -n "$out_msg" ]
- then
- echo "$out_msg $n"
- fi
- if [ -n "$err_msg" ]
- then
- echo >&2 "$err_msg $n"
- fi
- n=$[$n + 1]
-done
-
-exit "$exit_status"
diff --git a/spec/lib/external_command_spec.rb b/spec/lib/external_command_spec.rb
deleted file mode 100644
index 0ff1a9c0a..000000000
--- a/spec/lib/external_command_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# This is a test of the external_command library
-
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-script_dir = File.join(File.dirname(__FILE__), 'external_command_scripts')
-output_script = File.join(script_dir, "output.sh")
-
-require 'external_command'
-
-describe "when running ExternalCommand" do
-
- it "should get correct status code for /bin/true" do
- t = ExternalCommand.new("/bin/true").run()
- t.status.should == 0
- t.out.should == ""
- t.err.should == ""
- end
-
- it "should get correct status code for /bin/false" do
- f = ExternalCommand.new("/bin/false").run()
- f.status.should == 1
- f.out.should == ""
- f.err.should == ""
- end
-
- it "should get stdout and stderr" do
- f = ExternalCommand.new(output_script, "out", "err", "10", "23").run()
- f.status.should == 23
- f.out.should == (0..9).map {|i| "out #{i}\n"}.join("")
- f.err.should == (0..9).map {|i| "err #{i}\n"}.join("")
- end
-
- it "should work with large amounts of data" do
- f = ExternalCommand.new(output_script, "a longer output line", "a longer error line", "10000", "5").run()
- f.status.should == 5
- f.out.should == (0..9999).map {|i| "a longer output line #{i}\n"}.join("")
- f.err.should == (0..9999).map {|i| "a longer error line #{i}\n"}.join("")
- end
-
-end
-