lib/linux_admin/common.rb in linux_admin-0.0.1 vs lib/linux_admin/common.rb in linux_admin-0.1.0
- old
+ new
@@ -1,21 +1,94 @@
-module LinuxAdmin
+require 'shellwords'
+
+class LinuxAdmin
module Common
- def self.run(cmd, options = {})
+ def write(file, content)
+ raise ArgumentError, "file and content can not be empty" if file.blank? || content.blank?
+ File.open(file, "w") do |f|
+ f.write(content)
+ end
+ end
+
+ def run(cmd, options = {})
+ params = options[:params] || options[:parameters]
+
begin
- r, w = IO.pipe
- pid, status = Process.wait2(Kernel.spawn(cmd, :err => [:child, :out], :out => w))
- w.close
- if options[:return_output] && status.exitstatus == 0
- r.read
- elsif options[:return_exitstatus] || status.exitstatus == 0
- status.exitstatus
+ out = launch(build_cmd(cmd, params))
+
+ if options[:return_output] && exitstatus == 0
+ out
+ elsif options[:return_exitstatus] || exitstatus == 0
+ exitstatus
else
- raise "Error: Exit Code #{status.exitstatus}"
+ raise "Error: Exit Code #{exitstatus}"
end
rescue
return nil if options[:return_exitstatus]
raise
+ ensure
+ self.exitstatus = nil
end
+ end
+
+ private
+
+ def sanitize(params)
+ return {} if params.blank?
+ params.each_with_object({}) do |(k, v), h|
+ h[k] =
+ case v
+ when Array; v.collect {|s| s.shellescape}
+ when NilClass; v
+ else v.shellescape
+ end
+ end
+ end
+
+ def assemble_params(sanitized_params)
+ sanitized_params.collect do |pair|
+ pair_joiner = pair.first.try(:end_with?, "=") ? "" : " "
+ pair.flatten.compact.join(pair_joiner)
+ end.join(" ")
+ end
+
+ def build_cmd(cmd, params = nil)
+ return cmd if params.blank?
+ "#{cmd} #{assemble_params(sanitize(params))}"
+ end
+
+ # IO pipes have a maximum size of 64k before blocking,
+ # so we need to read and write synchronously.
+ # http://stackoverflow.com/questions/13829830/ruby-process-spawn-stdout-pipe-buffer-size-limit/13846146#13846146
+ THREAD_SYNC_KEY = "LinuxAdmin-exitstatus"
+
+ def launch(cmd)
+ pipe_r, pipe_w = IO.pipe
+ pid = Kernel.spawn(cmd, :err => [:child, :out], :out => pipe_w)
+ wait_for_process(pid, pipe_w)
+ wait_for_output(pipe_r)
+ end
+
+ def wait_for_process(pid, pipe_w)
+ self.exitstatus = :not_done
+ Thread.new(Thread.current) do |parent_thread|
+ _, status = Process.wait2(pid)
+ pipe_w.close
+ parent_thread[THREAD_SYNC_KEY] = status.exitstatus
+ end
+ end
+
+ def wait_for_output(pipe_r)
+ out = pipe_r.read
+ sleep(0.1) while exitstatus == :not_done
+ return out
+ end
+
+ def exitstatus
+ Thread.current[THREAD_SYNC_KEY]
+ end
+
+ def exitstatus=(value)
+ Thread.current[THREAD_SYNC_KEY] = value
end
end
end
\ No newline at end of file