Module: ExecSimple

Defined in:
lib/exec-simple.rb

Constant Summary collapse

VERSION =
"0.1.2"

Class Method Summary collapse

Class Method Details

.run(cmd, log: nil, timeout: nil) ⇒ Object

Run a command. Optionally log results from stadard output and error with a logger object. Optionally set a timeout as a maximum time to wait until the process is killed. just the exit_code if a log-object was provided

Parameters:

  • cmd (String)

    The command to run

  • log (Logging::Logger) (defaults to: nil)

    An optional logger object to use for output

  • timeout (Int) (defaults to: nil)

    An optional timeout to wait until the process is killed.



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
# File 'lib/exec-simple.rb', line 16

def self.run cmd, log: nil, timeout: nil
  # run the command concurrently to manage timeouts

  out = ""
  err = ""
  stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
  # we don't need stdin
  stdin.close
  # run readers on stdin and stderr
  op = Concurrent::Promise.execute do
    while( not stdout.eof? )
      #stdout.wait_readable
      #cur = stdout.read
      cur = stdout.readpartial(4096)
      if log.nil? then out << cur else log.info cur.chomp end
    end
  end
  ep = Concurrent::Promise.execute do
    while( not stderr.eof? )
      #stdout.wait_readable
      #cur = stdout.read
      cur = stderr.readpartial(4096)
      if log.nil? then err << cur else log.error cur.chomp end
    end
  end
  # 
  pid = wait_thr[:pid]

  future = Concurrent::Future.execute do
      wait_thr.value
    end
  # get the future's value
  exit_code = future.value(timeout)
  exitstatus = if exit_code.nil? then nil else exit_code.exitstatus end
  
  # if we didn't yet get a value, kill the process
  if wait_thr.alive?
    Process.kill('TERM', pid)
    sleep 1
    # check if it terminated successfully
    if wait_thr.alive?
      # if not give it some more time
      sleep 3
      # and then kill it
      Process.kill('KILL', pid) if wait_thr.alive?
    end
  end
  
  # finishing up
  stdout.close
  stderr.close

  # get the results
  return exitstatus if not log.nil?
  [out, err, exitstatus]
end