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