From e8774085f83a3d6a3f4c95726351ec97727247bc Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Sat, 11 Oct 2025 11:23:58 +0800 Subject: [PATCH] first commit --- conf-demo.py | 7 + judge | 63 ++ judge-daemon | 66 ++ judge-daemonold | 67 +++ judgeold | 67 +++ python/datasource.py | 528 ++++++++++++++++ python/datasource.pyc | Bin 0 -> 21089 bytes python/engine.py | 69 +++ python/engine.pyc | Bin 0 -> 2917 bytes python/engineconfig.py | 239 ++++++++ python/engineconfig.pyc | Bin 0 -> 7209 bytes python/entity.py | 271 +++++++++ python/entity.pyc | Bin 0 -> 9964 bytes python/judgescript.py | 286 +++++++++ python/judgescript.pyc | Bin 0 -> 10437 bytes python/ojunit.py | 21 + python/ojunit.pyc | Bin 0 -> 1139 bytes python/tester.py | 829 ++++++++++++++++++++++++++ python/tester.pyc | Bin 0 -> 27508 bytes scripts/bash-guard | 7 + scripts/binary-guard | 7 + scripts/compare-file.py | 34 ++ scripts/compare-guard | 10 + scripts/compare-wrapper-bash-3 | 4 + scripts/compare-wrapper-fpc-2.2 | 11 + scripts/compare-wrapper-g++-3.3 | 9 + scripts/compare-wrapper-gcc-3.3 | 9 + scripts/compare-wrapper-gmcs-2.0 | 11 + scripts/compare-wrapper-java-1.5 | 16 + scripts/compare-wrapper-java-1.6 | 16 + scripts/compare-wrapper-python-2.5 | 8 + scripts/compare-wrapper-python-2.5old | 8 + scripts/compile-guard | 6 + scripts/f | 7 + scripts/fpc | 4 + scripts/g++-3.3 | 4 + scripts/gcc-3.3-bc | 5 + scripts/gcc-3.3-nobc | 4 + scripts/gmcs-2.0 | 4 + scripts/java-guard | 7 + scripts/javac-1.5 | 4 + scripts/javac-1.6 | 4 + scripts/mono-guard | 7 + scripts/python-guard | 7 + scripts/python-guardd | 6 + scripts/run-guard.py | 159 +++++ utils/bitoj_adduser | 55 ++ utils/xmlrpc-debug-proxy.py | 141 +++++ 48 files changed, 3087 insertions(+) create mode 100755 conf-demo.py create mode 100755 judge create mode 100755 judge-daemon create mode 100755 judge-daemonold create mode 100755 judgeold create mode 100755 python/datasource.py create mode 100644 python/datasource.pyc create mode 100755 python/engine.py create mode 100644 python/engine.pyc create mode 100755 python/engineconfig.py create mode 100644 python/engineconfig.pyc create mode 100755 python/entity.py create mode 100644 python/entity.pyc create mode 100755 python/judgescript.py create mode 100644 python/judgescript.pyc create mode 100755 python/ojunit.py create mode 100644 python/ojunit.pyc create mode 100755 python/tester.py create mode 100644 python/tester.pyc create mode 100755 scripts/bash-guard create mode 100755 scripts/binary-guard create mode 100755 scripts/compare-file.py create mode 100755 scripts/compare-guard create mode 100755 scripts/compare-wrapper-bash-3 create mode 100755 scripts/compare-wrapper-fpc-2.2 create mode 100755 scripts/compare-wrapper-g++-3.3 create mode 100755 scripts/compare-wrapper-gcc-3.3 create mode 100755 scripts/compare-wrapper-gmcs-2.0 create mode 100755 scripts/compare-wrapper-java-1.5 create mode 100755 scripts/compare-wrapper-java-1.6 create mode 100755 scripts/compare-wrapper-python-2.5 create mode 100755 scripts/compare-wrapper-python-2.5old create mode 100755 scripts/compile-guard create mode 100755 scripts/f create mode 100755 scripts/fpc create mode 100755 scripts/g++-3.3 create mode 100755 scripts/gcc-3.3-bc create mode 100755 scripts/gcc-3.3-nobc create mode 100755 scripts/gmcs-2.0 create mode 100755 scripts/java-guard create mode 100755 scripts/javac-1.5 create mode 100755 scripts/javac-1.6 create mode 100755 scripts/mono-guard create mode 100755 scripts/python-guard create mode 100755 scripts/python-guardd create mode 100755 scripts/run-guard.py create mode 100755 utils/bitoj_adduser create mode 100755 utils/xmlrpc-debug-proxy.py diff --git a/conf-demo.py b/conf-demo.py new file mode 100755 index 0000000..36ecdcf --- /dev/null +++ b/conf-demo.py @@ -0,0 +1,7 @@ +#Add XML-RPC datasource +#config.add_xmlrpc_datasource('http://11.9.36.12/mod/programming/ojfeeder.php') +config.add_xmlrpc_datasource('http://d1kt.cn/course/mod/programming/ojfeeder.php') + +# Options for debug +#config.debug = True +config.no_cleanup = True diff --git a/judge b/judge new file mode 100755 index 0000000..05ffbcc --- /dev/null +++ b/judge @@ -0,0 +1,63 @@ +#!/usr/bin/env python2.6 +import os, signal, sys, logging, logging.handlers +judge = None +def setup_logging(): + from engineconfig import getConfig + config = getConfig() + + judgelog = os.path.join(config.judgehome, config.logdir, + 'judge-%s.log' % config.profile) + testerlog = os.path.join(config.judgehome, config.logdir, + 'tester-%s.log' % config.profile) + + # setup logger + # judge.main is for main judge program + logger = logging.getLogger('main') + hdlr = logging.handlers.RotatingFileHandler( + judgelog, 'a', 1024 * 1024 * 5, 4) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + if config.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + # judge.tester is for forked compiler and programs + logger = logging.getLogger('tester') + hdlr = logging.handlers.RotatingFileHandler( + testerlog, 'a', 1024 * 1024 * 5, 4) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + if config.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + +def main(): + judgehome = os.path.abspath(os.path.dirname(sys.argv[0])) + sys.path.insert(1, os.path.join(judgehome, 'python')) + + from engineconfig import getConfig + config = getConfig() + config.judgehome = judgehome + vars = {'config': config} + execfile(sys.argv[1], vars, vars) + + setup_logging() + + from engine import JudgeEngine + judge = JudgeEngine() + + # setup signal handler + signal.signal(signal.SIGINT, judge.quit) + signal.signal(signal.SIGQUIT, judge.quit) + signal.signal(signal.SIGTERM, judge.quit) + + judge.run() + +if __name__ == '__main__': + main() + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/judge-daemon b/judge-daemon new file mode 100755 index 0000000..c6c6d08 --- /dev/null +++ b/judge-daemon @@ -0,0 +1,66 @@ +#!/usr/bin/env python2.6 + +import os, signal, sys, time +from resource import setrlimit, RLIMIT_AS +configs = {} +quitnow = False +judgehome = os.path.abspath(os.path.dirname(sys.argv[0])) + +def main(): + os.chdir(judgehome) + + # redirect stdout and stderr to file + os.close(1) + fd = os.open('log/daemon.log', os.O_WRONLY | os.O_APPEND | os.O_CREAT) + os.close(2) + fd = os.dup(fd) + + # setup resource usage + setrlimit(RLIMIT_AS, (1024 * 1024 * 256, 1024 * 1024 * 256)) + + # setup signal handler + signal.signal(signal.SIGINT, quit) + signal.signal(signal.SIGQUIT, quit) + + # find config files + files = os.listdir(os.path.join(judgehome, 'conf')) + for filename in files: + if filename[:5] == 'conf-' and filename[-3:] == '.py': + configs[filename] = 0 + + # start all the judge process + for conf in configs.keys(): + configs[conf] = run(conf) + + # check for the dead of judge process + while not quitnow: + for conf in configs.keys(): + pid = configs[conf] + ret = os.waitpid(pid, os.WNOHANG) + if ret[0]: + print >>sys.stderr, 'judge process %d dead, restarting...' % pid + configs[conf] = run(conf) + time.sleep(1) + +def run(conf): + pid = os.fork() + if pid: + return pid + else: + cmd = '/usr/bin/python' + conf = os.path.join('conf', conf) + os.execvp(cmd, (cmd, 'judge', conf)) + +def quit(sig, frame): + quitnow = True + for conf in configs.keys(): + pid = configs[conf] + os.kill(pid, sig) + + for conf in configs.keys(): + pid = configs[conf] + os.waitpid(pid, 0) + exit() + +if __name__ == '__main__': + main() diff --git a/judge-daemonold b/judge-daemonold new file mode 100755 index 0000000..fba9b95 --- /dev/null +++ b/judge-daemonold @@ -0,0 +1,67 @@ +#!/usr/bin/env python2.6 + +import os, signal, sys, time +from resource import setrlimit, RLIMIT_AS + +configs = {} +quitnow = False +judgehome = os.path.abspath(os.path.dirname(sys.argv[0])) + +def main(): + os.chdir(judgehome) + + # redirect stdout and stderr to file + os.close(1) + fd = os.open('log/daemon.log', os.O_WRONLY | os.O_APPEND | os.O_CREAT) + os.close(2) + fd = os.dup(fd) + + # setup resource usage + setrlimit(RLIMIT_AS, (1024 * 1024 * 256, 1024 * 1024 * 256)) + + # setup signal handler + signal.signal(signal.SIGINT, quit) + signal.signal(signal.SIGQUIT, quit) + + # find config files + files = os.listdir(os.path.join(judgehome, 'conf')) + for filename in files: + if filename[:5] == 'conf-' and filename[-3:] == '.py': + configs[filename] = 0 + + # start all the judge process + for conf in configs.keys(): + configs[conf] = run(conf) + + # check for the dead of judge process + while not quitnow: + for conf in configs.keys(): + pid = configs[conf] + ret = os.waitpid(pid, os.WNOHANG) + if ret[0]: + print >>sys.stderr, 'judge process %d dead, restarting...' % pid + configs[conf] = run(conf) + time.sleep(1) + +def run(conf): + pid = os.fork() + if pid: + return pid + else: + cmd = '/usr/bin/python2.6' + conf = os.path.join('conf', conf) + os.execvp(cmd, (cmd, 'judge', conf)) + +def quit(sig, frame): + quitnow = True + for conf in configs.keys(): + pid = configs[conf] + os.kill(pid, sig) + + for conf in configs.keys(): + pid = configs[conf] + os.waitpid(pid, 0) + exit() + +if __name__ == '__main__': + main() diff --git a/judgeold b/judgeold new file mode 100755 index 0000000..d4be3ab --- /dev/null +++ b/judgeold @@ -0,0 +1,67 @@ +#!/usr/bin/env python2.6 + +import os, signal, sys, logging, logging.handlers + +judge = None + +def setup_logging(): + from engineconfig import getConfig + config = getConfig() + + judgelog = os.path.join(config.judgehome, config.logdir, + 'judge-%s.log' % config.profile) + testerlog = os.path.join(config.judgehome, config.logdir, + 'tester-%s.log' % config.profile) + + # setup logger + # judge.main is for main judge program + logger = logging.getLogger('main') + hdlr = logging.handlers.RotatingFileHandler( + judgelog, 'a', 1024 * 1024 * 5, 4) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + if config.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + # judge.tester is for forked compiler and programs + logger = logging.getLogger('tester') + hdlr = logging.handlers.RotatingFileHandler( + testerlog, 'a', 1024 * 1024 * 5, 4) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + if config.debug: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + +def main(): + judgehome = os.path.abspath(os.path.dirname(sys.argv[0])) + sys.path.insert(1, os.path.join(judgehome, 'python')) + + from engineconfig import getConfig + config = getConfig() + config.judgehome = judgehome + vars = {'config': config} + + execfile(sys.argv[1], vars, vars) + + setup_logging() + + from engine import JudgeEngine + judge = JudgeEngine() + + # setup signal handler + signal.signal(signal.SIGINT, judge.quit) + signal.signal(signal.SIGQUIT, judge.quit) + signal.signal(signal.SIGTERM, judge.quit) + + judge.run() + +if __name__ == '__main__': + main() + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/datasource.py b/python/datasource.py new file mode 100755 index 0000000..381e5b0 --- /dev/null +++ b/python/datasource.py @@ -0,0 +1,528 @@ + +import logging, os, sys, socket, time, xmlrpclib, bz2, Cookie +import unittest +from engineconfig import getConfig +from entity import Submit, Problem, TestCase, PresetCode, DataFile + +class DataSourceError(Exception): pass + +class DataSource: + + def __init__(self, driver): + self.driver = driver + self.config = getConfig() + self.logger = logging.getLogger('main') + + self.judge_id = self.driver.get_judge_id() + + def _get_config_retry(self): + ret = self.config.retry + if ret < 1: ret = 1 + return ret + + def _get_config_retry_wait(self): + ret = self.config.retry_wait + if ret < 0.1: ret = 0.1 + return ret + + def _do_action(self, func, args): + retry = self._get_config_retry() + retry_wait = self._get_config_retry_wait() + ret = None + while retry > 0: + try: + ret = func(*args) + except DataSourceError, e: + self.logger.exception("DataSourceError") + if retry > 0: + time.sleep(retry_wait) + retry = retry - 1 + continue + else: + self.logger.info('Quit after retried.') + sys.exit(1) + break + return ret + + def get_submits(self, limit): + func = self.driver.get_submits + args = (self.judge_id, limit) + rows = self._do_action(func, args) + ret = [] + if rows: + for row in rows: + ret.append(Submit(self, row)) + return ret + + def reset_submits(self): + func = self.driver.reset_submits + args = (self.judge_id,) + return self._do_action(func, args) + + def get_problem(self, pid): + func = self.driver.get_problem + args = (pid, ) + return Problem(self, self._do_action(func, args)) + + def get_tests(self, pid, full): + func = self.driver.get_tests + args = (pid, full) + rows = self._do_action(func, args) + ret = [] + if rows: + for row in rows: + ret.append(TestCase(self, row)) + return ret + + def get_test(self, tid, raw = True): + func = self.driver.get_test + args = (tid, ) + row = self._do_action(func, args) + if raw: + return row + else: + return TestCase(self, row) + + def get_presetcodes(self, pid, lang): + func = self.driver.get_presetcodes + args = (pid, lang) + rows = self._do_action(func, args) + return map(lambda row: PresetCode(self, row), rows) + + def get_datafiles(self, pid): + func = self.driver.get_datafiles + args = (pid, ) + rows = self._do_action(func, args) + return map(lambda row: DataFile(self, row), rows) + + def get_datafile_data(self, datafileid): + func = self.driver.get_datafile_data + args = (datafileid, ) + return self._do_action(func, args) + + def update_submits_status(self, sids, status): + func = self.driver.update_submits_status + if not isinstance(sids, (tuple, list)): + sids = (sids, ) + args = (sids, status) + self.logger.debug('set submits %s status to %s', sids.__str__(), status) + return self._do_action(func, args) + + def update_submit_compilemessage(self, sid, msg): + func = self.driver.update_submit_compilemessage + args = (sid, msg) + return self._do_action(func, args) + + def get_submit_status(self, sid): + func = self.driver.get_submit_status + args = (sid, ) + return self._do_action(func, args) + + def get_submit_compilemessage(self, sid): + func = self.driver.get_submit_compilemessage + args = (sid, ) + return self._do_action(func, args) + + def update_submit_test_results(self, sid, results): + func = self.driver.update_submit_test_results + args = (sid, results) + self.logger.info('update_submits_status: submit %s, %d records' % \ + (sid, len(results))) + return self._do_action(func, args) + + def get_submit(self, sid): + func = self.driver.get_submit + args = (sid, ) + ret = self._do_action(func, args) + return Submit(self, ret) + +class JspAuthTransport(xmlrpclib.Transport): + def __init__(self): + xmlrpclib.Transport.__init__(self) + self.__cookies = Cookie.SmartCookie() + + def request(self, host, handler, request_body, verbose=0): + # issue XML-RPC request + + h = self.make_connection(host) + if verbose: + h.set_debuglevel(1) + + self.send_request(h, handler, request_body) + self.__sendJsessionCookie(h) + self.send_host(h, host) + self.send_user_agent(h) + self.send_content(h, request_body) + + errcode, errmsg, headers = h.getreply() + + if errcode != 200: + raise xmlrpclib.ProtocolError( + host + handler, + errcode, errmsg, + headers + ) + self.verbose = verbose + self.__processCookies(headers) + return self.parse_response(h.getfile()) + + + def get_jsession_id(self): + if self.__cookies.has_key('MoodleSession'): + return self.__cookies['MoodleSession'].value + return None + + + def __sendJsessionCookie(self, connection): + if self.__cookies.has_key('MoodleSession'): + connection.putheader( + 'Cookie', + 'MoodleSession=%s' % self.__cookies['MoodleSession'].value) + if self.__cookies.has_key('MoodleSessionTest'): + connection.putheader( + 'Cookie', + 'MoodleSessionTest=%s' % self.__cookies['MoodleSessionTest'].value) + + + def __processCookies(self, headers): + if headers.getheader('Set-Cookie'): + self.__cookies.load(headers.getheader('Set-Cookie')) + + + def send_content(self, connection, request_body): + connection.putheader("Content-Type", "text/xml") + connection.putheader("Content-Length", str(len(request_body))) + + connection.endheaders() + if request_body: + connection.send(request_body) + +class XmlRpcDataSource: + + def __init__(self, url): + self.transport = JspAuthTransport() + self.server = xmlrpclib.Server(url, transport = self.transport) + self.config = getConfig() + self.logger = logging.getLogger('main') + + def get_judge_id(self): + while True: + try: + return self.server.oj.get_judge_id() + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_judge_id') + time.sleep(self.config.retry_wait) + + def reset_submits(self, judgeid): + while True: + try: + return self.server.oj.reset_submits(judgeid) + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to reset_submits') + time.sleep(self.config.retry_wait) + + def get_submits(self, judgeid, limit): + while True: + try: + submits = self.server.oj.get_submits(judgeid, limit) + for submit in submits: + if not isinstance(submit['code'], (str, unicode)): + submit['code'] = submit['code'].__str__() + return submits + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + time.sleep(self.config.retry_wait) + self.logger.exception('Failed to get_submits') + + def get_problem(self, problemid): + while True: + try: + return self.server.oj.get_problem(problemid) + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_problem') + time.sleep(self.config.retry_wait) + + def get_tests(self, problemid, full): + while True: + try: + tests = self.server.oj.get_tests(problemid, full) + for test in tests: + if not isinstance(test['input'], (str, unicode)): + test['input'] = test['input'].__str__() + if not isinstance(test['output'], (str, unicode)): + test['output'] = test['output'].__str__() + self.logger.debug('Got %d test case(s)', len(tests)) + return tests + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_tests') + time.sleep(self.config.retry_wait) + + def get_test(self, testid): + while True: + try: + test = self.server.oj.get_gztest(testid) + if not isinstance(test['input'], (str, unicode)): + test['input'] = test['input'].__str__() + if not isinstance(test['output'], (str, unicode)): + test['output'] = test['output'].__str__() + test['input'] = bz2.decompress(test['input']) + test['output'] = bz2.decompress(test['output']) + return test + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_tests') + time.sleep(self.config.retry_wait) + + def get_presetcodes(self, problemid, lang): + while True: + try: + codes = self.server.oj.get_presetcodes(problemid, lang) + for code in codes: + if not isinstance(code['code'], (str, unicode)): + code['code'] = code['code'].__str__() + self.logger.debug('Got %d presetcodes', len(codes)) + return codes + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_presetcodes') + time.sleep(self.config.retry_wait) + + def get_datafiles(self, problemid): + while True: + try: + files = self.server.oj.get_datafiles(problemid) + self.logger.debug('Got %d datafiles', len(files)) + return files + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_datafiles') + time.sleep(self.config.retry_wait) + + def get_datafile_data(self, datafileid): + while True: + try: + data = self.server.oj.get_datafile_data(datafileid) + return str(data) + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_datafiles') + time.sleep(self.config.retry_wait) + + def update_submit_compilemessage(self, id, compilemsg): + compilemsg = xmlrpclib.Binary(compilemsg) + while True: + try: + return self.server.oj.update_submit_compilemessage( + id, compilemsg) + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to update_submit_compilemessage') + time.sleep(self.config.retry_wait) + + def update_submit_test_results(self, id, results): + for r in results: + if not isinstance(r['stdout'], (str, unicode)): r['stdout'] = '' + if not isinstance(r['stderr'], (str, unicode)): r['stderr'] = '' + r['stdout'] = xmlrpclib.Binary(r['stdout']) + r['stderr'] = xmlrpclib.Binary(r['stderr']) + while True: + try: + return self.server.oj.update_submit_test_results(id, results) + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to update_submit_compilemessage') + time.sleep(self.config.retry_wait) + + def update_submits_status(self, submitids, newstatus): + while True: + mc = xmlrpclib.MultiCall(self.server) + for id in submitids: + mc.oj.update_submit_status(id, newstatus) + try: + return mc() + except xmlrpclib.Error, e: + raise DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to update_submits_status') + time.sleep(self.config.retry_wait) + + def get_submit_status(self, sid): + while True: + try: + return self.server.oj.get_submit_status(sid) + except xmlrpclib.Error, e: + return DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_submit_status') + time.sleep(self.config.retry_wait) + + def get_submit_compilemessage(self, sid): + while True: + try: + msg = self.server.oj.get_submit_compilemessage(sid) + if isinstance(msg, (str, unicode)): + return msg + else: + return msg.__str__() + except xmlrpclib.Error, e: + return DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_submit_status') + time.sleep(self.config.retry_wait) + + def get_submit(self, sid): + while True: + try: + return self.server.oj.get_submit(sid) + except xmlrpclib.Error, e: + return DataSourceError(e) + except socket.error, e: + self.logger.exception('Failed to get_submit') + time.sleep(self.config.retry_wait) + +class DataSourceTest(unittest.TestCase): + + def setUp(self): + execfile(os.path.join('..', 'testdata', 'test_config.py')) + self.config = getConfig() + self.datasource = self.config.datasources[0] + self.dbname = os.path.join('..', 'testdata', self.config.testdb) + + + def testGetSubmits(self): + submits = self.datasource.get_submits(4) + self.assertTrue(len(submits), 4) + s = submits[0] + self.assertEqual(s.id, '0000000001') + self.assertEqual(s.problem_id, '0000000001') + self.assertEqual(s.language, 'gcc-3.3') + self.assertEqual(s.code, '') + s = submits[1] + self.assertEqual(s.code, '#include ') + + def testGetSubmit(self): + s = self.datasource.get_submit(1) + self.assertEqual(s.id, '0000000001') + self.assertEqual(s.problem_id, '0000000001') + self.assertEqual(s.language, 'gcc-3.3') + self.assertEqual(s.code, '') + + def testResetSubmits(self): + self.datasource.reset_submits() + + def testGetProblem(self): + p = self.datasource.get_problem('0000000001') + self.assertEqual(p.id, '0000000001') + + def testGetTests(self): + tests = self.datasource.get_tests('0000000001', True) + self.assertTrue(len(tests), 2) + t = tests[0] + self.assertEqual(t.id, '0000000001') + self.assertEqual(t.problem_id, '0000000001') + self.assertEqual(t.timemodified, 0) + self.assertEqual(t.input, '1 2') + self.assertEqual(t.output, '3') + self.assertEqual(t.timelimit, 10) + self.assertEqual(t.memlimit, 1024 * 1024) + + def testGetTest(self): + t = self.datasource.get_test('0000000001', False) + self.assertEqual(t.id, '0000000001') + self.assertEqual(t.problem_id, '0000000001') + self.assertEqual(t.timemodified, 0) + self.assertEqual(t.input, '1 2') + self.assertEqual(t.output, '3') + self.assertEqual(t.timelimit, 10) + self.assertEqual(t.memlimit, 1024 * 1024) + + def testUpdateSubmitCompileMessage(self): + self.datasource.update_submit_compilemessage('0000000001', 'hello') + msg = self.datasource.get_submit_compilemessage('0000000001') + self.assertEqual(msg, 'hello') + self.datasource.update_submit_compilemessage('0000000001', '') + + def testUpdateSubmitStatus(self): + self.datasource.update_submits_status(('0000000004', '0000000001'), + 'compiling') + s = self.datasource.get_submit_status('0000000001') + self.assertEqual(s, 'compiling') + s = self.datasource.get_submit_status('0000000004') + self.assertEqual(s, 'compiling') + s = self.datasource.get_submit_status('0000000002') + self.assertEqual(s, 'new') + self.datasource.update_submits_status(('0000000004', '0000000001'), 'new') + s = self.datasource.get_submit_status('0000000001') + self.assertEqual(s, 'new') + + def testUpdateSubmitTestResults(self): + from pysqlite2 import dbapi2 as sqlite + conn = sqlite.connect(self.dbname) + cur = conn.cursor() + cur.execute('DELETE FROM submit_test_result') + conn.commit() + cur.close() + conn.close() + + id = '0000000001' + r1 = { 'test_id' : '0000000001', + 'stdout' : '3', + 'stderr' : '', + 'exitcode' : 0, + 'signal' : 0, + 'timeused' : 0, + 'memused' : 0} + r2 = { 'test_id' : '0000000002', + 'stdout' : '4', + 'stderr' : 'abc', + 'exitcode' : 0, + 'signal' : 11, + 'timeused' : 1, + 'memused' : 2} + self.datasource.update_submit_test_results(id, (r1, r2)) + + conn = sqlite.connect(self.dbname) + cur = conn.cursor() + cur.execute('SELECT * FROM submit_test_result') + rows = cur.fetchall() + self.assertEqual(len(rows), 2) + cur.close() + conn.close() + row = rows[0] + self.assertEqual(row[1], 1) #sid + self.assertEqual(row[2], 1) #tid + self.assertEqual(row[3], '3') #stdout + self.assertEqual(row[4], '') #stderr + self.assertEqual(row[5], 0) #exitcode + self.assertEqual(row[6], 0) #signal + self.assertEqual(row[7], 0) #timeused + self.assertEqual(row[8], 0) #memused + row = rows[1] + self.assertEqual(row[1], 1) #sid + self.assertEqual(row[2], 2) #tid + self.assertEqual(row[3], '4') #stdout + self.assertEqual(row[4], 'abc') #stderr + self.assertEqual(row[5], 0) #exitcode + self.assertEqual(row[6], 11) #signal + self.assertEqual(row[7], 1) #timeused + self.assertEqual(row[8], 2) #memused + +if __name__ == '__main__': + unittest.main() + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/datasource.pyc b/python/datasource.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f16987f60d082cb1fe03bd46c44403fbcc1f8f66 GIT binary patch literal 21089 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 literal 0 HcmV?d00001 diff --git a/python/engine.py b/python/engine.py new file mode 100755 index 0000000..3f5838a --- /dev/null +++ b/python/engine.py @@ -0,0 +1,69 @@ + +import logging, os, thread, threading, time, Queue +from engineconfig import getConfig +class JudgeEngine: + + def __init__(self): + self.quit_event = threading.Event() + self.test_queue = Queue.Queue() + + self.config = getConfig() + self.quit_event.clear() + self.logger = logging.getLogger('main') + + def run(self): + # one thread mode is good for debugging + if self.config.test_threads == 1: + thread.start_new_thread(self.transport, ()) + self.test() + else: + for i in range(self.config.test_threads): + thread.start_new_thread(self.test, ()) + self.transport() + + for ds in self.config.datasources: + ds.reset_submits() + self.logger.info('main thread quit') + return + + def transport(self): + total = 0 + while not self.quit_event.isSet(): + self.logger.info( + "%d submit in test queue, %d processed" % \ + (self.test_queue.qsize(), total)) + c = 16 - self.test_queue.qsize() + if c > 0: + submits = self.config.datasources[0].get_submits(c) + for submit in submits: + self.logger.info('Add submit %s to queue' % submit.id) + self.test_queue.put(submit) + total = total + len(submits) + time.sleep(self.config.fetch_interval) + self.logger.info("fetch thread quit") + return + + def test(self): + while not self.quit_event.isSet(): + #pdb.set_trace() + try: + submit = self.test_queue.get(True, self.config.fetch_interval) + except Queue.Empty: + continue + tester = self.config.get_tester(submit.language) + if tester: + tester.test(submit) + else: + submit.set_status('compile_failed') + submit.set_compilemessage('No compiler specified') + self.logger.info("test thread quit") + return + + def quit(self, signum, frame): + self.logger.info('signal receive: %d' % signum) + self.quit_event.set() + return + +class JudgeError(Exception): pass + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/engine.pyc b/python/engine.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c8ea01db0e6d9452d050873d4de85adfe1e856c GIT binary patch literal 2917 zcmbVO+m0JW5Un12?X?r{C7auVAPy+P44D#NVih9tJ1BO?KSDHX*-g~H5u2XTNg8cXk7+X`BCId zDZ5n(Yh}Ae0T+p@bHs#oaSiT%W^^SS6gWuL2ce33(sOBt9;uVzD7U?VOGi*B zvj4=UArPP0+=awymlI#i?cC}_us$R+c4Lf7sG;f?VNzelwwH%G2FKY5!5WpJ{0v>2 zt2k$DUIdyJc{?6<@D;86HMI8TKHnR=!@WZnvPXNyV|W1E z;nEf=g4HfCY7>%Gm=wa8aR}#G5tc&GYmkd}pb{SdNs`*{76JwEc`x{L% z6uc)r2EEYp**x!Ks)c@Ty$y+~mpCCHXwv zz{g)jCDD%AF&oj2)XWvrj9U0^n5_a_#)(D2{R<6X3RI5&D1)v?XZm}Ar=pB77w8o5 zo){TMa#8iW(0XhIWAjRJ(p}K+U#-LpQ z(w!&-VU5{;WETO94zH;_;$z&X}^uBp$+|`XBfAM(U_rDemMWY z21P%*qotSy|y;VNOmpDcWfk`D{loD=$EG9CY2mOwbY)GNtCRTO$v9;4c0;m}{ z-@)cjdSlI!1;5mgEcC|8w7zDV1t*pEMf=5=7hk}Rub}wXP>HFTifoyNsepDn$R@j{ zt`xjzE}E+)0qMvxyZnaE@h{I(?`JVjudPS{PsxNeaWz4*0O$|XU8%wW5b9A=vZfgP zW)(@1O1@smHUv~biXh!Ie`MRBBj^D3Xo}|oLz(l7oUff$nNv8c4B8O4$s1_NAO`+G zW8KdtQ#ZEBu!nl!iQYlhUX12#Kec@~beJK3lwAK!NE}pp#4od@d|+&Vy@LB?j+=sM zDt?_6L#9~oPp09i-Y8@V)#7%b+!z^X-W%C?hoa2uns-7k%zYqI>@KXEfY=meHc~#W zGEw{nm+N+_`vob%&2MqDmrw}_5mn4Jvu)PRB~v%sQOjH)73gYN?o*z}k>KHv*8uKd zvq}ScjL)D>Q@vivUOjT7v^Q?&wr|}N`wmWRhw!Z2D$XDGf%7XL3&vyde4;TO=Dmr1 zF-H|6-Yj>ioZ%9%yk$BHB=Ja+6k3x+p-7TRHkgAfNz7v!Gdg;F6s`CzbXWUp>v>Xt z%v*yV>os1B8taYC#&%;%w=5oBd7kBcNa&B^_fS0Tv-VTsiX`!=EmSa@#pI&qvCPLxdjb+)XQezN3yUX8@gUL%lKQ4M?*4Hf+0A8X%q{+h VX?i@~D*(7l>lCj~K*OE){{@PCC%pgw literal 0 HcmV?d00001 diff --git a/python/engineconfig.py b/python/engineconfig.py new file mode 100755 index 0000000..4d56a53 --- /dev/null +++ b/python/engineconfig.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python2.6 + +import string +from string import split +from os import path +from Queue import Queue +from subprocess import Popen, PIPE + +class EngineConfig: + + def __init__(self): + self.profile = 'default' + self.test_threads = 2 + self.retry = 10 + self.retry_wait = 5 + self.datasources = [] + self.fetch_interval = 1 + self.mips = 1000 + self.languages = {} + self.maxmem = 65536 + self.maxtime = 300 + self.compile_timelimit = 30 + self.output_size_limit = 1024 * 1024 * 2 + self.output_sendback_size_limit = 1024 * 64 + self.logdir = 'log' + self.debug = False + self.judgescript_wait = 10 + self.python = '/usr/bin/python3' + self.no_cleanup = False + self.datadir = path.join('data', self.profile) + self.rundir_root = '/var/lib/bitoj' + + self.runas = Queue() + self.setup_testers() + + # Init runas with ojrun?? users + pa = Popen(['getent', 'passwd'], stdout=PIPE) + pb = Popen(['awk', '-F:', '/^ojrun[0-9][0-9]:/{print $1}'], + stdin=pa.stdout, stdout=PIPE) + output = pb.communicate()[0] + if output: + for user in string.split(output, '\n'): + user = string.strip(user) + if user: self.runas.put(user) + pa.wait() + pb.wait() + + def add_tester(self, profile, tester): + self.languages[profile] = tester + + def get_tester(self, profile): + if self.languages.has_key(profile): + return self.languages[profile] + + def add_datasource(self, ds): + from datasource import DataSource + self.datasources.append(DataSource(ds)) + + def add_xmlrpc_datasource(self, url): + from datasource import XmlRpcDataSource + self.add_datasource(XmlRpcDataSource(url)) + + def setup_testers(self): + + default_compileguard = ( + '/scripts/compile-guard', ' ' + ) + default_runguard = split( + '/usr/bin/sudo -u /scripts/binary-guard ' + + '-e ' + + '-t -T 5 -m -d -o -p -x', + ' ' + ) + maxmem_runguard = split( + '/usr/bin/sudo -u /scripts/binary-guard ' + + '-e ' + + '-t -T 5 -m -d -o -p -x', + ' ' + ) + java_runguard = split( + '/usr/bin/sudo -u ' + + '/scripts/java-guard -t -T 5 -m 262144 ' + + '-e ' + + '-d -o -p -x', ' ' + ) + python_runguard = split( + '/scripts/python-guardd ' + + '-t -T 5 -m -d -o -p -x', + ' ' + ) + mono_runguard = split( + '/usr/bin/sudo -u ' + + '/scripts/mono-guard -t -T 5 -m ' + + '-e ' + + '-d -o -p -x', ' ' + ) + bash_runguard = split( + '/usr/bin/sudo -u /scripts/bash-guard ' + + '-e ' + + '-t -T 5 -m -d -o -p -x', + ' ' + ) + default_compare = ( + '/scripts/compare-guard', '', '', + '', '', '' + ) + + # Options for testers + from tester import SimpleTester, ComboTester + cbc = SimpleTester(source = 'main.c', + target = 'main', + compile = ('/scripts/gcc-3.3-bc',), + run = ('/main', ), + runenv = { + 'GCC_BOUNDS_OPTS' : '-no-message -no-statistics -no-check-mmap', + }, + basemem = {'Data' : 48, 'Stack' : 84 }, + compileguard = default_compileguard, + runguard = maxmem_runguard, + ) + + cnobc = SimpleTester( + source = 'main.c', target = 'main', + compile = ('/scripts/gcc-3.3-nobc',), + run = ('/main', ), + runenv = {}, + basemem = {'Data' : 28, 'Stack' : 84 }, + compileguard = default_compileguard, + runguard = default_runguard, + ) + + gcc33 = ComboTester() + gcc33.add_tester(cbc, has_timelimit = False, has_memlimit = False, + check_result = True, is_lasttest = False) + gcc33.add_tester(cnobc, has_timelimit = True, has_memlimit = True, + check_result = True, is_lasttest = True) + gcc33.comparecmd = default_compare + gcc33.source = 'main.c' + + gxx33 = SimpleTester( + source = 'main.cpp', target = 'main', + compile = ('/scripts/g++-3.3',), + run = ('/main',), + runenv = {}, + basemem = {'Data' : 52, 'Stack' : 84 }, + compileguard = default_compileguard, + runguard = default_runguard, + comparecmd = default_compare, + ) + + j2se15 = SimpleTester( + source = 'Main.java', target = 'Main.class', + compile = ('/scripts/javac-1.5',), + run = split('/usr/bin/java -cp -Xms8M -Xmx64M Main'), + runenv = {}, + basemem = {'RSS' : 7560 }, + baseproc = 8, + compileguard = default_compileguard, + runguard = java_runguard, + comparecmd = default_compare, + ) + + j2se16 = SimpleTester( + source = 'Main.java', target = 'Main.class', + compile = ('/scripts/javac-1.6',), + run = split('/usr/bin/java -cp -Xms8M -Xmx64M Main'), + runenv = {}, + basemem = {'RSS' : 7560 }, + baseproc = 8, + compileguard = default_compileguard, + runguard = java_runguard, + comparecmd = default_compare, + ) + + fpc = SimpleTester( + source = 'main.pas', target = 'main', + compile = ('/scripts/fpc',), + run = ('/main', ), + runenv = {}, + basemem = {'Data' : 32, 'Stack' : 84 }, + compileguard = default_compileguard, + runguard = default_runguard, + comparecmd = default_compare, + ) + + python3 = SimpleTester( + source = 'main.py', target = 'main.py', + compile = ('/bin/true',), + run = ('/usr/bin/python3', '/main.py'), + runenv = {}, + basemem = {'RSS' : 2048 }, + compileguard = (), + runguard = python_runguard, + comparecmd = default_compare, + ) + + gmcs20 = SimpleTester( + source = 'main.cs', target = 'main.exe', + compile = ('/scripts/gmcs-2.0',), + run = ('/usr/bin/mono', '/main.exe'), + runenv = { 'MONO_SHARED_DIR' : '' }, + basemem = {'RSS' : 8192 }, + compileguard = (), + runguard = mono_runguard, + comparecmd = default_compare, + ) + + bash3 = SimpleTester( + source = 'main.sh', target = 'main.sh', + compile = ('/bin/true',), + run = ('/bin/bash', '/main.sh'), + runenv = {}, + basemem = {'RSS' : 2048 }, + extraproc = 3, + compileguard = (), + runguard = bash_runguard, + comparecmd = default_compare, + ) + + self.add_tester('gcc-3.3', gcc33) + self.add_tester('gcc-3.3-nobc', cnobc) + self.add_tester('gcc-3.3-bc', cbc) + self.add_tester('g++-3.3', gxx33) + self.add_tester('java-1.5', j2se15) + self.add_tester('java-1.6', j2se16) + self.add_tester('fpc-2.2', fpc) + self.add_tester('python3', python3) + self.add_tester('gmcs-2.0', gmcs20) + self.add_tester('bash-3', bash3) + +config = None + +def getConfig(): + global config + if not config: + config = EngineConfig() + return config + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/engineconfig.pyc b/python/engineconfig.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95948e4e2ff9bb50f5fda3553c68b2b1a8a2b07c GIT binary patch literal 7209 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 config.output_sendback_size_limit: + r[4] = r[4][0:config.output_sendback_size_limit] + if len(r[5]) > config.output_sendback_size_limit: + r[5] = r[5][0:config.output_sendback_size_limit] + + newresults.append({ 'test_id' : r[0], + 'judge_result' : r[1], + 'exitcode' : r[2], + 'signal' : r[3], + 'stdout' : r[4], + 'stderr' : r[5], + 'timeused' : r[6], + 'memused' : r[7] }) + + return self.datasource.update_submit_test_results(self.id, newresults) + +class TestCase: + + write_lock = threading.Lock() + + def __init__(self, datasource, row): + config = getConfig() + logger = logging.getLogger('main') + self.id = row['id'] + self.problem_id = row['problem_id'] + self.timemodified = row['timemodified'] + #self.input = string.replace(row['input'], '\r\n', '\n') + #self.output = string.replace(row['output'], '\r\n', '\n') + self.timelimit = row['timelimit'] + if not self.timelimit: + self.timelimit = 1 + self.memlimit = row['memlimit'] + if not self.memlimit: + self.memlimit = config.maxmem + if row.has_key('nproc') and row['nproc']: + self.nproc = string.atoi(row['nproc']) + else: + self.nproc = 0 + + config = getConfig() + testdir = os.path.join(config.datadir, 'testcase') + if not os.path.exists(testdir): os.mkdir(testdir) + + self.infile = os.path.join(testdir, self.id + '.in') + self.outfile = os.path.join(testdir, self.id + '.out') + + TestCase.write_lock.acquire() + + if os.path.exists(self.infile): + inmtime = os.stat(self.infile)[stat.ST_MTIME] + else: + inmtime = 0 + if os.path.exists(self.outfile): + outmtime = os.stat(self.outfile)[stat.ST_MTIME] + else: + outmtime = 0 + logger.debug("inmtime=%d outmtime=%d timemodified=%d" % (inmtime, outmtime, self.timemodified)) + if inmtime <= self.timemodified or outmtime <= self.timemodified: + logger.debug('Creating input/output file %s and %s' % (self.infile, self.outfile)) + row = datasource.get_test(self.id) + input = string.replace(row['input'], '\r\n', '\n') + output = string.replace(row['output'], '\r\n', '\n') + + f = file(self.infile, 'w') + f.write(input) + if len(input) > 0 and input[-1] != '\n': f.write('\n') + f.close() + + f = file(self.outfile, 'w') + f.write(output) + if len(output) > 0 and output[-1] != '\n': f.write('\n') + f.close() + logger.debug('Finished') + else: + logger.debug('Skip input/output file creation') + + TestCase.write_lock.release() + + def get_input_file(self): + return self.infile + + def get_output_file(self): + return self.outfile + +class PresetCode: + + def __init__(self, datasource, row): + self.datasource = datasource + self.problem = None + + self.id = row['id'] + self.name = row['name'] + self.code = row['code'] + self.isheader = row['isheader'] + +class DataFile: + + write_lock = threading.Lock() + + def __init__(self, datasource, row): + self.datasource = datasource + self.problem = None + + # Data passed from datasource + self.id = row['id'] + self.problem_id = row['problem_id'] + self.filename = row['filename'] + self.type = row['type'] + self.timemodified = row['timemodified'] + + # Save datafile + config = getConfig() + testdir = os.path.join(config.datadir, 'testcase') + if not os.path.exists(testdir): os.mkdir(testdir) + self.absolute_path = os.path.join( + testdir, self.problem_id + '.' + self.filename) + + DataFile.write_lock.acquire() + + mtime = 0 + if os.path.exists(self.absolute_path): + mtime = os.stat(self.absolute_path)[stat.ST_MTIME] + if mtime < self.timemodified: + data = datasource.get_datafile_data(self.id) + data = bz2.decompress(data) + if self.type == 'text': + f = open(self.absolute_path, 'w') + f.write(string.replace(data, '\r\n', '\n')) + f.close() + else: + f = open(self.absolute_path, 'wb') + f.write(data) + f.close() + + DataFile.write_lock.release() + +if __name__ == '__main__': + unittest.main() + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/entity.pyc b/python/entity.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef16f4c0cf6e02e7a4a8b9e4df66802d88a928e4 GIT binary patch literal 9964 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 K^_lt$HRnHk=KXL0 literal 0 HcmV?d00001 diff --git a/python/judgescript.py b/python/judgescript.py new file mode 100755 index 0000000..7a2a6ec --- /dev/null +++ b/python/judgescript.py @@ -0,0 +1,286 @@ + +import string, os, time, tempfile, logging +import unittest + +from engineconfig import getConfig + +class InternalJudge: + + def __init__(self, allowpe = False): + self.logger = logging.getLogger('main') + self.allowpe = allowpe + + def judge(self, sid, tid, tin, tout, result, errfile, rundir = None): + """Judge if the result correct. + + @param sid String submit id + @param tid String testcase id + @param tin String filename of the standard input + @param tout String filename of the standard output + @param result String filename of the result + """ + + return self.compare_file(tin, tout, result, self.allowpe) + + def compare_file(self, input, output, result, allowpe): + fo = file(output, 'rb'); fr = file(result, 'rb') + + if not allowpe: + r = 'AC' + while r == 'AC': + so = fo.read(8192); sr = fr.read(8192) + if so == '' and sr == '': break + if so != sr: r = 'WA' + else: + so = fo.read(); sr = fr.read() + r = self.compare_string(so, sr) + + fo.close(); fr.close() + return r + + def compare_string(self, output, result): + if output == result: return 'AC' + + outnum = '' + retnum = '' + for c in output: + if c in string.digits: outnum += c + for c in result: + if c in string.digits: retnum += c + self.logger.debug('numbers in output: %s' % outnum) + self.logger.debug('numbers in result: %s' % retnum) + if len(outnum) > 0 and len(retnum) > 0 and outnum == retnum: + return 'PE' + + return 'WA' + +class ExternalJudge: + + def __init__(self, problemid, vlang, vcode): + """Constructor. + + @param pid String problem id + @param vlang String validator language + @param vcode String validator code + """ + self.config = getConfig() + self.logger = logging.getLogger('main') + self.problemid = problemid + self.lang = vlang + self.code = vcode + + # save validator code + tester = self.config.get_tester(self.lang) + datadir = os.path.join(self.config.datadir, 'validator', problemid) + if not os.path.exists(datadir): + os.makedirs(datadir) + self.comparecmd = tester.comparecmd + self.codefile = os.path.abspath(os.path.join(datadir, tester.source)) + f = file(self.codefile, 'w') + f.write(string.replace(vcode, '\r\n', '\n')) + if len(vcode) > 0 and vcode[-1] != '\n': f.write('\n') + f.close() + + self.logger.debug("Save validator code as %s" % self.codefile) + + def judge(self, sid, tid, tin, tout, result, errfile, rundir = None): + """Judge if the result correct. + + @param sid String submit id + @param tid String testcase id + @param tin String filename of the standard input + @param tout String filename of the standard output + @param result String filename of the result + @param rundir String in which dir the judge script should be started + """ + + rfiledir = os.path.join(self.config.datadir, sid) + rfile = os.path.join(rfiledir, tid + '.rst') + if not os.path.exists(rfiledir): + os.mkdir(rfiledir) + + cmd = [] + for s in self.comparecmd: + s = s.replace('', self.config.judgehome) + s = s.replace('', self.lang) + s = s.replace('', self.codefile) + s = s.replace('', tin) + s = s.replace('', tout) + s = s.replace('', result) + cmd.append(s) + self.logger.debug("Run validator as %s" % cmd.__str__()) + + pid = os.fork() + if pid == 0: + os.close(1) + os.open(rfile, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0666) + os.close(2) + os.open(errfile, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0666) + + if rundir: os.chdir(rundir) + os.execv(cmd[0], cmd) + print 'WA' + + remaintime = self.config.judgescript_wait + pid1 = 0 + while remaintime > 0: + pid1, status = os.waitpid(pid, os.WNOHANG) + if pid1 > 0: + break + time.sleep(0.1) + remaintime -= 0.1 + + while pid1 == 0: + try: + os.kill(pid, 9) + except os.OSError, e: + pass + pid1, status = os.waitpid(pid, os.WNOHANG) + + f = file(rfile, 'r') + ret = string.strip(f.readline()) + f.close() + if ret != 'AC' and ret != 'PE': ret = 'WA' + return ret + + +class InternalJudgeTest(unittest.TestCase): + """Internal Judge Test Case.""" + + def testWithoutPE(self): + ij = InternalJudge() + output = 'hello world\n' + result = 'hello world\n' + self.assertEqual(ij.compare_string(output, result), 'AC') + output = 'hello world' + self.assertEqual(ij.compare_string(output, result), 'WA') + result = 'hello world' + self.assertEqual(ij.compare_string(output, result), 'AC') + result = 'hello world ' + self.assertEqual(ij.compare_string(output, result), 'WA') + + def testWithPE(self): + ij = InternalJudge(True) + input = '' + output = 'hello world\n' + result = 'hello world\n' + self.assertEqual(ij.compare_string(output, result), 'AC') + result = 'hello worl\n' + self.assertEqual(ij.compare_string(output, result), 'WA') + output = '1 2 3 4 5\n' + result = '1\n2\n3\n4\n5\n' + self.assertEqual(ij.compare_string(output, result), 'PE') + result = '12345\n' + self.assertEqual(ij.compare_string(output, result), 'PE') + result = '1 2 3 4 5' + self.assertEqual(ij.compare_string(output, result), 'PE') + +class ExternalJudgeTest(unittest.TestCase): + """External Judge Test Case.""" + + def setUp(self): + config = getConfig() + config.judgehome = '../' + config.datadir = os.path.join('..', 'testdata') + + self.tin = tempfile.NamedTemporaryFile('w') + self.tin.write('3') + self.tin.flush() + self.tout = tempfile.NamedTemporaryFile('w') + self.tout.write('3') + self.tout.flush() + self.rst = tempfile.NamedTemporaryFile('w') + self.rst.write('6') + self.rst.flush() + self.err = tempfile.NamedTemporaryFile('w') + self.err.close() + self.tin.close() + self.tout.close() + self.rst.close() + + def testGCC33(self): + code = """ +#include + +int main() { + printf("AC\\n"); + return 0; +} +""" + self.j = ExternalJudge('p1', 'gcc-3.3', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testGPP33(self): + code = """ +#include +using namespace std; +int main() +{ + cout << "AC" << endl; + return 0; +} +""" + self.j = ExternalJudge('p1', 'g++-3.3', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testFPC(self): + code = """ +program main; + +begin + write('AC'); +end. +""" + self.j = ExternalJudge('p1', 'fpc-2.2', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testJava(self): + code = """ +public class Main { + public static void main(String[] args) { + System.out.println("AC"); + } +} +""" + self.j = ExternalJudge('p1', 'java-1.5', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + self.j = ExternalJudge('p1', 'java-1.6', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testPython(self): + code = """public class Main +{ + public static void Main() + { + System.Console.WriteLine("AC"); + } +} +""" + self.j = ExternalJudge('p1', 'gmcs-2.0', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testPython(self): + code = """#!/usr/bin/env python + +print 'AC' +""" + self.j = ExternalJudge('p1', 'python-2.5', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + + def testBash(self): + code = """echo AC""" + self.j = ExternalJudge('p1', 'bash-3', code) + x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name) + self.assertEquals('AC', x) + +if __name__ == '__main__': + unittest.main() + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/judgescript.pyc b/python/judgescript.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a8bba8fdd02f18c506e1d8ea26386dba9fafe8a GIT binary patch literal 10437 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 literal 0 HcmV?d00001 diff --git a/python/tester.py b/python/tester.py new file mode 100755 index 0000000..1233a08 --- /dev/null +++ b/python/tester.py @@ -0,0 +1,829 @@ + +import math, os, resource, signal, string, sys, threading, logging, time, pickle +import shutil, Queue +import unittest +import pdb +from engineconfig import getConfig +from ojunit import * + +class TesterBase: + + def __init__(self): + self.logger = logging.getLogger('main') + + def get_datadir(self, submit): + config = getConfig() + + # get datadir + datadir = os.path.abspath(config.datadir) + if not os.path.exists(datadir): os.mkdir(datadir) + + # get filenames + datadir = os.path.join(datadir, submit.id) + if not os.path.exists(datadir): os.mkdir(datadir) + + return datadir + + def get_rundir(self, submit): + config = getConfig() + return os.path.join(config.rundir_root, submit.user, 'run') + +class SimpleTester(TesterBase): + + has_timelimit = True + has_memlimit = True + check_result = True + is_lasttest = True + + def __init__(self, + source, target, + compile = None, compileenv = {}, + run = None, runenv = {}, basemem = 0, + baseproc = 0, extraproc = 0, + compileguard = None, runguard = None, comparecmd = None): + TesterBase.__init__(self) + self.source = source + self.target = target + self.compilecmd = compile + self.compileenv = compileenv + self.runcmd = run + self.runenv = runenv + self.basemem = basemem + self.baseproc = baseproc + self.extraproc = extraproc + self.compileguard = compileguard + self.runguard = runguard + self.comparecmd = comparecmd + + def test(self, submit): + #pdb.set_trace() + if not self.prepare(submit): + self.logger.info('self.prepare') + submit.set_status('compile_failed') + submit.set_compilemessage('Failed to delete source/target file') + return False + + submit.set_status('compiling') + if not self.compile(submit): + self.logger.info('self.compile') + submit.set_status('compile_failed') + self.cleanup(submit) + return False + + submit.set_status('running') + testcases = submit.get_testcases() + ret = [] + for testcase in testcases: + r = self.run(submit, testcase) + ret.append(r) + self.logger.debug('run result: ' + r.__str__()) + + passed = True + if self.check_result: + for r in ret: + if r[1] == 'AC' or not self.has_timelimit and r[1] == 'TLE': + pass + else: + passed = False + if not passed or self.is_lasttest: + submit.update_test_results(ret) + submit.set_status('finish') + + self.cleanup(submit) + + return passed + + def prepare(self, submit): + datadir = self.get_datadir(submit) + + # write presetcodes to dir + presetcodes = submit.get_presetcodes() + for presetcode in presetcodes: + pcname = os.path.join(datadir, presetcode.name) + if os.path.exists(pcname): + try: + os.unlink(pcname) + except OSError, e: + self.logger.exception( + "Failed to delete presetcode file %s" % pcname) + return False + f = open(pcname, 'w') + f.write(string.replace(presetcode.code, '\r\n', '\n')) + f.write('\n'); + f.close() + + # delete existing source and target file + datadirsource = os.path.join(datadir, self.source) + datadirtarget = os.path.join(datadir, self.target) + if os.path.exists(datadirsource): + try: + os.unlink(datadirsource) + except OSError, e: + self.logger.exception("Failed to delete source") + return False + if os.path.exists(datadirtarget): + try: + os.unlink(datadirtarget) + except OSError, e: + self.logger.exception("Failed to delete target") + return False + + # preprocess source code + code = string.replace(submit.code, '\r\n', '\n') + code = string.replace(code, chr(0x1a), '') # char generated by tc + code = string.replace(code, 'getch()', '') + code = string.replace(code, 'getch ()', '') + code = string.replace(code, 'getch ( )', '') + + code = string.replace(code, '\r\n', '\n') + # write source to disk + f = open(datadirsource, 'w') + f.write(code) + if len(submit.code) > 0 and submit.code[-1] != '\n': f.write('\n') + f.close() + + # setup rundir + config = getConfig() + try: + submit.user = config.runas.get_nowait() + except Queue.Empty: + self.logger.exception("No runas user left, please create more!") + return False + rundir = self.get_rundir(submit) + + if not os.path.exists(os.path.dirname(rundir)): + os.mkdir(os.path.dirname(rundir)) + + if os.path.exists(rundir): + try: + self._remove(rundir) + except OSError, e: + self.logger.exception("Failed to delete rundir") + config.runas.put(submit.user) + return False + os.mkdir(rundir) + os.chmod(rundir, 0775) + + return True + + def cleanup(self, submit, force = False): + datadir = self.get_datadir(submit) + rundir = self.get_rundir(submit) + config = getConfig() + if not config.no_cleanup or force: + if os.path.exists(datadir): + self._remove(datadir) + if os.path.exists(rundir): + try: + self._remove(rundir) + except OSError: + os.system('/usr/bin/sudo -u %s /bin/rm -rf %s' % (submit.user, rundir)) + + config.runas.put(submit.user) + + def compile(self, submit): + config = getConfig() + datadir = self.get_datadir(submit) + cmd = [] + if self.compileguard: + for s in self.compileguard: + s = string.replace(s, '', config.judgehome) + s = string.replace(s, '', datadir) + cmd.append(s) + if self.compilecmd: + for s in self.compilecmd: + s = string.replace(s, '', config.judgehome) + s = string.replace(s, '', datadir) + cmd.append(s) + cmd.append(self.source) + for code in submit.get_presetcodes(): + if not code.isheader: + cmd.append(code.name) + self.logger.debug(string.join(cmd, '_')) + + errfile = os.path.join(datadir, 'compile.err') + + (exitcode, sig, timeused, memused) = \ + self._execute(submit, cmd, timelimit = config.compile_timelimit, + infile = None, outfile = None, errfile = errfile, + env = self.compileenv) + + compilemsg = None + if os.path.exists(errfile): + f = file(errfile, 'r') + compilemsg = string.join(f.readlines(), '') + f.close() + + if compilemsg: + submit.set_compilemessage(compilemsg) + + if exitcode == 0: + self.logger.info('submit %s compile success' % submit.id) + else: + self.logger.info('submit %s compile failed' % submit.id) + self.logger.debug(compilemsg) + + return exitcode == 0 + + def run(self, submit, testcase): + config = getConfig() + datadir = self.get_datadir(submit) #dir save input and result + rundir = self.get_rundir(submit) #dir program runs in + + infile = testcase.get_input_file() + outfile = os.path.join(datadir, testcase.id + '.out') + errfile = os.path.join(datadir, testcase.id + '.err') + statfile = os.path.join(rundir, testcase.id + '.stat') + + # If submit input filename or output filename is provided, + # no input or output redirect will happen. + submit_input_filename = submit.get_input_filename() + submit_output_filename = submit.get_output_filename() + if submit_input_filename: + shutil.copyfile(infile, os.path.join(rundir, submit_input_filename)) + _infile = infile + infile = None + if submit_output_filename: + _outfile = outfile + outfile = None + + # Create data file symolic links + datafiles = submit.get_problem().get_datafiles() + for datafile in datafiles: + targetname = os.path.join(rundir, datafile.filename) + try: + shutil.copyfile(datafile.absolute_path, targetname) + except: + pass + + # Use extra process setting on testcase if it exist + extraproc = self.baseproc + (testcase.nproc if testcase.nproc else self.extraproc) + + cmd = [] + if self.runguard: + for s in self.runguard: + s = s.replace('', config.judgehome) + s = s.replace('', '%d' % extraproc) + s = s.replace('', '%d' % (testcase.timelimit)) + s = s.replace('', '%d' % config.maxmem) + s = s.replace('', rundir) + s = s.replace('', submit.user) + s = s.replace('', statfile) + cmd.append(s) + if self.runcmd: + for s in self.runcmd: + s = s.replace('', config.judgehome) + s = s.replace('', datadir) + s = s.replace('', submit.user) + cmd.append(s) + if self.runenv: + for k in self.runenv.keys(): + s = self.runenv[k] + s = s.replace('', config.judgehome) + s = s.replace('', datadir) + s = s.replace('', submit.user) + self.runenv[k] = s + self.logger.debug(string.join(cmd, ' ') + ' ' + str(self.runenv)) + + (exitcode, sig, timeused, memused) = \ + self._execute(submit, cmd, timelimit = testcase.timelimit * 10, + infile = infile, outfile = outfile, + errfile = errfile, env = self.runenv, + rlimit_fsize = config.output_size_limit + 2, + statfile = statfile) + if submit_input_filename: + infile = _infile + if submit_output_filename: + outfile = _outfile + try: + shutil.copyfile(os.path.join(rundir, submit_output_filename), + outfile) + except IOError: + f = file(outfile, 'w') + f.close() + + ret = [testcase.id, exitcode, sig, outfile, errfile, timeused, memused] + if timeused > testcase.timelimit: + ret.insert(1, 'TLE') + return ret + if memused > testcase.memlimit: + ret.insert(1, 'MLE') + return ret + + if exitcode == 125: + ret.insert(1, 'JSE') + return ret + elif exitcode == 126: + ret.insert(1, 'RFC') + return ret + elif exitcode == 127: + ret.insert(1, 'JGE') + return ret + + if sig == signal.SIGABRT or sig == signal.SIGSEGV: + ret.insert(1, 'RE') + return ret + elif sig == signal.SIGKILL or sig == signal.SIGXCPU: + ret.insert(1, 'TLE') + return ret + elif sig == signal.SIGXFSZ: + ret.insert(1, 'OLE') + return ret + elif sig == signal.SIGFPE: + ret.insert(1, 'FPE') + return ret + elif sig > 0: + ret.insert(1, 'KS') + return ret + + s = os.stat(outfile) + if s.st_size > config.output_size_limit: + ret.insert(1, 'OLE') + return ret + + js = submit.get_judge_script() + ret.insert(1, js.judge(submit.id, testcase.id, + os.path.abspath(testcase.get_input_file()), + os.path.abspath(testcase.get_output_file()), + os.path.abspath(outfile), + os.path.abspath(errfile), rundir)) + return ret + + def _execute(self, submit, command, timelimit = 1, + infile = None, outfile = None, errfile = None, env = {}, + rlimit_fsize = -1, statfile = None): + + pid = os.fork() + + if pid == 0: + if rlimit_fsize > 0: + resource.setrlimit(resource.RLIMIT_FSIZE, + (rlimit_fsize, rlimit_fsize)) + + # child process + if infile: + try: + os.close(0) + os.open(infile, os.O_RDONLY) + except Exception, e: + print e + sys.exit(125) + if outfile: + try: + os.close(1) + os.open(outfile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) + except Exception, e: + print e + sys.exit(125) + if errfile: + try: + os.close(2) + os.open(errfile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) + except Exception, e: + print e + sys.exit(125) + + #os.chdir(self.get_datadir(submit)) + os.execve(command[0], command, env) + sys.exit(125) + + # parent process + pid, status = os.waitpid(pid, 0) + timeused = 0; memused = 0; sig = 0; exitcode = 0 + + # get this child sig and exitcode + if os.WIFEXITED(status): exitcode = os.WEXITSTATUS(status) + if os.WIFSIGNALED(status): sig = os.WTERMSIG(status) + + # read information form statfile + if statfile: + try: + stat = pickle.load(file(statfile, 'r')) + exitcode = stat['exitcode'] + sig = stat['sig'] + timeused = stat['timeused'] + memused = 0 + if self.basemem.has_key('RSS'): + memused += stat['memrss'] - self.basemem['RSS'] + if self.basemem.has_key('Data'): + memused += stat['memdata'] - self.basemem['Data'] + if self.basemem.has_key('Stack'): + memused += stat['memstack'] - self.basemem['Stack'] + memused = max(0, memused) + except Exception, e: + self.logger.exception(e) + self.logger.error("Failed to read statfile: %s" % statfile) + exitcode = 127 # judge script error + + return (exitcode, sig, timeused, memused) + + def _remove(self, top): + for root, dirs, files in os.walk(top, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir(top) + +class ComboTester: + + testers = [] + + def add_tester(self, tester, + has_timelimit = True, has_memlimit = True, + check_result = True, is_lasttest = False): + tester.has_timelimit = has_timelimit + tester.has_memlimit = has_memlimit + tester.check_result = check_result + tester.is_lasttest = is_lasttest + self.testers.append(tester) + + def test(self, submit): + r = True + for t in self.testers: + r = t.test(submit) + if not r: + break + return r + +class SimpleTesterTestCase(OJTestCase): + + @classmethod + def suite(clazz): + tests = ( 'testSteps', 'testNoSource', + 'testAC', 'testWA', 'testZero', + 'testBinary', 'testCE', 'testOLE', 'testTLE', + 'testTargetDeleted', + ) + return unittest.TestSuite(map(SimpleTesterTestCase, tests)) + + def setUp(self): + OJTestCase.setUp(self) + self.st = self.config.languages['gcc-3.3-nobc'] + + def testSteps(self): + self.logger.info("TESTING: Steps") + submit = self.ds.get_submit(3) + + self.st.prepare(submit) + datadir = os.path.join(self.config.datadir, submit.id) + self.assertTrue(os.path.isdir(datadir)) + self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.source))) + + submit.set_status('compiling') + r = self.st.compile(submit) + self.assertTrue(r) + self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.target))) + self.assertTrue(os.path.isfile(os.path.join(datadir, 'compile.err'))) + + submit.set_status('running') + testcases = submit.get_testcases() + + r = self.st.run(submit, testcases[0]) + self.assertEqual(r[0], testcases[0].id) + self.assertEqual(r[1], 'AC') + self.assertEqual(r[2], 0) + self.assertEqual(r[3], 0) + f = file(r[4], 'r') + o = string.join(f.readlines(), '\n') + f.close() + self.assertEqual(o, '3\n') + f = file(r[5], 'r') + o = string.join(f.readlines(), '\n') + f.close() + self.assertEqual(o, '') + + r = self.st.run(submit, testcases[1]) + f = file(r[4], 'r') + o = string.join(f.readlines(), '\n') + f.close() + self.assertEqual(o, '4\n') + + self.st.cleanup(submit) + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) + + def testAC(self): + self.logger.info("TESTING: AC") + submit = self.ds.get_submit(3) + + datadir = os.path.join(self.config.datadir, submit.id) + r = self.st.test(submit) + self.assertTrue(r) # passed + + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup + + from pysqlite2 import dbapi2 as sqlite + conn = sqlite.connect(self.dbname) + cur = conn.cursor() + + cur.execute('SELECT * FROM submit_test_result WHERE sid=?', (submit.id, )) + rows = cur.fetchall() + cur.execute('DELETE FROM submit_test_result') + conn.commit() + + self.assertEqual(len(rows), 2) + + # test if the submit status change + cur.execute('SELECT status FROM submit WHERE id=?', (submit.id, )) + rows = cur.fetchall() + self.assertEqual(rows[0][0], 10) + + cur.close() + conn.close() + + def testNoSource(self): + self.logger.info("TESTING: No Source") + submit = self.ds.get_submit(1) + + # the code of this submit is empty + datadir = os.path.join(self.config.datadir, submit.id) + r = self.st.test(submit) + self.assertFalse(r) + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup + self.assertTrue(submit.get_status(), 'compile_failed') + + def testNoTarget(self): + self.logger.info("TESTING: No Target") + submit = self.ds.get_submit(1) + + # the code of this submit generate nothing + datadir = os.path.join(self.config.datadir, submit.id) + r = self.st.test(submit) + self.assertFalse(r) + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup + self.assertTrue(submit.get_status(), 'compile_failed') + + def testCE(self): + self.logger.info("TESTING: CE") + submit = self.ds.get_submit(7) + + datadir = os.path.join(self.config.datadir, submit.id) + r = self.st.test(submit) + self.assertFalse(r) # not passed + + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup + + self.assertTrue(len(submit.get_compilemessage()) > 0) + self.assertTrue(submit.get_status(), 'compile_failed') + + def testWA(self): + self.logger.info("TESTING: WA") + submit = self.ds.get_submit(4) + + datadir = os.path.join(self.config.datadir, submit.id) + r = self.st.test(submit) + self.assertFalse(r) # not passed + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup + from pysqlite2 import dbapi2 as sqlite + conn = sqlite.connect(self.dbname) + cur = conn.cursor() + + cur.execute('SELECT * FROM submit_test_result') + rows = cur.fetchall() + cur.execute('DELETE FROM submit_test_result') + conn.commit() + + self.assertEqual(len(rows), 2) + + cur.close() + conn.close() + + def testTargetDeleted(self): + self.logger.info("TESTING: Target Deleted") + submit = self.ds.get_submit(3) + + self.st.prepare(submit) + datadir = os.path.join(self.config.datadir, submit.id) + self.assertTrue(os.path.isdir(datadir)) + self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.source))) + + submit.set_status('compiling') + r = self.st.compile(submit) + self.assertTrue(r) + self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.target))) + self.assertTrue(os.path.isfile(os.path.join(datadir, 'compile.err'))) + + self.st.cleanup(submit, True) + + submit.set_status('running') + testcases = submit.get_testcases() + r = self.st.run(submit, testcases[0]) + self.assertNotEqual(r[1], 'AC') + + def testZero(self): + self.logger.info("TESTING: ZERO") + submit = self.ds.get_submit(5) + + # this program output '\0' + datadir = os.path.join(self.config.datadir, submit.id) + self.st.prepare(submit) + r = self.st.compile(submit) + self.assertTrue(r) + testcases = submit.get_testcases() + r = self.st.run(submit, testcases[0]) + f = file(os.path.join(datadir, '0000000001.out')) + o = string.join(f.readlines(), '\n') + f.close() + self.assertEqual(o, '3\0\n') + + self.st.cleanup(submit) + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) + + def testBinary(self): + self.logger.info("TESTING: BINARY") + submit = self.ds.get_submit(6) + + # this program output '0xbb' + datadir = os.path.join(self.config.datadir, submit.id) + self.st.prepare(submit) + r = self.st.compile(submit) + self.assertTrue(r) + testcases = submit.get_testcases() + r = self.st.run(submit, testcases[0]) + f = file(os.path.join(datadir, '0000000001.out')) + o = string.join(f.readlines(), '\n') + f.close() + self.assertEqual(o, '\xbb') + + self.st.cleanup(submit) + self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) + + def testOLE(self): + self.logger.info("TESTING: OLE") + submit = self.ds.get_submit(10) + + self.st.prepare(submit) + r = self.st.compile(submit) + self.assertTrue(r, "Submit compile failed") + ts = submit.get_testcases() + r = self.st.run(submit, ts[0]) + self.assertEqual(r[1], 'OLE') + + def testTLE(self): + self.logger.info("TESTING: TLE") + submit = self.ds.get_submit(11) + + self.st.prepare(submit) + self.st.compile(submit) + ts = submit.get_testcases() + r = self.st.run(submit, ts[0]) + self.assertEqual(r[1], 'TLE') + +class ComboTesterTestCase(OJTestCase): + + @classmethod + def suite(clazz): + tests = ( 'testGCC33', ) + return unittest.TestSuite(map(ComboTesterTestCase, tests)) + + def testGCC33(self): + self.ct = self.config.languages['gcc-3.3'] + submits = self.ds.get_submits(8) + r = self.ct.test(submits[2]) + self.assertTrue(r) + + from pysqlite2 import dbapi2 as sqlite + conn = sqlite.connect(self.dbname) + cur = conn.cursor() + cur.execute('DELETE FROM submit_test_result') + conn.commit() + cur.close() + conn.close() + +class LanguageTestCase(OJTestCase): + + @classmethod + def suite(clazz): + tests = ( 'testGCC33NOBC', 'testGCC33BC', 'testGXX33', + 'testJava5', 'testJava6', + 'testFreePascal20', + 'testPython25', + ) + return unittest.TestSuite(map(LanguageTestCase, tests)) + + def setUp(self): + OJTestCase.setUp(self) + + def testGCC33NOBC(self): + st = self.config.languages['gcc-3.3-nobc'] + submit = self.ds.get_submit(1001) + r = st.test(submit) + self.assertTrue(r, 'GCC NOBC test failed') + + def testGCC33BC(self): + st = self.config.languages['gcc-3.3-bc'] + submit = self.ds.get_submit(1001) + r = st.test(submit) + self.assertTrue(r, 'GCC WITH BC test failed') + + def testGXX33(self): + st = self.config.languages['g++-3.3'] + submit = self.ds.get_submit(1002) + r = st.test(submit) + self.assertTrue(r, 'GCC WITH BC test failed') + + def testJava5(self): + st = self.config.languages['java-1.6'] + submit = self.ds.get_submit(1004) + r = st.test(submit) + self.assertTrue(r, 'Java 5 test failed') + + def testJava6(self): + st = self.config.languages['java-1.6'] + submit = self.ds.get_submit(1004) + r = st.test(submit) + self.assertTrue(r, 'Java 6 test failed') + + def testFreePascal20(self): + st = self.config.languages['fpc-2.0'] + submit = self.ds.get_submit(1005) + r = st.test(submit) + self.assertTrue(r, 'FreePascal 2.0 test failed') + + def testPython25(self): + st = self.config.languages['python-2.5'] + submit = self.ds.get_submit(1006) + r = st.test(submit) + self.assertTrue(r, 'Python 2.5 test failed') + +class SecurityTestCase(OJTestCase): + """ + Security test including: + + * File Permission: only rundir are allowed to visit + * Forked Process: can oj kill all these process + * Daemon Process: can oj kill the daemon process + * Sleep: can oj kill sleep process + * Network: network usage should be forbidden + """ + + @classmethod + def suite(clazz): + tests = (#'testGCC33FilePermission', 'testJavaFilePermission', + #'testPython25FilePermission', + # 'testSleep', + 'testFork', 'testDaemon', + ) + return unittest.TestSuite(map(SecurityTestCase, tests)) + + def testGCC33FilePermission(self): + st = self.config.languages['gcc-3.3-nobc'] + submit = self.ds.get_submit(2311) + r = st.test(submit) + self.assertTrue(r, 'gcc 3.3 no bc security test failed') + + def testJavaFilePermission(self): + st = self.config.languages['java-1.6'] + submit = self.ds.get_submit(2331) + r = st.test(submit) + self.assertTrue(r, 'Java 1.6 security test failed') + + def testPython25FilePermission(self): + st = self.config.languages['python-2.5'] + submit = self.ds.get_submit(2361) + r = st.test(submit) + self.assertTrue(r, 'Python 2.5 security test failed') + + def testFork(self): + st = self.config.languages['gcc-3.3-nobc'] + submit = self.ds.get_submit(2412) + st.prepare(submit) + r = st.compile(submit) + self.assertTrue(r, "Failed") + ts = submit.get_testcases() + r = st.run(submit, ts[0]) + self.assertEqual(r[1], 'TLE', "Sleep test failed") + + def testDaemon(self): + st = self.config.languages['gcc-3.3-nobc'] + submit = self.ds.get_submit(2413) + st.prepare(submit) + r = st.compile(submit) + self.assertTrue(r, "Failed") + ts = submit.get_testcases() + r = st.run(submit, ts[0]) + self.assertEqual(r[1], 'TLE', "Sleep test failed") + + def testSleep(self): + st = self.config.languages['gcc-3.3-nobc'] + submit = self.ds.get_submit(2411) + st.prepare(submit) + r = st.compile(submit) + self.assertTrue(r, "Failed") + ts = submit.get_testcases() + r = st.run(submit, ts[0]) + self.assertEqual(r[1], 'TLE', "Sleep test failed") + + def testNetwork(self): + pass + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestSuite() + #suite.addTests(LanguageTestCase.suite()) + #suite.addTests(SimpleTesterTestCase.suite()) + #suite.addTest(ComboTesterTestCase.suite()) + suite.addTest(SecurityTestCase.suite()) + unittest.TextTestRunner().run(suite) + +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/python/tester.pyc b/python/tester.pyc new file mode 100644 index 0000000000000000000000000000000000000000..012c2cd7c0c8289bbad8f8f2bac4147aa1f67071 GIT binary patch literal 27508 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 NlUIgvgI(j^{{{bxLyG_a literal 0 HcmV?d00001 diff --git a/scripts/bash-guard b/scripts/bash-guard new file mode 100755 index 0000000..c6c678e --- /dev/null +++ b/scripts/bash-guard @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. + +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/binary-guard b/scripts/binary-guard new file mode 100755 index 0000000..c6c678e --- /dev/null +++ b/scripts/binary-guard @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. + +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/compare-file.py b/scripts/compare-file.py new file mode 100755 index 0000000..5830283 --- /dev/null +++ b/scripts/compare-file.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import sys, string, hashlib, os +from stat import * + +def check_file(name, size, md5sum): + global result + + m = hashlib.md5() + if os.path.isfile(name): + s = os.stat(name)[ST_SIZE] + if s == string.atoi(size): + f = open(name, 'rb') + m.update(f.read(string.atoi(size))) + f.close() + d = m.hexdigest() + result.write("%s %d %s\n" % (name, s, d)) + return d == md5sum + else: + result.write("Size of %s is %d\n" % (name, s)) + else: + result.write("%s not found\n" % name) + return False + +if __name__ == '__main__': + result = open(sys.argv[3], 'w+') + fstdout = open(sys.argv[2], 'r') + for line in fstdout: + name, size, md5sum = string.split(string.strip(line), ' ') + if not check_file(name, size, md5sum): + print 'WA'; break + fstdout.close() + result.close() + print 'AC' diff --git a/scripts/compare-guard b/scripts/compare-guard new file mode 100755 index 0000000..e5ec1d7 --- /dev/null +++ b/scripts/compare-guard @@ -0,0 +1,10 @@ +#!/bin/dash + +scriptdir=`dirname $0` +prog=$scriptdir/compare-wrapper-$1 +if [ -e "$prog" ] ; then + shift + exec "$prog" "$@" +else + echo 'JSE' +fi diff --git a/scripts/compare-wrapper-bash-3 b/scripts/compare-wrapper-bash-3 new file mode 100755 index 0000000..2cfc6cf --- /dev/null +++ b/scripts/compare-wrapper-bash-3 @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin +exec /bin/bash "$@" diff --git a/scripts/compare-wrapper-fpc-2.2 b/scripts/compare-wrapper-fpc-2.2 new file mode 100755 index 0000000..8935622 --- /dev/null +++ b/scripts/compare-wrapper-fpc-2.2 @@ -0,0 +1,11 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +cdir=`pwd` +cd $vdir +/usr/bin/fpc -O3 -vw0 -dONLINE_JUDGE -Sd -omain -MDelphi `basename $1` >&2 +cd $cdir +shift +exec "$vdir/main" "$@" diff --git a/scripts/compare-wrapper-g++-3.3 b/scripts/compare-wrapper-g++-3.3 new file mode 100755 index 0000000..3364759 --- /dev/null +++ b/scripts/compare-wrapper-g++-3.3 @@ -0,0 +1,9 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +target=$vdir/main +/usr/bin/g++ -Wall -O2 -o $target $1 -lm +shift +exec "$vdir/main" "$@" diff --git a/scripts/compare-wrapper-gcc-3.3 b/scripts/compare-wrapper-gcc-3.3 new file mode 100755 index 0000000..c704926 --- /dev/null +++ b/scripts/compare-wrapper-gcc-3.3 @@ -0,0 +1,9 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +target=$vdir/main +/usr/bin/gcc -Wall -O2 -std=c99 -o $target $1 -lm >&2 +shift +exec "$vdir/main" "$@" diff --git a/scripts/compare-wrapper-gmcs-2.0 b/scripts/compare-wrapper-gmcs-2.0 new file mode 100755 index 0000000..c04acb3 --- /dev/null +++ b/scripts/compare-wrapper-gmcs-2.0 @@ -0,0 +1,11 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +cdir=`pwd` +cd $vdir +/usr/bin/gmcs "$1" +cd $cdir +shift +exec "$vdir/main.exe" "$@" diff --git a/scripts/compare-wrapper-java-1.5 b/scripts/compare-wrapper-java-1.5 new file mode 100755 index 0000000..ca31a27 --- /dev/null +++ b/scripts/compare-wrapper-java-1.5 @@ -0,0 +1,16 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +cdir=`pwd` +cd $vdir +JAVAC=javac +if [ -e '/usr/lib/jvm/java-6-sun/bin/javac ] ; then + JAVAC=/usr/lib/jvm/java-6-sun/bin/javac +else if [ -e '/usr/lib/jvm/java-6-openjdk/bin/javac ] ; then + JAVAC=/usr/lib/jvm/java-6-openjdk/bin/javac +fi +$JAVAC -source 1.5 -cp . "$@" +shift +exec java -cp "$vdir" Main "$@" diff --git a/scripts/compare-wrapper-java-1.6 b/scripts/compare-wrapper-java-1.6 new file mode 100755 index 0000000..ca31a27 --- /dev/null +++ b/scripts/compare-wrapper-java-1.6 @@ -0,0 +1,16 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin + +vdir=`dirname $1` +cdir=`pwd` +cd $vdir +JAVAC=javac +if [ -e '/usr/lib/jvm/java-6-sun/bin/javac ] ; then + JAVAC=/usr/lib/jvm/java-6-sun/bin/javac +else if [ -e '/usr/lib/jvm/java-6-openjdk/bin/javac ] ; then + JAVAC=/usr/lib/jvm/java-6-openjdk/bin/javac +fi +$JAVAC -source 1.5 -cp . "$@" +shift +exec java -cp "$vdir" Main "$@" diff --git a/scripts/compare-wrapper-python-2.5 b/scripts/compare-wrapper-python-2.5 new file mode 100755 index 0000000..57146f7 --- /dev/null +++ b/scripts/compare-wrapper-python-2.5 @@ -0,0 +1,8 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin +PYTHON=python +if [ -e /usr/bin/python2.6 ] ; then + PYTHON=python +fi +exec $PYTHON "$@" diff --git a/scripts/compare-wrapper-python-2.5old b/scripts/compare-wrapper-python-2.5old new file mode 100755 index 0000000..9e3b6f0 --- /dev/null +++ b/scripts/compare-wrapper-python-2.5old @@ -0,0 +1,8 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin +PYTHON=python3 +if [ -e /usr/bin/python2.6 ] ; then + PYTHON=python3 +fi +exec $PYTHON "$@" diff --git a/scripts/compile-guard b/scripts/compile-guard new file mode 100755 index 0000000..0462a78 --- /dev/null +++ b/scripts/compile-guard @@ -0,0 +1,6 @@ +#!/bin/dash + +ulimit -t 30 +cd $1 +shift 1 +exec $@ diff --git a/scripts/f b/scripts/f new file mode 100755 index 0000000..1abcf84 --- /dev/null +++ b/scripts/f @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. +echo `dirname $0`/run-guard.py $@>fff.txt +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/fpc b/scripts/fpc new file mode 100755 index 0000000..1cb675d --- /dev/null +++ b/scripts/fpc @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin +exec /usr/bin/fpc -O3 -vw0 -dONLINE_JUDGE -Sd -omain -MDelphi "$@" 2>&1 | sed '1d' | sed '1d' diff --git a/scripts/g++-3.3 b/scripts/g++-3.3 new file mode 100755 index 0000000..67b1b91 --- /dev/null +++ b/scripts/g++-3.3 @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin +exec /usr/bin/g++ -Wall -O2 -std=c++98 -DOJ -DONLINE_JUDGE -o main "$@" -lm diff --git a/scripts/gcc-3.3-bc b/scripts/gcc-3.3-bc new file mode 100755 index 0000000..c366046 --- /dev/null +++ b/scripts/gcc-3.3-bc @@ -0,0 +1,5 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin + +exec /usr/bin/gcc -Wall -O0 -g -std=c99 -DOJ -DONLINE_JUDGE -o main "$@" -lm diff --git a/scripts/gcc-3.3-nobc b/scripts/gcc-3.3-nobc new file mode 100755 index 0000000..f6b1962 --- /dev/null +++ b/scripts/gcc-3.3-nobc @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/bin:/usr/bin +exec /usr/bin/gcc -Wall -O2 -std=c99 -DOJ -DONLINE_JUDGE -o main "$@" -lm diff --git a/scripts/gmcs-2.0 b/scripts/gmcs-2.0 new file mode 100755 index 0000000..f34dde0 --- /dev/null +++ b/scripts/gmcs-2.0 @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin +exec /usr/bin/gmcs "$@" diff --git a/scripts/java-guard b/scripts/java-guard new file mode 100755 index 0000000..c6c678e --- /dev/null +++ b/scripts/java-guard @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. + +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/javac-1.5 b/scripts/javac-1.5 new file mode 100755 index 0000000..38b9f3a --- /dev/null +++ b/scripts/javac-1.5 @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin +exec /usr/lib/jvm/java-6-sun/bin/javac -source 1.5 -cp . "$@" diff --git a/scripts/javac-1.6 b/scripts/javac-1.6 new file mode 100755 index 0000000..2592512 --- /dev/null +++ b/scripts/javac-1.6 @@ -0,0 +1,4 @@ +#!/bin/dash + +export PATH=$PATH:/usr/bin:/bin +exec /usr/lib/jvm/java-6-sun/bin/javac -source 1.6 -cp . "$@" diff --git a/scripts/mono-guard b/scripts/mono-guard new file mode 100755 index 0000000..c6c678e --- /dev/null +++ b/scripts/mono-guard @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. + +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/python-guard b/scripts/python-guard new file mode 100755 index 0000000..1abcf84 --- /dev/null +++ b/scripts/python-guard @@ -0,0 +1,7 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. +echo `dirname $0`/run-guard.py $@>fff.txt +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/python-guardd b/scripts/python-guardd new file mode 100755 index 0000000..499c445 --- /dev/null +++ b/scripts/python-guardd @@ -0,0 +1,6 @@ +#!/bin/dash + +# This is just a wrapper of run-guard program. +# AppArmor use different profile for different programs, so we create +# different scripts for different language. +exec `dirname $0`/run-guard.py $@ diff --git a/scripts/run-guard.py b/scripts/run-guard.py new file mode 100755 index 0000000..4f77dd5 --- /dev/null +++ b/scripts/run-guard.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python + +import os, sys, string, signal, resource, time, getopt, pickle + +class RunGuard: + + def __init__(self): + self.timelimit = 1 + self.memlimit = 65536 * 1024 + self.timetime = 5 + self.args = None + self.writeto = None + self.nproc = 1 + self.ofile = 32 + self.ldpreload = None + self.rundir = None + + self.usepickle = False + self.memrss = 0 + self.memdata = 0 + self.memstack = 0 + self.timeused = 0 + self.exitcode = 0 + self.sig = 0 + + def run(self): + self.parse_opts() + + self.childpid = os.fork() + if self.childpid == 0: + self.execute() + else: + self.monitor() + + self.write_result() + + def parse_opts(self): + try: + pos = sys.argv.index('-x') + optlist, self.args = getopt.gnu_getopt(sys.argv[:pos], 'e:t:m:d:o:T:p') + self.args = sys.argv[pos:] + except ValueError: + optlist, self.args = getopt.gnu_getopt(sys.argv, 'e:t:m:d:o:T:p') + for o, v in optlist: + if o == '-e': + self.nproc += string.atoi(v) + if o == '-t': + self.timelimit = string.atoi(v) + if o == '-m': + self.memlimit = string.atoi(v) * 1024 + if o == '-d': + self.rundir = v + if o == '-o': + self.writeto = v + if o == '-T': + self.timetime = string.atoi(v) + if o == '-p': + self.usepickle = True + + v = os.getenv('GUARD_RLIMIT_OFILE') + if v: self.ofile = string.atoi(v) + self.ldpreload = os.getenv('GUARD_LD_PRELOAD') + + def execute(self): + if self.rundir != None and os.path.isdir(self.rundir): + os.chdir(self.rundir) + + if self.nproc: + resource.setrlimit(resource.RLIMIT_NPROC, (self.nproc, self.nproc)) + if self.ofile: + resource.setrlimit(resource.RLIMIT_OFILE, (self.ofile, self.ofile)) + if self.ldpreload: + os.putenv('LD_PRELOAD', self.ldpreload) + + resource.setrlimit(resource.RLIMIT_CPU, (self.timelimit, self.timelimit)) + resource.setrlimit(resource.RLIMIT_AS, (self.memlimit, self.memlimit)) + os.execv(self.args[1], self.args[1:]) + sys.exit(127) # exit with JGE + + def monitor(self): + pid = 0 + remaintime = self.timelimit * self.timetime + while remaintime > 0: + pid, status, ru = os.wait4(self.childpid, os.WNOHANG) + self._get_memused() + if pid > 0: break + time.sleep(0.05) + remaintime -= 0.05 + + while pid == 0: + try: + os.kill(self.childpid, signal.SIGKILL) + except OSError, e: + pass + pid, status, ru = os.wait4(self.childpid, os.WNOHANG) + time.sleep(0.1) + + if os.WIFEXITED(status): + self.exitcode = os.WEXITSTATUS(status) + + if os.WIFSIGNALED(status): + self.sig = os.WTERMSIG(status) + + self.timeused = ru[0] + ru[1] + + def _get_memused(self): + procdir = '/proc/%d' % self.childpid + if os.path.isdir(procdir): + cmdline = file(procdir + '/cmdline', 'r').readlines() + + # do not get memory usage of this script after just fork + if len(cmdline) > 0 and \ + string.strip(cmdline[0], '\0') != \ + string.join(self.args[1:], '\0'): + return + + procstatus = file(procdir + '/status', 'r') + rss = 0; data = 0; stack = 0 + for line in procstatus: + n = line[0:6] + if n == 'VmRSS:': + rss = string.atoi(line[7:-3]) + if n == 'VmData': + data = string.atoi(line[8:-3]) + if n == 'VmStk:': + stack = string.atoi(line[7:-3]) + self.memrss = max(self.memrss, rss) + if self.memdata + self.memstack < data + stack: + self.memdata = data + self.memstack = stack + return + + def write_result(self): + if self.writeto == None: + f = sys.stdout + else: + f = file(self.writeto, 'w') + + if self.usepickle: + obj = { 'exitcode' : self.exitcode, + 'sig' : self.sig, + 'timeused' : self.timeused, + 'memrss' : self.memrss, + 'memdata' : self.memdata, + 'memstack' : self.memstack } + pickle.dump(obj, f) + else: + print >>f, "exitcode: %d" % self.exitcode + print >>f, "sig: %d" % self.sig + print >>f, "time: %.3f" % self.timeused + print >>f, "rss: %d" % self.memrss + print >>f, "data: %d" % self.memdata + print >>f, "stack: %d" % self.memstack + + if self.writeto != None: f.close() + +if __name__ == '__main__': + os.umask(0002) + RunGuard().run() diff --git a/utils/bitoj_adduser b/utils/bitoj_adduser new file mode 100755 index 0000000..9c83fb7 --- /dev/null +++ b/utils/bitoj_adduser @@ -0,0 +1,55 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +这个程序用来向系统中添加帐号。这些帐号会在 bitoj 中使用,用户提交的程序会以 +不同的用户帐号执行。 + +""" + +import os, sys, string + +rundir = '/var/lib/bitoj' + +def usage(message = None): + if message: + print message + print "Usage: bitoj_adduser minnum maxnum" + +def adduser(usernum): + global rundir + + username = 'ojrun%02d' % usernum + homedir = os.path.join(rundir, username) + cmd = 'adduser --system --group --disabled-login --home %s %s' \ + % (homedir, username) + if os.system(cmd) == 0: + os.system('adduser oj %s' % username) + os.system('adduser %s oj' % username) + os.system('chmod g+w %s' % homedir) + os.system('setquota -u %s 25600 25600 1000 1000 /' % username) + +def main(): + if len(sys.argv) < 3: + usage() + sys.exit(1) + + minnum = string.atoi(sys.argv[1]) + maxnum = string.atoi(sys.argv[2]) + + if minnum > maxnum: + usage("minnum should be small than maxnum") + sys.exit(1) + + if minnum < 1 or maxnum > 100: + usage("minnum should between 1 and 100") + sys.exit(1) + + if maxnum < 1 or maxnum > 100: + usage("maxnum should between 1 and 100") + sys.exit(1) + + for i in range(minnum, maxnum + 1): + adduser(i) + +main() diff --git a/utils/xmlrpc-debug-proxy.py b/utils/xmlrpc-debug-proxy.py new file mode 100755 index 0000000..3d30839 --- /dev/null +++ b/utils/xmlrpc-debug-proxy.py @@ -0,0 +1,141 @@ +#!/usr/bin/python + +"""HTTP debugging proxy + +(Presumably) originally by Sam Rushing + http://www.nightmare.com/medusa/programming.html + +Modified by Phillip Pearson + http://www.myelin.co.nz/notes/xmlrpc-debug-proxy.html + (Changes placed in the public domain; do what you will) + + +A very small proxy for HTTP that dumps out what it sees, so you can debug your +XML-RPC without having to decipher the output from a packet sniffer. + +This is basically the proxy used in the Medusa asynchronous sockets tutorial +(available on http://www.nightmare.com/medusa/programming.html) with a minor +adjustment to make it flush its buffers before closing any connections. Without +that it will drop off important things like :) + +Syntax: xmlrpc-debug-proxy.py + +This will listen on port 8000+ and proxy through to : + +e.g. 'aproxy.py localhost 80' listens on localhost:8080 and proxies through to + the local web server on port 80. + +To debug stuff connecting to Radio, run 'xmlrpc-debug-proxy.py localhost 5335' +and point your scripts at http://localhost:13335/RPC2 (instead of +http://localhost:5335/RPC2) + +""" + +import asynchat +import asyncore +import socket +import string + +class proxy_server (asyncore.dispatcher): + + def __init__ (self, host, port): + asyncore.dispatcher.__init__ (self) + self.create_socket (socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.there = (host, port) + here = ('', port + 8000) + self.bind (here) + self.listen (5) + + def handle_accept (self): + print 'New connection' + proxy_receiver (self, self.accept()) + +class proxy_sender (asynchat.async_chat): + + "Sends data to the server" + + def __init__ (self, receiver, address): + asynchat.async_chat.__init__ (self) + self.receiver = receiver + self.set_terminator (None) + self.create_socket (socket.AF_INET, socket.SOCK_STREAM) + self.buffer = '' + self.set_terminator ('\n') + self.connect (address) + + def handle_connect (self): + print 'Sender connected' + + def collect_incoming_data (self, data): + self.buffer = self.buffer + data + + def found_terminator (self): + data = self.buffer + self.buffer = '' + print (u'==> (%d) %s' % (self.id, unicode(repr(data), 'utf-8'))).encode('utf-8') + self.receiver.push (data + '\n') + + def handle_close (self): + print 'Sender closing (inbuf len %d (%s), ac_in %d, ac_out %d )' % ( + len( self.buffer ), + self.buffer, + len( self.ac_in_buffer ), + len( self.ac_out_buffer ) + ) + + if len( self.buffer ): + self.found_terminator() + + self.receiver.close_when_done() + self.close() + +class proxy_receiver (asynchat.async_chat): + + "Receives data from the caller" + + channel_counter = 0 + + def __init__ (self, server, (conn, addr)): + asynchat.async_chat.__init__ (self, conn) + self.set_terminator ('\n') + self.server = server + self.id = self.channel_counter + self.channel_counter = self.channel_counter + 1 + self.sender = proxy_sender (self, server.there) + self.sender.id = self.id + self.buffer = '' + + def collect_incoming_data (self, data): + self.buffer = self.buffer + data + + def found_terminator (self): + import re + data = re.sub( r'\:8080', '', self.buffer ) + data = re.sub( r'localhost', self.server.there[0], data ) + self.buffer = '' + print (u'<== (%d) %s' % (self.id, unicode(repr(data), 'utf-8'))).encode('utf-8') + self.sender.push (data + '\n') + + def handle_close (self): + print 'Receiver closing (inbuf len %d (%s), ac_in %d, ac_out %d )' % ( + len( self.buffer ), + self.buffer, + len( self.ac_in_buffer ), + len( self.ac_out_buffer ) + ) + + if len( self.buffer ): + self.found_terminator() + + self.sender.close_when_done() + self.close() + +if __name__ == '__main__': + import sys + import string + if len(sys.argv) < 3: + print 'Usage: %s ' % sys.argv[0] + else: + ps = proxy_server (sys.argv[1], string.atoi (sys.argv[2])) + asyncore.loop()