From 1502051f4ac6929fb3d85027b368923134c767ff Mon Sep 17 00:00:00 2001 From: root Date: Mon, 18 Jul 2011 01:16:30 +0430 Subject: Initial commit --- base/compile.sh | 23 ++++++++++++++++ base/compilers | 1 + base/config.py | 42 +++++++++++++++++++++++++++++ base/core.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++ base/daemon.sh | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base/init.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++ base/jail.sh | 41 ++++++++++++++++++++++++++++ base/log.sh | 30 +++++++++++++++++++++ base/run.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base/update.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 513 insertions(+) create mode 100755 base/compile.sh create mode 120000 base/compilers create mode 100644 base/config.py create mode 100644 base/core.py create mode 100644 base/daemon.sh create mode 100644 base/init.sh create mode 100755 base/jail.sh create mode 100755 base/log.sh create mode 100644 base/run.py create mode 100644 base/update.py (limited to 'base') diff --git a/base/compile.sh b/base/compile.sh new file mode 100755 index 0000000..8bb9534 --- /dev/null +++ b/base/compile.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# compile.sh +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +$TIMEOUT $COMPILE_TIME \ + $CHROOT --userspec=$BIN_USER:$BIN_GROUP $JAIL \ + $COMPILER_DIR/$LANGUAGE.sh + +exit $? diff --git a/base/compilers b/base/compilers new file mode 120000 index 0000000..337c47b --- /dev/null +++ b/base/compilers @@ -0,0 +1 @@ +/mnt/jail/bin/compilers/ \ No newline at end of file diff --git a/base/config.py b/base/config.py new file mode 100644 index 0000000..2092aa5 --- /dev/null +++ b/base/config.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python2 +# +# config.py: parse problem config +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +PROBCONFADDR = os.getenv('PROBCONFADDR') + +def parse(): + PROBCONF = open (PROBCONFADDR, 'r') + + COUNT = int (PROBCONF.readline().strip()) + CONFMOD = PROBCONF.readline().strip() + + CONFIG = [] + + if CONFMOD == 'default': + confline = PROBCONF.readline().strip() + for i in range(COUNT): + CONFIG.append (confline.replace ('%n', str(i)).split()) + else: + for i in range(COUNT): + confline = PROBCONF.readline() + CONFIG.append (confline.replace ('%n', str(i)).split()) + + PROBCONF.close() + + return [COUNT, CONFIG] diff --git a/base/core.py b/base/core.py new file mode 100644 index 0000000..6e42ebd --- /dev/null +++ b/base/core.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python2 +# +# core.py +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import sys +import run +import config +import update + +# Reading common options from env: +MODE_DIR = os.getenv("MODE_DIR") +COMPILER = os.getenv("COMPILER") +LOGGER = os.getenv("LOGGER") + +# Reading user's code options from env +USER = os.getenv("USER") +SUBID = os.getenv("SUBID") +PROBLEM = os.getenv("PROBLEM") +LANGUAGE = os.getenv('LANGUAGE') + +# Reading problem specific options from problem config +[COUNT, CONFIG] = config.parse() + +# Initializing log +prefix = SUBID + ':' + USER + ':' + PROBLEM + ':' + LANGUAGE + ': ' +logmsg = '' + +# Start compiling +update.status ('COMPILE', SUBID, -1) +compret = os.system (COMPILER + ' ' + LANGUAGE) +compret /= 256 + +update.status ('COMPILE', SUBID, compret) + +if compret == 124: # Compile time limit exceeded, refer to Gnu timeout manual + logmsg = 'Compile time limit exceeded' +elif compret: # Unspecified Compilation error + logmsg = 'Compilation error' +else: + # Start running + update.status ('RUN', SUBID, -1, -1); + arr = run.main (COUNT, CONFIG) + + charstat = 'CWTMRU' # [ correct, wrong, time, memory, runtime, unexpected ] + + for status in arr: + if status > 5: + logmsg += '[' + str(status - 6) + ']' + else: + logmsg += charstat[status] + +os.system (LOGGER + ' LOG grading ' + prefix + logmsg) + +update.conn.close() diff --git a/base/daemon.sh b/base/daemon.sh new file mode 100644 index 0000000..de75186 --- /dev/null +++ b/base/daemon.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# +# daemon.sh +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +PID=$$ +LOCK="/var/run/judge.pid" + +echo $PID >$LOCK +echo "Judge daemon started ... pid=$PID" >&2 + +export CHROOT=`which chroot` +export SU=`which su` +export TIME=`which time` +export TIMEOUT=`which timeout` + +export JUDGE='/judge' + +export BASE="$JUDGE/base" +export INIT="$BASE/init.sh" +export CORE="$BASE/core.py" +export LOGGER="$BASE/log.sh" +export JAILER="$BASE/jail.sh" +export COMPILER="$BASE/compile.sh" + +export LOG_DIR="$JUDGE/log" + +export JAIL="$JUDGE/jail" +export SU_SYNTAX="--session-command" + +export PROBLEMS_DIR="$JUDGE/problems" + +export COMPILER_DIR="/bin/compilers" +export CODE_DIR='/source' # FIXME +export BIN_USER=0 +export BIN_GROUP=0 +export COMPILE_TIME=10 # FIXME + +export RUN_DIR='/home' # FIXME +export RUN_USER='judge' # FIXME +export RUN_GROUP=99 + +export LOG_DIR="$JUDGE/log" +export LOG_DATE_FORMAT="--rfc-3339=ns" + +export TIME_FORMAT='%e %M %x' + +export DB_HOST='127.0.0.1' +export DB_USERNAME='' +export DB_PASSWORD='' +export DB_NAME='' + +PORT=31415 +FIFO="/tmp/fifo$PID" + +rm -f /tmp/fifo* +mkfifo $FIFO + +trap "{ killall -9 nc init.sh daemon.sh + rm -f $FIFO $LOCK; }" EXIT + +while true +do +{ + echo . + sh $INIT < $FIFO & + nc -l $PORT > $FIFO +} +done + +rm -f $FIFO $LOCK diff --git a/base/init.sh b/base/init.sh new file mode 100644 index 0000000..638080d --- /dev/null +++ b/base/init.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# init.sh +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +export PID=$$ + +read SUBID USER PROBLEM LANGUAGE CODE TEST + +if [ -z "$SUBID" -o -z "$USER" -o -z "$PROBLEM" \ + -o -z "$LANGUAGE" -o -z "$CODE" -o -z "$TEST" ] +then + sh $LOGGER LOG error "receiving initial variables failed" + exit 1 +fi + +sh $LOGGER LOG error "init started ..." + +# TODO seams buggy FIXME +LOCK="/var/run/judgeinit.pid" +while [ -e $LOCK ] +do + LOCK_PID=`cat $LOCK` + if ! ps $LOCK_PID >/dev/null + then + rm -f $LOCK + else + sleep 1 + fi +done +echo $PID >$LOCK + +export SUBID=$SUBID +export PROBLEM=$PROBLEM +export PROBLEM_DIR="$PROBLEMS_DIR/$PROBLEM" +export PROBCONFADDR="$PROBLEM_DIR/config" +export TESTER="$PROBLEM_DIR/tester" +export INIT_DIR="$PROBLEM_DIR/inits" +export TEST_INIT="$INIT_DIR/$TEST.init" +export INPUT_DIR="$PROBLEM_DIR/inputs" +export TEST_INPUT="$INPUT_DIR/$TEST.in" + +export USER=$USER +export LANGUAGE=$LANGUAGE +export SOURCE="$CODE_DIR/$SUBID-$PROBLEM-$USER.$LANGUAGE" # FIXME +export OUT="$RUN_DIR/$SUBID.out" + +cp -f $CODE $JAIL$SOURCE + +sh $LOGGER LOG error "core started ..." + +python $CORE + +rm -f $LOCK diff --git a/base/jail.sh b/base/jail.sh new file mode 100755 index 0000000..b8ac5e8 --- /dev/null +++ b/base/jail.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# jail.sh +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +TEST=$1 shift +TIME_LIMIT=`expr 2 \* $1` shift +FIRST_INPUT=$@ + +FIFORUN="/tmp/fiforun$$" + +{ + mkfifo $FIFORUN +# exec 3<>$FIFORUN + + sh -c "$FIRST_INPUT" >$FIFORUN & + sleep 0.1 + coproc $TESTER $INIT_DIR/$TEST.init >$FIFORUN & + + exec <$FIFORUN >&${COPROC[1]} \ + $TIME -f "result $TIME_FORMAT" \ + $TIMEOUT $TIME_LIMIT \ + $SU $RUN_USER \ + $SU_SYNTAX $OUT +# $SU_SYNTAX "$OUT 2>/dev/null" +} 2>&1 + +rm -f $FIFORUN diff --git a/base/log.sh b/base/log.sh new file mode 100755 index 0000000..7005248 --- /dev/null +++ b/base/log.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# log.sh +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +COMMAND=$1 shift +FILE=$1 shift +TEXT=$@ + +LOG_DIR="/judge/log" + +if [ $COMMAND == "LOG" ]; then + echo [`date $LOG_DATE_FORMAT`] $PID: $TEXT >>$LOG_DIR/$FILE.log +elif [ $COMMAND == "ARCHIVE" ]; then + mv $LOG_DIR/$FILE.log $LOG_DIR/$FILE.log-`date +%Y%m%d` + bzip2 $LOG_DIR/$FILE.log-`date +%Y%m%d` #$FILE.log --suffix=-`date +%Y%m%d` +fi diff --git a/base/run.py b/base/run.py new file mode 100644 index 0000000..37aec11 --- /dev/null +++ b/base/run.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python2 +# +# run.py +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import update + +SUBID = os.getenv("SUBID") +LOGGER = os.getenv("LOGGER") +JAILER = os.getenv("JAILER") +INPUT_DIR = os.getenv("INPUT_DIR") + +def run (testnum, CONFIG): + TIMELIM = CONFIG[1] + MEMLIM = CONFIG[2] + INPUT_COMMAND = INPUT_DIR + '/' + CONFIG[3] + + if (CONFIG[0] == 'normal'): + INPUT_COMMAND = 'cat ' + INPUT_COMMAND + +# print JAILER + ' ' + str (testnum) + ' ' + str(TIMELIM) + ' ' + INPUT_COMMAND + + update.status ('RUN', SUBID, -1, testnum) + + stdin = os.popen (JAILER + ' ' + str (testnum) + ' ' + str(TIMELIM) + ' ' + INPUT_COMMAND , 'r') + + status = time = mem = ret = score = -1 + + while True: + line = stdin.readline() + + if not line: break + + result = line.strip().split(' ') + + if result[0].isdigit(): + score = int (result[0]) + elif result[0] == "result": + time = float (result[1]) + mem = int (result[2]) + ret = int (result[3]) + + if ret > 127: status = 6 + retstat - 128 # signal + elif ret > 124: status = 5 # unexpected + elif ret == 124: status = 2 # time + elif ret != 0: status = 4 # runtime + elif mem > MEMLIM: status = 3 # memory + elif time > TIMELIM: status = 2 # time + elif score <= 0: status = 1 # wrong + else: status = 0 # correct + + os.system(LOGGER + " LOG error {0} {1} {2} {3} {4} {5} {6}".format(SUBID, status, testnum, time, mem, ret, score)) + + error = update.status ('RUN', SUBID, status, testnum, time, mem, score) + + return [status, error] + +#strstat = [ 'Accepted!', 'Wrong answer', 'Time limit exceeded', 'Memory limit exceeded', 'Run time error', 'Unexpected error', 'Signal #' ] + +def main(COUNT, CONFIG): + arr = [] + for i in range(COUNT): + [status, error] = run (i, CONFIG[i]) + arr.append(status) + if error: + break + + update.status ('END', SUBID, status) + + return arr diff --git a/base/update.py b/base/update.py new file mode 100644 index 0000000..5953cee --- /dev/null +++ b/base/update.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python2 + +# update.py +# Copyright (C) 2011 Hamed Saleh and Mahrud Sayrafi + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import MySQLdb + +HOST = os.getenv("DB_HOST") +USER = os.getenv("DB_USERNAME") +PASS = os.getenv("DB_PASSWORD") +NAME = os.getenv("DB_NAME") + +conn = MySQLdb.connection (host=HOST, user=USER, passwd=PASS, db=NAME) + +strstat = [ 'Accepted!', 'Wrong answer', 'Time limit exceeded', 'Memory limit exceeded', + 'Run time error', 'Unexpected error', 'Signal #' ] + +def status (query, SUBID, status, test = -1, time = -1, mem = -1, score = -1): + if query == 'COMPILE': + if status == -1: + msg = 'compiling...' + elif status == 0: + msg = 'successfully compiled' + elif status == 124: + msg = 'compile time limit exceeded' + else: + msg = 'compilation error' + + conn.query ("""UPDATE `submitions` + SET status='{MSG}' WHERE id='{SUBID}'""".format (MSG = msg, SUBID = SUBID)) + + elif query == 'RUN': + if status == -1: + if test == -1: + msg = 'running...' + else: + msg = 'running on test ' + str (test + 1) + else: + msg = 'running on test ' + str (test + 1) + + conn.query ("UPDATE `submitions` SET status='{MSG}' WHERE id='{SUBID}'".format (MSG = msg, SUBID = SUBID)) + + return status > 0 + + elif query == 'END': + if status == -1: + status = 5; + + msg = strstat [min (6, status)] + + if status > 5: + msg += str (status - 6) + + if status > 0: + msg += ' on test ' + str (test + 1) + + conn.query ("""UPDATE `submitions` + SET status='{MSG}' WHERE id='{SUBID}'""".format (MSG = msg, SUBID = SUBID)) -- cgit