Limiting Execution Time in a Shell, Part II
A year ago, I thought I worked around the halting problem. I have students’ code that I need to grade, but sometimes they slip in an infinite loop. This really messes up automated grading. I wrote a shell script that would run their code for X seconds, killing their process if necessary. Sadly, I went to use that script again this semester, but it would hang when I invoked it across SSH:
ssh tester@localhost "cd student/folder && forn 30 theirs.exe"
The script works fine when not run across SSH. I don’t know what the problem is. Instead of asking the Internet for help, I just rewrote the script in Ruby, and it works just fine across SSH:
#!/usr/bin/env ruby
# ----------------------------------------------------------------------------
# FILE: forn
# AUTHOR: Chris Johnson
# DATE: Dec 03 2014
#
# Executes a command with a time limit. If the command takes longer than the
# specified number of seconds, it is killed and status 37 is returned.
# Otherwise, the exit status of the command is returned.
# ----------------------------------------------------------------------------
require 'timeout'
if ARGV.length < 1
puts "Usage: forn delay-in-seconds command arg1 arg2 ..."
exit 1
end
delay = ARGV[0].to_i
ARGV.shift
# Start command, but kill it if delay elapses.
pid = Process.spawn(ARGV.shelljoin)
begin
Timeout.timeout(delay) do
Process.wait pid
exit $?.exitstatus
end
rescue Timeout::Error
puts "Uh oh. Execution timed out after #{delay} second#{delay == 1 ? '' : 's'}."
Process.kill(:SIGTERM, pid)
exit 37
end