summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rwxr-xr-xbase/compile.sh23
l---------base/compilers1
-rw-r--r--base/config.py42
-rw-r--r--base/core.py69
-rw-r--r--base/daemon.sh84
-rw-r--r--base/init.sh67
-rwxr-xr-xbase/jail.sh41
-rwxr-xr-xbase/log.sh30
-rw-r--r--base/run.py84
-rw-r--r--base/update.py72
10 files changed, 513 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+
+$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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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 <http://www.gnu.org/licenses/>.
+
+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))