Class: Frontkick::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/frontkick/command.rb

Class Method Summary collapse

Class Method Details

.exec(cmd, opts = {}) ⇒ Object



6
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
# File 'lib/frontkick/command.rb', line 6

def self.exec(cmd, opts = {})
  stdout, stderr, exit_code, duration = nil
  stdin, out, err, wait_thr, pid = nil

  cmd_array = cmd.kind_of?(Array) ? cmd : [cmd]
  lock_fd = file_lock(opts[:exclusive]) if opts[:exclusive]
  begin
    timeout(opts[:timeout]) do # nil is for no timeout
      duration = Benchmark.realtime do
        stdin, out, err, wait_thr = Open3.popen3(*cmd_array)
        stdin.close
        pid = wait_thr.pid
        stdout = out.read
        stderr = err.read
        exit_code = wait_thr.value.exitstatus
        process_wait(pid)
      end
    end
  rescue Timeout::Error => e
    Process.kill('SIGINT', pid)
    exit_code = wait_thr.value.exitstatus
    process_wait(pid)
    duration = opts[:timeout]
    stdout = ""
    stderr = "pid:#{pid}\tcommand:#{cmd_array.join(' ')} is timeout!"
  ensure
    stdin.close if stdin and !stdin.closed?
    out.close if out and !out.closed?
    err.close if err and !err.closed?
    wait_thr.kill if wait_thr and !wait_thr.stop?
    lock_fd.flock(File::LOCK_UN) if lock_fd
  end
  
  Result.new(:stdout => stdout, :stderr => stderr, :exit_code => exit_code, :duration => duration)
end

.file_lock(lock_file) ⇒ Object

Use file lock to perfome exclusive operation

Parameters:

  • lock_file

    file path used to lock

Returns:

  • file descriptor

Raises:

  • Fontkick::Locked if locked



55
56
57
58
59
60
61
62
63
# File 'lib/frontkick/command.rb', line 55

def self.file_lock(lock_file)
  lock_fd = File.open(lock_file, File::RDWR|File::CREAT, 0644)
  success = lock_fd.flock(File::LOCK_EX|File::LOCK_NB)
  unless success
    lock_fd.flock(File::LOCK_UN)
    raise Frontkick::Locked
  end
  lock_fd
end

.process_wait(pid) ⇒ Object



42
43
44
45
46
47
48
# File 'lib/frontkick/command.rb', line 42

def self.process_wait(pid)
  begin
    pid, status = Process.waitpid2(pid) # wait child processes finish
  rescue Errno::ECHILD => e
    # no child process
  end
end