| 
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 |