teaching machines

Limiting Execution Time in a Shell, Part II

December 5, 2014 by . Filed under code, public.

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