Last active
August 29, 2015 14:08
-
-
Save kriansa/6de78a3fc6eb3e2013d4 to your computer and use it in GitHub Desktop.
Tiny helper to execute a external command with a timeout.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Open stdout and stderr streams and start external executable with timeout. | |
# The timeout is given in seconds. | |
# | |
# Block form: | |
# | |
# exec_with_timeout(timeout, [env,] cmd... [, opts]) {|stdout, stderr| | |
# stdout.read | |
# ... | |
# } | |
# | |
# Non-block form: | |
# | |
# stdout, stderr = exec_with_timeout(timeout, [env,] cmd... [, opts]) | |
# ... | |
# stdout.close # stdout and stderr should be closed explicitly in this form. | |
# stderr.close | |
# | |
# The parameters +cmd...+ is passed to Process.spawn. | |
# So a commandline string and list of argument strings can be accepted as follows. | |
# | |
# exec_with_timeout("echo a") {|stdout, stderr| ... } | |
# exec_with_timeout("echo", "a") {|stdout, stderr| ... } | |
# exec_with_timeout(["echo", "argv0"], "a") {|stdout, stderr| ... } | |
# | |
# If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn. | |
# | |
# exec_with_timeout("pwd", :chdir=>"/") {|i,o,e,t| | |
# p o.read.chomp #=> "/" | |
# } | |
# | |
# If the process takes longer than +timeout+, it will be killed and a Timeout::Error | |
# exception will be raised. | |
# | |
def exec_with_timeout(timeout = 5, *args) | |
err_read, err_write = IO.pipe | |
out_read, out_write = IO.pipe | |
pipes = { | |
out: out_write, | |
err: err_write, | |
} | |
if args.last.is_a?(Hash) | |
args.last.merge!(pipes) | |
else | |
args << pipes | |
end | |
pid = Process.spawn(*args) | |
begin | |
Timeout.timeout(timeout) do | |
Process.wait(pid) | |
# Close the pipes to be able to read | |
err_write.close | |
out_write.close | |
# Exec the block or return the read content | |
if block_given? | |
yield out_read, err_read | |
else | |
[out_read, err_read] | |
end | |
end | |
rescue Timeout::Error => e | |
Process.kill('TERM', pid) | |
Process.wait(pid) | |
raise e | |
ensure | |
# Close the pipes | |
out_write.close unless out_write.closed? | |
err_write.close unless err_write.closed? | |
out_read.close if block_given? and !out_read.closed? | |
err_read.close if block_given? and !err_read.closed? | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment