diff --git a/judge/ev-exam/.gitignore b/judge/ev-exam/.gitignore deleted file mode 100644 --- a/judge/ev-exam/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -[^.]* - diff --git a/judge/ev/.gitignore b/judge/ev/.gitignore deleted file mode 100644 --- a/judge/ev/.gitignore +++ /dev/null @@ -1,1 +0,0 @@ -[^.]* diff --git a/judge/log/.gitignore b/judge/log/.gitignore deleted file mode 100644 --- a/judge/log/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -[^.]* - diff --git a/judge/result/.gitignore b/judge/result/.gitignore deleted file mode 100644 --- a/judge/result/.gitignore +++ /dev/null @@ -1,1 +0,0 @@ -[^.]* diff --git a/judge/scripts/config/.gitignore b/judge/scripts/config/.gitignore deleted file mode 100644 --- a/judge/scripts/config/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -env*.rb - diff --git a/judge/scripts/config/env_exam.rb.SAMPLE b/judge/scripts/config/env_exam.rb.SAMPLE deleted file mode 100644 --- a/judge/scripts/config/env_exam.rb.SAMPLE +++ /dev/null @@ -1,20 +0,0 @@ -# -# See documentation in lib/configuration.rb -# -Grader::Initializer.run do |config| - - config.problems_dir = GRADER_ROOT + "/../ev" - config.user_result_dir = GRADER_ROOT + "/../result" - - config.talkative = true - config.logging = true - config.log_dir = GRADER_ROOT + "/../log" - - config.report_grader = true - - config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" - config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" - config.test_request_problem_templates_dir = config.problems_dir + "/test_request" - - config.comment_report_style = :short -end diff --git a/judge/scripts/config/env_grading.rb.SAMPLE b/judge/scripts/config/env_grading.rb.SAMPLE deleted file mode 100644 --- a/judge/scripts/config/env_grading.rb.SAMPLE +++ /dev/null @@ -1,19 +0,0 @@ -# -# See documentation in lib/configuration.rb -# -Grader::Initializer.run do |config| - config.problems_dir = GRADER_ROOT + "/../ev" - config.user_result_dir = GRADER_ROOT + "/../result" - - config.talkative = true - config.logging = true - config.log_dir = GRADER_ROOT + "/../log" - - config.report_grader = true - - config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" - config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" - config.test_request_problem_templates_dir = config.problems_dir + "/test_request" - - config.comment_report_style = :full -end diff --git a/judge/scripts/config/env_test.rb.SAMPLE b/judge/scripts/config/env_test.rb.SAMPLE deleted file mode 100644 --- a/judge/scripts/config/env_test.rb.SAMPLE +++ /dev/null @@ -1,29 +0,0 @@ -# -# See documentation in lib/configuration.rb -# -Grader::Initializer.run do |config| - config.problems_dir = GRADER_ROOT + "/test/sandbox/ev" - config.user_result_dir = GRADER_ROOT + "/test/sandbox/result" - - config.talkative = false - - config.report_grader = false - - config.rails_env = 'test' - - config.comment_report_style = :full - - config.test_request_input_base_dir = GRADER_ROOT + "/test/data/test_request/input" - config.test_request_output_base_dir = GRADER_ROOT + "/test/sandbox/test_request/output" - config.test_request_problem_templates_dir = GRADER_ROOT + "/test/data/test_request/problems" - - # - # These options are for testing - # - class << config - attr_accessor :test_data_dir, :test_sandbox_dir - end - - config.test_data_dir = GRADER_ROOT + "/test/data" - config.test_sandbox_dir = GRADER_ROOT + "/test/sandbox" -end diff --git a/judge/scripts/config/environment.rb.SAMPLE b/judge/scripts/config/environment.rb.SAMPLE deleted file mode 100644 --- a/judge/scripts/config/environment.rb.SAMPLE +++ /dev/null @@ -1,10 +0,0 @@ -# Rails app directory -RAILS_ROOT = "/home/jittat/web_grader" - -GRADER_ROOT = "/home/jittat/grader/scripts" - -# This load all required codes -require File.join(File.dirname(__FILE__),'../lib/boot') - -# load the required environment file -require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb" diff --git a/judge/scripts/grader b/judge/scripts/grader deleted file mode 100755 --- a/judge/scripts/grader +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/ruby - -def stop_grader(id) - if id==:all - File.open(File.dirname(__FILE__) + "/stop.all",'w').close - else - File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close - end -end - -def check_stopfile - FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or - FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}") -end - -def clear_stopfile - if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}") - system("rm " + File.dirname(__FILE__) + "/stop.#{Process.pid}") - end -end - -def config - Grader::Configuration.get_instance -end - -def log_file_name - if !File.exists?(config.log_dir) - raise "Log directory does not exist: #{config.log_dir}" - end - config.log_dir + - "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}" -end - -def log(str) - if config.talkative - puts str - end - if config.logging - fp = File.open(log_file_name,"a") - fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}") - fp.close - end -end - -def display_manual - puts <= 1) and (ARGV[0]=='stop') - if ARGV.length==1 - puts "you should specify pid-list or 'all'" - display_manual - elsif (ARGV.length==2) and (ARGV[1]=='all') - stop_grader(:all) - puts "A global stop file ('stop.all') created." - puts "You should remove it manually later." - else - (1..ARGV.length-1).each do |i| - stop_grader(ARGV[i]) - end - puts "stop file(s) created" - end - exit(0) -end - -if check_stopfile - puts "Stop file exists. Terminated." - clear_stopfile - exit(0) -end - -grader_mode = 'queue' -if ARGV.length >= 1 - GRADER_ENV = ARGV[0] - if ARGV.length >=2 - grader_mode = ARGV[1] - end -else - GRADER_ENV = 'exam' -end - -puts "environment: #{GRADER_ENV}" -require File.join(File.dirname(__FILE__),'config/environment') - -# add grader_mode to config -# this is needed because method log needs it. TODO: clean this up -class << config - attr_accessor :grader_mode -end -config.grader_mode = grader_mode - -# reading rails environment -log 'Reading rails environment' - -RAILS_ENV = config.rails_env -require RAILS_ROOT + '/config/environment' - -# register grader process -if config.report_grader - grader_proc = GraderProcess.register(config.grader_hostname, - Process.pid, - grader_mode) -else - grader_proc = nil -end - -#set loggin environment -ENV['GRADER_LOGGING'] = log_file_name - -# register exit handler to report inactive, and terminated -at_exit do - if grader_proc!=nil - grader_proc.report_inactive - grader_proc.terminate - end -end - -# -# MAIN LOOP -# - -case grader_mode -when "queue", "test_request" - log "Grader: #{grader_mode}" - if grader_mode=="queue" - engine = Grader::Engine.new - else - engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new, - Grader::TestRequestReporter.new) - end - - runner = Grader::Runner.new(engine, grader_proc) - while true - - if check_stopfile # created by calling grader stop - clear_stopfile - log "stopped (with stop file)" - break - end - - if grader_mode=="queue" - task = runner.grade_oldest_task - else - task = runner.grade_oldest_test_request - end - if task==nil - sleep(1) - end - end - -when "prob" - engine = Grader::Engine.new - runner = Grader::Runner.new(engine, grader_proc) - - grader_proc.report_active if grader_proc!=nil - - ARGV.shift - ARGV.shift - - ARGV.each do |prob_name| - prob = Problem.find_by_name(prob_name) - if prob==nil - puts "cannot find problem: #{prob_name}" - else - runner.grade_problem(prob) - end - end - -when "sub" - engine = Grader::Engine.new - runner = Grader::Runner.new(engine, grader_proc) - - grader_proc.report_active if grader_proc!=nil - - ARGV.shift - ARGV.shift - - ARGV.each do |sub_id| - puts "Grading #{sub_id}" - begin - submission = Submission.find(sub_id.to_i) - rescue ActiveRecord::RecordNotFound - puts "Record not found" - submission = nil - end - - if submission!=nil - runner.grade_submission(submission) - end - end - -else - display_manual - exit(0) -end - diff --git a/judge/scripts/grader_id b/judge/scripts/grader_id deleted file mode 100755 --- a/judge/scripts/grader_id +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/ruby - -def talk(str) - if TALKATIVE - puts str - end -end - -def execute(command, error_message="") - if not system(command) - puts "ERROR: #{error_message}" - exit(127) - end -end - -def save_source(submission,dir,fname) - f = File.open("#{dir}/#{fname}","w") - f.write(submission.source) - f.close -end - -def call_judge(problem_home,language,problem_out_dir,fname) - ENV['PROBLEM_HOME'] = problem_home - Dir.chdir problem_out_dir - cmd = "#{problem_home}/script/judge #{language} #{fname}" -# puts "CMD: #{cmd}" - system(cmd) -end - -def read_result(test_result_dir) - cmp_msg_fname = "#{test_result_dir}/compiler_message" - cmp_msg = File.open(cmp_msg_fname).read - - result_fname = "#{test_result_dir}/result" - comment_fname = "#{test_result_dir}/comment" - if FileTest.exist?(result_fname) - result = File.open(result_fname).readline.to_i - comment = File.open(comment_fname).readline.chomp - return {:points => result, - :comment => comment, - :cmp_msg => cmp_msg} - else - return {:points => 0, - :comment => 'compile error', - :cmp_msg => cmp_msg} - end -end - -def save_result(submission,result) - submission.graded_at = Time.now - submission.points = result[:points] - submission.grader_comment = report_comment(result[:comment]) - submission.compiler_message = result[:cmp_msg] - submission.save -end - -def grade(submission_id) - sub = Submission.find(submission_id) - user = sub.user - problem = sub.problem - - language = sub.language.name - lang_ext = sub.language.ext - # FIX THIS - talk 'some hack on language' - if language == 'cpp' - language = 'c++' - end - - user_dir = "#{USER_RESULT_DIR}/#{user.login}" - Dir.mkdir(user_dir) if !FileTest.exist?(user_dir) - - problem_out_dir = "#{user_dir}/#{problem.name}" - Dir.mkdir(problem_out_dir) if !FileTest.exist?(problem_out_dir) - - problem_home = "#{PROBLEMS_DIR}/#{problem.name}" - source_name = "#{problem.name}.#{lang_ext}" - - save_source(sub,problem_out_dir,source_name) - call_judge(problem_home,language,problem_out_dir,source_name) - save_result(sub,read_result("#{problem_out_dir}/test-result")) -end - -def stop_grader - File.open(File.dirname(__FILE__) + '/stop','w') -end - -def check_stopfile - FileTest.exist?(File.dirname(__FILE__) + '/stop') -end - -def clear_stopfile - system("rm " + File.dirname(__FILE__) + '/stop') -end - -# reading environment and options -GRADER_ENV = 'exam' -puts "environment: #{GRADER_ENV}" -require File.dirname(__FILE__) + "/environment.rb" - -#main program -talk 'Reading rails environment' - -RAILS_ENV = 'development' -require RAILS_APP_DIR + '/config/environment' - -current_dir = `pwd` -grade(ARGV[0].to_i) - - diff --git a/judge/scripts/import_problem b/judge/scripts/import_problem deleted file mode 100755 --- a/judge/scripts/import_problem +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/ruby - -# import_problem: -# * creates a directory for a problem in the current directory, -# * copy testdata in the old format and create standard testcase config file - -require 'erb' -require 'fileutils' -require File.join(File.dirname(__FILE__),'lib/import_helper') - -def input_filename(dir,i) - "#{dir}/input-#{i}.txt" -end - -def answer_filename(dir,i) - "#{dir}/answer-#{i}.txt" -end - -def build_testrun_info_from_dir(num_testruns,importing_test_dir) - filenames = Dir["#{importing_test_dir}/*.in"].collect do |filename| - File.basename((/(.*)\.in/.match(filename))[1]) - end - build_testrun_info(num_testruns,filenames) -end - -def copy_testcase(importing_test_dir,fname,dir,i) - system("cp #{importing_test_dir}/#{fname}.in #{input_filename(dir,i)}") - system("cp #{importing_test_dir}/#{fname}.sol #{answer_filename(dir,i)}") -end - -def process_options(options) - i = 4 - while ii+1 - i += 1 - end - if ARGV[i]=='-m' - options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1 - i += 1 - end - i += 1 - end -end - -SCRIPT_DIR = File.dirname(__FILE__) - -# print usage -if (ARGV.length < 4) or (ARGV[3][0,1]=="-") - puts < 1, :mem_limit => 16} -process_options(options) - -testrun_info = build_testrun_info_from_dir(num_testruns, testcase_dir) - -# start working -puts "creating directories" - -system("mkdir #{problem}") -system("mkdir #{problem}/script") -system("mkdir #{problem}/test_cases") -#system("cp #{GRADER_DIR}/std-script/* #{problem}/script") - -puts "copying testcases" - -tr_num = 0 - -num_testcases = 0 - -testrun_info.each do |testrun| - tr_num += 1 - puts "testrun: #{tr_num}" - - testrun.each do |testcase_info| - testcase_num, testcase_fname = testcase_info - - puts "copy #{testcase_fname} to #{testcase_num}" - - system("mkdir #{problem}/test_cases/#{testcase_num}") - copy_testcase("#{testcase_dir}",testcase_fname,"#{problem}/test_cases/#{testcase_num}",testcase_num) - - num_testcases += 1 - end -end - -# generating all_tests.cfg -puts "generating testcase config file" - -template = File.open(SCRIPT_DIR + "/templates/all_tests.cfg.erb").read -all_test_cfg = ERB.new(template) - -cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") -cfg_file.puts all_test_cfg.result -cfg_file.close - - -# copy check script -if res = /^wrapper:(.*)$/.match(check_script) - # wrapper script - check_script_fname = res[1] - script_name = File.basename(check_script_fname) - check_wrapper_template = File.open(SCRIPT_DIR + "/templates/check_wrapper").read - check_wrapper = ERB.new(check_wrapper_template) - - check_file = File.open("#{problem}/script/check","w") - check_file.puts check_wrapper.result - check_file.close - - File.chmod(0755,"#{problem}/script/check") - - system("cp #{check_script_fname} #{problem}/script/#{script_name}") -else - if File.exists?(SCRIPT_DIR + "/templates/check.#{check_script}") - check_script_fname = SCRIPT_DIR + "/templates/check.#{check_script}" - else - check_script_fname = check_script - end - system("cp #{check_script_fname} #{problem}/script/check") -end - -# generating test_request directory -puts "generating test_request template" -FileUtils.mkdir_p("test_request/#{problem}/script") -FileUtils.mkdir_p("test_request/#{problem}/test_cases/1") - -template = File.open(SCRIPT_DIR + "/templates/test_request_all_tests.cfg.erb").read -test_request_all_test_cfg = ERB.new(template) - -cfg_file = File.open("test_request/#{problem}/test_cases/all_tests.cfg","w") -cfg_file.puts test_request_all_test_cfg.result -cfg_file.close - -system("cp #{SCRIPT_DIR}/templates/check_empty test_request/#{problem}/script/check") -system("cp #{SCRIPT_DIR}/templates/answer-1.txt test_request/#{problem}/test_cases/1") - -puts "done" diff --git a/judge/scripts/lib/boot.rb b/judge/scripts/lib/boot.rb deleted file mode 100644 --- a/judge/scripts/lib/boot.rb +++ /dev/null @@ -1,10 +0,0 @@ - -require File.join(File.dirname(__FILE__), 'configuration') -require File.join(File.dirname(__FILE__), 'initializer') - -require File.join(File.dirname(__FILE__), 'submission_helper') -require File.join(File.dirname(__FILE__), 'test_request_helper') - -require File.join(File.dirname(__FILE__), 'engine') -require File.join(File.dirname(__FILE__), 'runner') - diff --git a/judge/scripts/lib/configuration.rb b/judge/scripts/lib/configuration.rb deleted file mode 100644 --- a/judge/scripts/lib/configuration.rb +++ /dev/null @@ -1,84 +0,0 @@ -module Grader - - # This singleton class holds basic configurations for grader. When - # running in each mode, grader uses resources from different - # directories and outputs differently. Usually the attributes name - # are descriptive; below we explain more on each attributes. - class Configuration - # Rails' environment: "development", "production" - attr_accessor :rails_env - - # Grader looks for problem [prob] in problem_dir/[prob], and store - # execution results for submission [x] of user [u] in directory - # user_result_dir/[u]/[x] - attr_accessor :problems_dir - attr_accessor :user_result_dir - - # If report_grader=true, the grader would add a row in model - # GraderProcess. It would report itself with grader_hostname and - # process id. - attr_accessor :report_grader - attr_accessor :grader_hostname - - # If talkative=true, grader would report status to console. If - # logging=true, grader would report status to a log file located - # in log_dir, in a file name mode.options.pid. TODO: defined - # log file naming. - attr_accessor :talkative - attr_accessor :logging - attr_accessor :log_dir - - # These are directories related to the test interface. - attr_accessor :test_request_input_base_dir - attr_accessor :test_request_output_base_dir - attr_accessor :test_request_problem_templates_dir - - # Comment received from the grading script will be filtered - # through Configuration#report_comment. How this method behave - # depends on this option; right now only two formats, :short and - # :long - attr_accessor :comment_report_style - - def report_comment(comment) - case comment_report_style - when :short - if comment.chomp =~ /^[\[\]P]+$/ # all P's - 'passed' - elsif comment.chomp =~ /[Cc]ompil.*[Ee]rror/ - 'compilation error' - else - 'failed' - end - - when :full - comment.chomp - end - end - - # Codes for singleton - private_class_method :new - - @@instance = nil - - def self.get_instance - if @@instance==nil - @@instance = new - end - @@instance - end - - private - def initialize - @talkative = false - @log_file = nil - @report_grader = false - @grader_hostname = `hostname`.chomp - - @rails_env = 'development' - - @comment_report_style = :full - end - - end - -end diff --git a/judge/scripts/lib/dir_init.rb b/judge/scripts/lib/dir_init.rb deleted file mode 100644 --- a/judge/scripts/lib/dir_init.rb +++ /dev/null @@ -1,126 +0,0 @@ -require 'ftools' - -# DirInit::Manager handles directory initialization and clean-up when -# there are many concurrent processes that wants to modify the -# directory in the same way. -# -# An example usage is when each process wants to copy some temporary -# files to the directory and delete these files after finishing its -# job. Problems may occur when the first process delete the files -# while the second process is still using the files. -# -# This library maintain a reference counter on the processes using the -# directory. It locks the dir to manage critical section when -# updating the reference counter. -# -# Example usage: -# -# dman = DirInit::Manager.new("mydir") -# -# dman.setup do -# # do some initialization -# end -# -# #... do anything you want -# -# dman.teardown do -# # clean up -# end -# -# DirInit::Manager ensures that the block passed to setup -# only runs once by the first process in the concurrent dir usage and -# block passed to teardown runs once by the last process in -# that concurrent activities leaving that dir. - -module DirInit - - class Manager - - def initialize(dir_name, usage_filename='.usage_counter') - @dir_name = dir_name - @usage_filename = usage_filename - end - - # Check if someone has initialized the dir. If not, call block. - - def setup # :yields: block - dir = File.new(@dir_name) - dir.flock(File::LOCK_EX) - begin - counter_filename = get_counter_filename - if File.exist? counter_filename - # someone is here - f = File.new(counter_filename,"r+") - counter = f.read.to_i - f.seek(0) - f.write("#{counter+1}\n") - f.close - else - # i'm the first, create the counter file - counter = 0 - f = File.new(counter_filename,"w") - f.write("1\n") - f.close - end - - # if no one is here - if counter == 0 - if block_given? - yield - end - end - - rescue - raise - - ensure - # make sure it unlock the directory - dir.flock(File::LOCK_UN) - end - end - - # Check if I am the last one using the dir. If true, call block. - - def teardown - dir = File.new(@dir_name) - dir.flock(File::LOCK_EX) - begin - counter_filename = get_counter_filename - if File.exist? counter_filename - # someone is here - f = File.new(counter_filename,"r+") - counter = f.read.to_i - f.seek(0) - f.write("#{counter-1}\n") - f.close - - if counter == 1 - # i'm the last one - - File.delete(counter_filename) - if block_given? - yield - end - end - else - # This is BAD - raise "Error: reference count missing" - end - - rescue - raise - - ensure - # make sure it unlock the directory - dir.flock(File::LOCK_UN) - end - end - - protected - - def get_counter_filename - return File.join(@dir_name,@usage_filename) - end - - end -end diff --git a/judge/scripts/lib/engine.rb b/judge/scripts/lib/engine.rb deleted file mode 100644 --- a/judge/scripts/lib/engine.rb +++ /dev/null @@ -1,175 +0,0 @@ -require 'fileutils' -require 'ftools' -require 'lib/dir_init' - -module Grader - - # - # A grader engine grades a submission, against anything: a test - # data, or a user submitted test data. It uses two helpers objects: - # room_maker and reporter. - # - class Engine - - attr_writer :room_maker - attr_writer :reporter - - def initialize(room_maker=nil, reporter=nil) - @config = Grader::Configuration.get_instance - - @room_maker = room_maker || Grader::SubmissionRoomMaker.new - @reporter = reporter || Grader::SubmissionReporter.new - end - - # takes a submission, asks room_maker to produce grading directories, - # calls grader scripts, and asks reporter to save the result - def grade(submission) - current_dir = `pwd`.chomp - - user = submission.user - problem = submission.problem - - # TODO: will have to create real exception for this - if user==nil or problem == nil - @reporter.report_error(submission,"Grading error: problem with submission") - #raise "engine: user or problem is nil" - end - - # TODO: this is another hack so that output only task can be judged - if submission.language!=nil - language = submission.language.name - lang_ext = submission.language.ext - else - language = 'c' - lang_ext = 'c' - end - - # FIX THIS - talk 'some hack on language' - if language == 'cpp' - language = 'c++' - end - - # COMMENT: should it be only source.ext? - if problem!=nil - source_name = "#{problem.name}.#{lang_ext}" - else - source_name = "source.#{lang_ext}" - end - - begin - grading_dir = @room_maker.produce_grading_room(submission) - @room_maker.save_source(submission,source_name) - problem_home = @room_maker.find_problem_home(submission) - - # puts "GRADING DIR: #{grading_dir}" - # puts "PROBLEM DIR: #{problem_home}" - - dinit = DirInit::Manager.new(problem_home) - - dinit.setup do - copy_log = copy_script(problem_home) - save_copy_log(problem_home,copy_log) - end - - call_judge(problem_home,language,grading_dir,source_name) - - @reporter.report(submission,"#{grading_dir}/test-result") - - dinit.teardown do - copy_log = load_copy_log(problem_home) - clear_copy_log(problem_home) - clear_script(copy_log,problem_home) - end - - rescue RuntimeError => msg - @reporter.report_error(submission,"Grading error: #{msg}") - - ensure - @room_maker.clean_up(submission) - Dir.chdir(current_dir) # this is really important - end - end - - protected - - def talk(str) - if @config.talkative - puts str - end - end - - def call_judge(problem_home,language,grading_dir,fname) - ENV['PROBLEM_HOME'] = problem_home - - talk grading_dir - Dir.chdir grading_dir - cmd = "#{problem_home}/script/judge #{language} #{fname}" - talk "CMD: #{cmd}" - system(cmd) - end - - def get_std_script_dir - GRADER_ROOT + '/std-script' - end - - def copy_script(problem_home) - script_dir = "#{problem_home}/script" - std_script_dir = get_std_script_dir - - raise "std-script directory not found" if !FileTest.exist?(std_script_dir) - - scripts = Dir[std_script_dir + '/*'] - - copied = [] - - scripts.each do |s| - fname = File.basename(s) - if !FileTest.exist?("#{script_dir}/#{fname}") - copied << fname - system("cp #{s} #{script_dir}") - end - end - - return copied - end - - def copy_log_filename(problem_home) - return File.join(problem_home, '.scripts_copied') - end - - def save_copy_log(problem_home, log) - f = File.new(copy_log_filename(problem_home),"w") - log.each do |fname| - f.write("#{fname}\n") - end - f.close - end - - def load_copy_log(problem_home) - f = File.new(copy_log_filename(problem_home),"r") - log = [] - f.readlines.each do |line| - log << line.strip - end - f.close - log - end - - def clear_copy_log(problem_home) - File.delete(copy_log_filename(problem_home)) - end - - def clear_script(log,problem_home) - log.each do |s| - system("rm #{problem_home}/script/#{s}") - end - end - - def mkdir_if_does_not_exist(dirname) - Dir.mkdir(dirname) if !FileTest.exist?(dirname) - end - - end - -end diff --git a/judge/scripts/lib/import_helper.rb b/judge/scripts/lib/import_helper.rb deleted file mode 100644 --- a/judge/scripts/lib/import_helper.rb +++ /dev/null @@ -1,28 +0,0 @@ - -def filter_filename_for_testrun(testrun, filename_list) - l = [] - regex = Regexp.new("^(#{testrun}[a-z]*|#{testrun}-.*)$") - filename_list.each do |filename| - if regex.match(filename) - l << filename - end - end - l -end - -def build_testrun_info(num_testruns, input_filename_list) - info = [] - num_testcases = 0 - num_testruns.times do |i| - r = i+1 - testrun_info = [] - filenames = filter_filename_for_testrun(r, input_filename_list) - filenames.each do |fname| - num_testcases += 1 - testrun_info << [num_testcases,fname] - end - info << testrun_info - end - info -end - diff --git a/judge/scripts/lib/initializer.rb b/judge/scripts/lib/initializer.rb deleted file mode 100644 --- a/judge/scripts/lib/initializer.rb +++ /dev/null @@ -1,13 +0,0 @@ - -module Grader - - class Initializer - - def self.run(&block) - config = Grader::Configuration.get_instance - yield config - end - - end - -end diff --git a/judge/scripts/lib/runner.rb b/judge/scripts/lib/runner.rb deleted file mode 100644 --- a/judge/scripts/lib/runner.rb +++ /dev/null @@ -1,58 +0,0 @@ -# -# A runner drives the engine into various tasks. -# - -module Grader - - class Runner - - def initialize(engine, grader_process=nil) - @engine = engine - @grader_process = grader_process - end - - def grade_oldest_task - task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) - if task!=nil - @grader_process.report_active(task) if @grader_process!=nil - - submission = Submission.find(task.submission_id) - @engine.grade(submission) - task.status_complete! - @grader_process.report_inactive(task) if @grader_process!=nil - end - return task - end - - def grade_problem(problem) - users = User.find(:all) - users.each do |u| - puts "user: #{u.login}" - last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id) - if last_sub!=nil - @engine.grade(last_sub) - end - end - end - - def grade_submission(submission) - puts "Submission: #{submission.id} by #{submission.user.full_name}" - @engine.grade(submission) - end - - def grade_oldest_test_request - test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING) - if test_request!=nil - @grader_process.report_active(test_request) if @grader_process!=nil - - @engine.grade(test_request) - test_request.status_complete! - @grader_process.report_inactive(test_request) if @grader_process!=nil - end - return test_request - end - - end - -end - diff --git a/judge/scripts/lib/submission_helper.rb b/judge/scripts/lib/submission_helper.rb deleted file mode 100644 --- a/judge/scripts/lib/submission_helper.rb +++ /dev/null @@ -1,123 +0,0 @@ -module Grader - - class SubmissionRoomMaker - def initialize - @config = Grader::Configuration.get_instance - end - - def produce_grading_room(submission) - user = submission.user - problem = submission.problem - grading_room = "#{@config.user_result_dir}/" + - "#{user.login}/#{problem.name}/#{submission.id}" - - FileUtils.mkdir_p(grading_room) - grading_room - end - - def find_problem_home(submission) - problem = submission.problem - "#{@config.problems_dir}/#{problem.name}" - end - - def save_source(submission,source_name) - dir = self.produce_grading_room(submission) - f = File.open("#{dir}/#{source_name}","w") - f.write(submission.source) - f.close - end - - def clean_up(submission) - end - end - - class SubmissionReporter - def initialize - @config = Grader::Configuration.get_instance - end - - def report(sub,test_result_dir) - save_result(sub,read_result(test_result_dir)) - end - - def report_error(sub,msg) - save_result(sub,{:points => 0, - :comment => "Grading error: #{msg}" }) - end - - protected - def read_result(test_result_dir) - cmp_msg_fname = "#{test_result_dir}/compiler_message" - if FileTest.exist?(cmp_msg_fname) - cmp_file = File.open(cmp_msg_fname) - cmp_msg = cmp_file.read - cmp_file.close - else - cmp_msg = "" - end - - result_fname = "#{test_result_dir}/result" - comment_fname = "#{test_result_dir}/comment" - if FileTest.exist?(result_fname) - comment = "" - begin - result_file = File.open(result_fname) - result = result_file.readline.to_i - result_file.close - rescue - result = 0 - comment = "error reading result file." - end - - begin - comment_file = File.open(comment_fname) - comment += comment_file.readline.chomp - comment_file.close - rescue - comment += "" - end - - return {:points => result, - :comment => comment, - :cmp_msg => cmp_msg} - else - if FileTest.exist?("#{test_result_dir}/a.out") - return {:points => 0, - :comment => 'error during grading', - :cmp_msg => cmp_msg} - else - return {:points => 0, - :comment => 'compilation error', - :cmp_msg => cmp_msg} - end - end - end - - def save_result(submission,result) - problem = submission.problem - submission.graded_at = Time.now.gmtime - points = result[:points] - submission.points = points - comment = @config.report_comment(result[:comment]) - - # - # TODO: FIX THIS MESSAGE - # - if problem == nil - submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)' - elsif points == problem.full_score - #submission.grader_comment = 'PASSED: ' + comment - submission.grader_comment = comment - elsif result[:comment].chomp =~ /^[\[\]P]+$/ - submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)' - else - #submission.grader_comment = 'FAILED: ' + comment - submission.grader_comment = comment - end - submission.compiler_message = result[:cmp_msg] or '' - submission.save - end - - end - -end diff --git a/judge/scripts/lib/test_request_helper.rb b/judge/scripts/lib/test_request_helper.rb deleted file mode 100644 --- a/judge/scripts/lib/test_request_helper.rb +++ /dev/null @@ -1,242 +0,0 @@ -# -# This part contains various test_request helpers for interfacing -# with Grader::Engine. There are TestRequestRoomMaker and -# TestRequestReporter. - -module Grader - - # - # A TestRequestRoomMaker is a helper object for Engine - # - finds grading room: in user_result_dir/(user)/test_request/ ... - # - prepare problem configuration for grading --- basically it copy - # all config files, and copy user's input into the testcase - # directory. First, it finds the template from problem template - # directory; if it can't find a template, it'll use the template - # from default template. - class TestRequestRoomMaker - def initialize - @config = Grader::Configuration.get_instance - end - - def produce_grading_room(test_request) - grading_room = grading_room_dir(test_request) - FileUtils.mkdir_p(grading_room) - - # - # Also copy additional submitted file to this directory as well. - # The program would see this file only if it is copied - # to the sandbox directory later. The run script should do it. - # - if FileTest.exists?("#{test_request.input_file_name}.files") - cmd = "cp #{test_request.input_file_name}.files/* #{grading_room}" - system(cmd) - end - - grading_room - end - - def find_problem_home(test_request) - problem_name = test_request.problem_name - - template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name - - raise "Test Request: error template not found" if !File.exists?(template_dir) - - problem_home = problem_home_dir(test_request) - FileUtils.mkdir_p(problem_home) - - copy_problem_template(template_dir,problem_home) - link_input_file(test_request,problem_home) - - problem_home - end - - def save_source(test_request,source_name) - dir = self.produce_grading_room(test_request) - submission = test_request.submission - f = File.open("#{dir}/#{source_name}","w") - f.write(submission.source) - f.close - end - - def clean_up(test_request) - problem_home = problem_home_dir(test_request) - remove_data_files(problem_home) - end - - protected - def grading_room_dir(test_request) - problem_name = test_request.problem_name - user = test_request.user - grading_room = "#{@config.user_result_dir}" + - "/#{user.login}/test_request" + - "/#{problem_name}/#{test_request.id}" - grading_room - end - - def problem_home_dir(test_request) - problem_name = test_request.problem_name - user = test_request.user - "#{@config.user_result_dir}" + - "/#{user.login}/test_request/#{problem_name}" - end - - def copy_problem_template(template_dir,problem_home) - cmd = "cp -R #{template_dir}/* #{problem_home}" - system_and_raise_when_fail(cmd,"Test Request: cannot copy problem template") - end - - def link_input_file(test_request,problem_home) - input_fname = "#{test_request.input_file_name}" - if !File.exists?(input_fname) - raise "Test Request: input file not found." - end - - input_fname_problem_home = "#{problem_home}/test_cases/1/input-1.txt" - if File.exists?(input_fname_problem_home) - FileUtils.rm([input_fname_problem_home], :force => true) - end - - cmd = "ln -s #{input_fname} #{input_fname_problem_home}" - system_and_raise_when_fail(cmd,"Test Request: cannot link input file") - end - - def remove_data_files(problem_home) - if File.exists?("#{problem_home}/test_cases/1/input-1.txt") - cmd = "rm #{problem_home}/test_cases/1/*" - system_and_raise_when_fail(cmd,"Test Request: cannot remove data files") - end - end - - def system_and_raise_when_fail(cmd,msg) - if !system(cmd) - raise msg - end - end - - end - - class TestRequestReporter - def initialize - @config = Grader::Configuration.get_instance - end - - def report(test_request,test_result_dir) - save_result(test_request,read_result(test_result_dir)) - end - - def report_error(test_request, msg) - save_result(test_request, {:running_stat => { - :msg => "#{msg}", - :running_time => nil, - :exit_status => "Some error occured. Program did not run", - :memory_usage => nil - }}) - end - - protected - def read_result(test_result_dir) - # TODO: - cmp_msg_fname = "#{test_result_dir}/compiler_message" - cmp_file = File.open(cmp_msg_fname) - cmp_msg = cmp_file.read - cmp_file.close - - result_file_name = "#{test_result_dir}/1/result" - - if File.exists?(result_file_name) - output_file_name = "#{test_result_dir}/1/output.txt" - results = File.open("#{test_result_dir}/1/result").readlines - stat = extract_running_stat(results) - - return { - :output_file_name => output_file_name, - :running_stat => stat, - :comment => "", - :cmp_msg => cmp_msg} - else - return { - :running_stat => nil, - :comment => "Compilation error", - :cmp_msg => cmp_msg} - end - end - - def extract_running_stat(results) - running_stat_line = results[-1] - - # extract exit status line - run_stat = "" - if !(/[Cc]orrect/.match(results[0])) - run_stat = results[0].chomp - else - run_stat = 'Program exited normally' - end - - # extract running time - if res = /r(.*)u(.*)s/.match(running_stat_line) - seconds = (res[1].to_f + res[2].to_f) - time_stat = "Time used: #{seconds} sec." - else - seconds = nil - time_stat = "Time used: n/a sec." - end - - # extract memory usage - if res = /s(.*)m/.match(running_stat_line) - memory_used = res[1].to_i - else - memory_used = -1 - end - - return { - :msg => "#{run_stat}\n#{time_stat}", - :running_time => seconds, - :exit_status => run_stat, - :memory_usage => memory_used - } - end - - def save_result(test_request,result) - if result[:output_file_name]!=nil - test_request.output_file_name = link_output_file(test_request, - result[:output_file_name]) - end - test_request.graded_at = Time.now - test_request.compiler_message = (result[:cmp_msg] or '') - test_request.grader_comment = (result[:comment] or '') - if result[:running_stat]!=nil - test_request.running_stat = (result[:running_stat][:msg] or '') - test_request.running_time = (result[:running_stat][:running_time] or nil) - test_request.exit_status = result[:running_stat][:exit_status] - test_request.memory_usage = result[:running_stat][:memory_usage] - else - test_request.running_stat = '' - end - test_request.save - end - - protected - def link_output_file(test_request, fname) - target_file_name = random_output_file_name(test_request.user, - test_request.problem) - FileUtils.mkdir_p(File.dirname(target_file_name)) - cmd = "ln -s #{fname} #{target_file_name}" - if !system(cmd) - raise "TestRequestReporter: cannot move output file" - end - return target_file_name - end - - def random_output_file_name(user,problem) - problem_name = TestRequest.name_of(problem) - begin - tmpname = "#{@config.test_request_output_base_dir}" + - "/#{user.login}/#{problem_name}/#{rand(10000)}" - end while File.exists?(tmpname) - tmpname - end - - end - -end diff --git a/judge/scripts/new_problem b/judge/scripts/new_problem deleted file mode 100755 --- a/judge/scripts/new_problem +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/ruby - -# new_problem: -# * creates a directory for a problem in the current directory, -# * create standard testcase config file - -require 'erb' - -def process_options(options) - i = 2 - while ii+1 - i += 1 - end - if ARGV[i]=='-m' - options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1 - i += 1 - end - i += 1 - end -end - - -puts "This script is out of dated, shall be fixed soon" -puts "Right now, you can create raw_ev and import" -exit(0) - -GRADER_DIR = File.dirname(__FILE__) - -# print usage -if ARGV.length < 2 - puts < 1, :mem_limit => 16} -process_options(options) - -# start working -puts "creating directories" - -system("mkdir #{problem}") -system("mkdir #{problem}/script") -system("mkdir #{problem}/test_cases") - -puts "creating testcases directories" - -1.upto(num_testcases) do |i| - system("mkdir #{problem}/test_cases/#{i}") -end - -# generating all_tests.cfg -puts "generating testcase config file" - -template = File.open(File.dirname(__FILE__) + "/templates/all_tests.cfg.erb").read -all_test_cfg = ERB.new(template) - -cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") -cfg_file.puts all_test_cfg.result -cfg_file.close - -puts "done" diff --git a/judge/scripts/std-script/.gitignore b/judge/scripts/std-script/.gitignore deleted file mode 100644 --- a/judge/scripts/std-script/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -box - diff --git a/judge/scripts/std-script/box.cc b/judge/scripts/std-script/box.cc deleted file mode 100644 --- a/judge/scripts/std-script/box.cc +++ /dev/null @@ -1,684 +0,0 @@ -/* - * A Simple Testing Sandbox - * - * (c) 2001--2004 Martin Mares - */ - -#define _LARGEFILE64_SOURCE -//#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NONRET __attribute__((noreturn)) -#define UNUSED __attribute__((unused)) - -static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */ -static double timeout; -static int pass_environ; -static int use_wall_clock; -static int file_access; -static int verbose; -static int memory_limit; -static int allow_times; -static char *redir_stdin, *redir_stdout; -static char *set_cwd; - -static pid_t box_pid; -static int is_ptraced; -static volatile int timer_tick; -static time_t start_time; -static int ticks_per_sec; -static int page_size; - -#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0 -/* glibc 2.1 or newer -> has lseek64 */ -#define long_seek(f,o,w) lseek64(f,o,w) -#else -/* Touching clandestine places in glibc */ -extern loff_t llseek(int fd, loff_t pos, int whence); -#define long_seek(f,o,w) llseek(f,o,w) -#endif - -int max_mem_used = 0; - -void print_running_stat(double wall_time, - double user_time, - double system_time, - int mem_usage) -{ - fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n", - wall_time, user_time, system_time, mem_usage); -} - -static void NONRET -box_exit(void) -{ - if (box_pid > 0) { - if (is_ptraced) - ptrace(PTRACE_KILL, box_pid); - kill(-box_pid, SIGKILL); - kill(box_pid, SIGKILL); - } - - struct timeval total; - int wall; - struct rusage rus; - int stat; - pid_t p; - - // wait so that we can get information - p = wait4(box_pid, &stat, WUNTRACED, &rus); - if (p < 0) { - fprintf(stderr,"wait4: error\n"); - print_running_stat(0,0,0,max_mem_used); - } else if (p != box_pid) { - fprintf(stderr,"wait4: unknown pid %d exited!\n", p); - print_running_stat(0,0,0,max_mem_used); - } else { - if (!WIFEXITED(stat)) - fprintf(stderr,"wait4: unknown status\n"); - struct timeval total; - int wall; - wall = time(NULL) - start_time; - timeradd(&rus.ru_utime, &rus.ru_stime, &total); - - print_running_stat((double)wall, - (double) rus.ru_utime.tv_sec + - ((double) rus.ru_utime.tv_usec/1000000.0), - (double) rus.ru_stime.tv_sec + - ((double) rus.ru_stime.tv_usec/1000000.0), - max_mem_used); - } - exit(1); -} - -static void NONRET __attribute__((format(printf,1,2))) -die(const char *msg, ...) -{ - va_list args; - va_start(args, msg); - vfprintf(stderr, msg, args); - fputc('\n', stderr); - box_exit(); -} - -static void __attribute__((format(printf,1,2))) -log(const char *msg, ...) -{ - va_list args; - va_start(args, msg); - if (verbose) - { - vfprintf(stderr, msg, args); - fflush(stderr); - } - va_end(args); -} - -static void -valid_filename(unsigned long addr) -{ - char namebuf[4096], *p, *end; - static int mem_fd; - - if (!file_access) - die("File access forbidden."); - if (file_access >= 9) - return; - - if (!mem_fd) - { - sprintf(namebuf, "/proc/%d/mem", (int) box_pid); - mem_fd = open(namebuf, O_RDONLY); - if (mem_fd < 0) - die("open(%s): %m", namebuf); - } - p = end = namebuf; - do - { - if (p >= end) - { - int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1)); - int l = namebuf + sizeof(namebuf) - end; - if (l > remains) - l = remains; - if (!l) - die("Access to file with name too long."); - if (long_seek(mem_fd, addr, SEEK_SET) < 0) - die("long_seek(mem): %m"); - remains = read(mem_fd, end, l); - if (remains < 0) - die("read(mem): %m"); - if (!remains) - die("Access to file with name out of memory."); - end += l; - addr += l; - } - } - while (*p++); - - log("[%s] ", namebuf); - if (file_access >= 3) - return; - if (!strchr(namebuf, '/') && strcmp(namebuf, "..")) - return; - if (file_access >= 2) - { - if ((!strncmp(namebuf, "/etc/", 5) || - !strncmp(namebuf, "/lib/", 5) || - !strncmp(namebuf, "/usr/lib/", 9)) - && !strstr(namebuf, "..")) - return; - if (!strcmp(namebuf, "/dev/null") || - !strcmp(namebuf, "/dev/zero") || - !strcmp(namebuf, "/proc/meminfo") || - !strcmp(namebuf, "/proc/self/stat") || - !strncmp(namebuf, "/usr/share/zoneinfo/", 20)) - return; - } - die("Forbidden access to file `%s'.", namebuf); -} - -static int -valid_syscall(struct user *u) -{ - switch (u->regs.orig_eax) - { - case __NR_execve: - { - static int exec_counter; - return !exec_counter++; - } - case __NR_open: - case __NR_creat: - case __NR_unlink: - case __NR_oldstat: - case __NR_access: - case __NR_oldlstat: - case __NR_truncate: - case __NR_stat: - case __NR_lstat: - case __NR_truncate64: - case __NR_stat64: - case __NR_lstat64: - valid_filename(u->regs.ebx); - return 1; - case __NR_exit: - case __NR_read: - case __NR_write: - case __NR_close: - case __NR_lseek: - case __NR_getpid: - case __NR_getuid: - case __NR_oldfstat: - case __NR_dup: - case __NR_brk: - case __NR_getgid: - case __NR_geteuid: - case __NR_getegid: - case __NR_dup2: - case __NR_ftruncate: - case __NR_fstat: - case __NR_personality: - case __NR__llseek: - case __NR_readv: - case __NR_writev: - case __NR_getresuid: -#ifdef __NR_pread64 - case __NR_pread64: - case __NR_pwrite64: -#else - case __NR_pread: - case __NR_pwrite: -#endif - case __NR_ftruncate64: - case __NR_fstat64: - case __NR_fcntl: - case __NR_fcntl64: - case __NR_mmap: - case __NR_munmap: - case __NR_ioctl: - case __NR_uname: - case 252: - case 243: -// added for free pascal - case __NR_ugetrlimit: - case __NR_readlink: - return 1; - // case __NR_time: - case __NR_alarm: - // case __NR_pause: - case __NR_signal: - case __NR_fchmod: - case __NR_sigaction: - case __NR_sgetmask: - case __NR_ssetmask: - case __NR_sigsuspend: - case __NR_sigpending: - case __NR_getrlimit: - case __NR_getrusage: - case __NR_gettimeofday: - case __NR_select: - case __NR_readdir: - case __NR_setitimer: - case __NR_getitimer: - case __NR_sigreturn: - case __NR_mprotect: - case __NR_sigprocmask: - case __NR_getdents: - case __NR_getdents64: - case __NR__newselect: - case __NR_fdatasync: - case __NR_mremap: - case __NR_poll: - case __NR_getcwd: - case __NR_nanosleep: - case __NR_rt_sigreturn: - case __NR_rt_sigaction: - case __NR_rt_sigprocmask: - case __NR_rt_sigpending: - case __NR_rt_sigtimedwait: - case __NR_rt_sigqueueinfo: - case __NR_rt_sigsuspend: - case __NR_mmap2: - case __NR__sysctl: - return (filter_syscalls == 1); - case __NR_times: - case __NR_time: - return allow_times; - case __NR_kill: - if (u->regs.ebx == box_pid) - die("Commited suicide by signal %d.", (int)u->regs.ecx); - return 0; - default: - return 0; - } -} - -static void -signal_alarm(int unused UNUSED) -{ - /* Time limit checks are synchronous, so we only schedule them there. */ - timer_tick = 1; - - //NOTE: do not use alarm, changed to setitimer for precision - // alarm(1); -} - -static void -signal_int(int unused UNUSED) -{ - /* Interrupts are fatal, so no synchronization requirements. */ - die("Interrupted."); -} - -static void -check_timeout(void) -{ - double sec; - - if (use_wall_clock) - sec = (double)(time(NULL) - start_time); - else - { - char buf[4096], *x; - int c, utime, stime; - static int proc_status_fd; - if (!proc_status_fd) - { - sprintf(buf, "/proc/%d/stat", (int) box_pid); - proc_status_fd = open(buf, O_RDONLY); - if (proc_status_fd < 0) - die("open(%s): %m", buf); - } - lseek(proc_status_fd, 0, SEEK_SET); - if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0) - die("read on /proc/$pid/stat: %m"); - if (c >= (int) sizeof(buf) - 1) - die("/proc/$pid/stat too long"); - buf[c] = 0; - x = buf; - while (*x && *x != ' ') - x++; - while (*x == ' ') - x++; - if (*x++ != '(') - die("proc syntax error 1"); - while (*x && (*x != ')' || x[1] != ' ')) - x++; - while (*x == ')' || *x == ' ') - x++; - if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2) - die("proc syntax error 2"); - //printf("%s - %d\n",x,ticks_per_sec); - sec = ((double)(utime + stime))/(double)ticks_per_sec; - } - if (verbose > 1) - fprintf(stderr, "[timecheck: %d seconds]\n", sec); - if (sec > timeout) { - die("Time limit exceeded.",sec,timeout); - } -} - -static void -check_memory_usage() -{ - char proc_fname[100]; - sprintf(proc_fname,"/proc/%d/statm",box_pid); - //printf("proc fname: %s\n",proc_fname); - FILE *fp = fopen(proc_fname,"r"); - if(fp!=NULL) { - char line[1000]; - fgets(line,999,fp); - //printf("%s\n",line); - int m; - - if(sscanf(line,"%d",&m)==1) { - m = (m*page_size+1023)/1024; - if(m>max_mem_used) - max_mem_used = m; - } - - fclose(fp); - } -} - -static void -boxkeeper(void) -{ - int syscall_count = 0; - struct sigaction sa; - - is_ptraced = 1; - bzero(&sa, sizeof(sa)); - sa.sa_handler = signal_int; - sigaction(SIGINT, &sa, NULL); - start_time = time(NULL); - ticks_per_sec = sysconf(_SC_CLK_TCK); - page_size = getpagesize(); - if (ticks_per_sec <= 0) - die("Invalid ticks_per_sec!"); - - check_memory_usage(); - - sa.sa_handler = signal_alarm; - sigaction(SIGALRM, &sa, NULL); - //alarm(1); - - struct itimerval val; - val.it_interval.tv_sec = 0; - val.it_interval.tv_usec = 50000; - val.it_value.tv_sec = 0; - val.it_value.tv_usec = 50000; - setitimer(ITIMER_REAL,&val,NULL); - - /* - --- add alarm handler no matter what.. - if (timeout) - { - sa.sa_handler = signal_alarm; - sigaction(SIGALRM, &sa, NULL); - alarm(1); - } - */ - - for(;;) - { - struct rusage rus; - int stat; - pid_t p; - - if (timer_tick) - { - check_timeout(); - check_memory_usage(); - timer_tick = 0; - } - p = wait4(box_pid, &stat, WUNTRACED, &rus); - - if (p < 0) - { - if (errno == EINTR) - continue; - die("wait4: %m"); - } - if (p != box_pid) - die("wait4: unknown pid %d exited!", p); - if (WIFEXITED(stat)) - { - struct timeval total; - int wall; - wall = time(NULL) - start_time; - timeradd(&rus.ru_utime, &rus.ru_stime, &total); - - box_pid = 0; - if (WEXITSTATUS(stat)) - fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat)); - else if ((use_wall_clock ? - wall : - (double) total.tv_sec + - ((double) total.tv_usec/1000000.0)) > timeout) - fprintf(stderr,"Time limit exceeded.\n"); - else - // report OK and statistics - fprintf(stderr,"OK\n"); - - print_running_stat((double) wall, - (double) rus.ru_utime.tv_sec + - ((double) rus.ru_utime.tv_usec/1000000.0), - (double) rus.ru_stime.tv_sec + - ((double) rus.ru_stime.tv_usec/1000000.0), - max_mem_used); -/* - (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n", - (double) total.tv_sec + ((double)total.tv_usec / 1000000.0), - (int) total.tv_usec, - wall, - syscall_count, - max_mem_used); -*/ - exit(0); - } - if (WIFSIGNALED(stat)) - { - box_pid = 0; - fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat)); - - struct timeval total; - int wall; - wall = time(NULL) - start_time; - timeradd(&rus.ru_utime, &rus.ru_stime, &total); - print_running_stat((double) wall, - (double) rus.ru_utime.tv_sec + - ((double) rus.ru_utime.tv_usec/1000000.0), - (double) rus.ru_stime.tv_sec + - ((double) rus.ru_stime.tv_usec/1000000.0), - max_mem_used); - exit(0); - } - if (WIFSTOPPED(stat)) - { - int sig = WSTOPSIG(stat); - if (sig == SIGTRAP) - { - struct user u; - static int stop_count = -1; - if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0) - die("ptrace(PTRACE_GETREGS): %m"); - stop_count++; - if (!stop_count) /* Traceme request */ - log(">> Traceme request caught\n"); - else if (stop_count & 1) /* Syscall entry */ - { - log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx); - syscall_count++; - if (!valid_syscall(&u)) - { - /* - * Unfortunately, PTRACE_KILL kills _after_ the syscall completes, - * so we have to change it to something harmless (e.g., an undefined - * syscall) and make the program continue. - */ - unsigned int sys = u.regs.orig_eax; - u.regs.orig_eax = 0xffffffff; - if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0) - die("ptrace(PTRACE_SETREGS): %m"); - die("Forbidden syscall %d.", sys); - } - } - else /* Syscall return */ - log("= %ld\n", u.regs.eax); - ptrace(PTRACE_SYSCALL, box_pid, 0, 0); - } - else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ) - { - log(">> Signal %d\n", sig); - ptrace(PTRACE_SYSCALL, box_pid, 0, sig); - } - else - die("Received signal %d.", sig); - } - else - die("wait4: unknown status %x, giving up!", stat); - } -} - -static void -box_inside(int argc, char **argv) -{ - struct rlimit rl; - char *args[argc+1]; - char *env[1] = { NULL }; - - memcpy(args, argv, argc * sizeof(char *)); - args[argc] = NULL; - if (set_cwd && chdir(set_cwd)) - die("chdir: %m"); - if (redir_stdin) - { - close(0); - if (open(redir_stdin, O_RDONLY) != 0) - die("open(\"%s\"): %m", redir_stdin); - } - if (redir_stdout) - { - close(1); - if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1) - die("open(\"%s\"): %m", redir_stdout); - } - dup2(1, 2); - setpgrp(); - if (memory_limit) - { - rl.rlim_cur = rl.rlim_max = memory_limit * 1024; - if (setrlimit(RLIMIT_AS, &rl) < 0) - die("setrlimit: %m"); - } - rl.rlim_cur = rl.rlim_max = 64; - if (setrlimit(RLIMIT_NOFILE, &rl) < 0) - die("setrlimit: %m"); - if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0) - die("ptrace(PTRACE_TRACEME): %m"); - execve(args[0], args, (pass_environ ? environ : env)); - die("execve(\"%s\"): %m", args[0]); -} - -static void -usage(void) -{ - fprintf(stderr, "Invalid arguments!\n"); - printf("\ -Usage: box [] -- \n\ -\n\ -Options:\n\ --a \tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\ --c \tChange directory to first\n\ --e\t\tPass full environment of parent process\n\ --f\t\tFilter system calls (-ff=very restricted)\n\ --i \tRedirect stdin from \n\ --m \tLimit address space to KB\n\ --o \tRedirect stdout to \n\ --t