Method: Frontkick::Command.exec

Defined in:
lib/frontkick/command.rb

.exec(*env_cmd, **opts, &block) ⇒ Object



7
8
9
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/frontkick/command.rb', line 7

def self.exec(*env_cmd, **opts, &block)
  env, cmd = env_cmd.size >= 2 ? env_cmd : [{}, env_cmd.first]
  # With this conversion,
  #
  #   popen3(env, *cmd_array, opts)
  #
  # works for both interface of:
  #
  #   popen3(env, command, opts)
  #   popen3(env, program, *args, opts)
  cmd_array = cmd.is_a?(Array) ? cmd : [cmd]

  opts[:timeout_kill] = true unless opts.has_key?(:timeout_kill) # default: true
  opts[:timeout_kill_signal] = 'SIGINT' unless opts.has_key?(:timeout_kill_signal) # default: 'SIGINT'

  exit_code, duration = nil
  stdin, stdout, stderr, wait_thr, pid = nil

  if opts[:out]
    if opts[:out].is_a?(String)
      out = File.open(opts[:out], 'w')
      out.sync = true
    else
      out = opts[:out] # IO
    end
  else
    out = StringIO.new
  end

  if opts[:err]
    if opts[:err].is_a?(String)
      err = File.open(opts[:err], 'w')
      err.sync = true
    else
      err = opts[:err] # IO
    end
  else
    err = StringIO.new
  end

  if opts[:dry_run]
    command = build_command(env, cmd_array)
    return Result.new(:stdout => command, :stderr => '', :exit_code => 0, :duration => 0)
  end

  popen3_opts = self.popen3_opts(opts)

  lock_fd = file_lock(opts[:exclusive], opts[:exclusive_blocking]) if opts[:exclusive]
  begin
    ::Timeout.timeout(opts[:timeout], Frontkick::TimeoutLocal) do # nil is for no timeout
      duration = Benchmark.realtime do
        if opts[:popen2e]
          stdin, stdout, wait_thr = Open3.popen2e(env, *cmd_array, popen3_opts)
          out_thr = Thread.new {
            begin
              while true
                out.write stdout.readpartial(4096)
              end
            rescue EOFError
            end
          }
          stdin.close
          pid = wait_thr.pid

          yield(wait_thr) if block_given?

          out_thr.join
          exit_code = wait_thr.value.exitstatus
          process_wait(pid)
        else
          stdin, stdout, stderr, wait_thr = Open3.popen3(env, *cmd_array, popen3_opts)
          out_thr = Thread.new {
            begin
              while true
                out.write stdout.readpartial(4096)
              end
            rescue EOFError
            end
          }
          err_thr = Thread.new {
            begin
              while true
                err.write stderr.readpartial(4096)
              end
            rescue EOFError
            end
          }
          stdin.close
          pid = wait_thr.pid

          yield(wait_thr) if block_given?

          out_thr.join
          err_thr.join
          exit_code = wait_thr.value.exitstatus
          process_wait(pid)
        end
      end
    end
  rescue Frontkick::TimeoutLocal => e
    if opts[:timeout_kill]
      Process.kill(opts[:timeout_kill_signal], pid)
      exit_code = wait_thr.value.exitstatus
      process_wait(pid)
    end
    command = build_command(env, cmd_array)
    raise Frontkick::Timeout.new(pid, command, opts[:timeout_kill])
  ensure
    stdin.close if stdin and !stdin.closed?
    stdout.close if stdout and !stdout.closed?
    stderr.close if stderr and !stderr.closed?
    wait_thr.kill if wait_thr and !wait_thr.stop?
    lock_fd.flock(File::LOCK_UN) if lock_fd
    if opts[:out] and opts[:out].is_a?(String)
      out.close rescue nil
    end
    if opts[:err] and opts[:err].is_a?(String)
      err.close rescue nil
    end
  end

  Result.new(
    :stdout => opts[:out] ? opts[:out] : out.string,
    :stderr => opts[:err] ? opts[:err] : err.string,
    :exit_code => exit_code, :duration => duration
  )
end