10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
# File 'lib/base/utils.rb', line 10
def sh(*args)
options = args[-1].respond_to?(:to_hash) ? args.pop.to_hash: {}
options = { :timeout => 5.0, :max => 1024 * 1024, :sudo => true, :block => true }.merge(options)
arg = options[:sudo] == false ? args[0] : "sudo " << args[0]
begin
stdin, stdout, stderr, status = Open3.popen3(arg)
pid = status[:pid]
out_buf = ""
err_buf = ""
if options[:block]
start = Time.now
while (Time.now - start) < options[:timeout] && status.alive?
begin
out_buf << stdout.read_nonblock(4096)
err_buf << stderr.read_nonblock(4096)
rescue IO::WaitReadable, EOFError
end
sleep 0.2
end
if status.alive?
Process.kill("TERM", pid)
Process.detach(pid)
raise RuntimeError, "sh #{args} executed with failure and process with pid #{pid} timed out:\nstdout:\n#{out_buf}\nstderr:\n#{err_buf}"
end
exit_status = status.value.exitstatus
raise RuntimeError, "sh #{args} executed with failure and process with pid #{pid} exited with #{status.value.exitstatus}:\nstdout:\n#{out_buf}\nstderr:\n#{err_buf}" unless exit_status == 0
exit_status
else
Thread.new do
sleep options[:timeout]
if status.alive?
Process.kill("TERM", pid)
Process.detach(pid)
logger.error "sh #{args} executed with pid #{pid} timed out" if logger
else
logger.error "sh #{args} executed with failure, the exit status is #{status.value.exitstatus}" if status.value.exitstatus != 0 && logger
end
end
return 0
end
rescue Errno::EPERM
raise RuntimeError, "sh #{args} executed with failure and process with pid #{pid} cannot be killed (privilege issue?):\nstdout:\n#{out_buf}\nstderr:\n#{err_buf}"
rescue Errno::ESRCH
raise RuntimeError, "sh #{args} executed with failure and process with pid #{pid} does not exist:\nstdout:\n#{out_buf}\nstderr:\n#{err_buf}"
rescue => e
raise e
end
end
|