Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions lib/linux_admin/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,57 @@ module LinuxAdmin
module Common
def self.run(cmd, options = {})
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(cmd)
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

# 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 self.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 self.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 self.wait_for_output(pipe_r)
out = pipe_r.read
sleep(0.1) while exitstatus == :not_done
return out
end

def self.exitstatus
Thread.current[THREAD_SYNC_KEY]
end

def self.exitstatus=(value)
Thread.current[THREAD_SYNC_KEY] = value
end
end
end