blumonilr пре 5 година
комит
297b1c61e7

+ 18 - 0
app.py

@@ -0,0 +1,18 @@
+from flask import Flask
+
+app = Flask(__name__)
+
+
+@app.route('/')
+def hello_world():
+    return 'Hello World!'
+
+
+@app.route('/api/judge', methods = ['POST'])
+def judge():
+
+    return {'result': 'System Error'}
+
+
+if __name__ == '__main__':
+    app.run()

+ 14 - 0
demo/a+b.c

@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main() {
+    int a, b;
+    
+    scanf("%d %d", &a, &b);
+    printf("%d\n", a * b);
+    // open("asdf", O_CREAT);
+    /*scanf("%d", &a);*/
+    
+    return 0;
+}

+ 74 - 0
demo/test.py

@@ -0,0 +1,74 @@
+#!/usr/bin/python
+#! -*- coding: utf8 -*-
+
+import lorun
+import os
+
+RESULT_STR = [
+    'Accepted',
+    'Presentation Error',
+    'Time Limit Exceeded',
+    'Memory Limit Exceeded',
+    'Wrong Answer',
+    'Runtime Error',
+    'Output Limit Exceeded',
+    'Compile Error',
+    'System Error'
+]
+
+def compileSrc(src_path):
+    if os.system('gcc %s -o m'%src_path) != 0:
+        print('compile failure!')
+        return False
+    return True
+
+def runone(p_path, in_path, out_path):
+    fin = open(in_path)
+    ftemp = open('temp.out', 'w')
+    
+    runcfg = {
+        'args':['./m'],
+        'fd_in':fin.fileno(),
+        'fd_out':ftemp.fileno(),
+        'timelimit':1000, #in MS
+        'memorylimit':20000, #in KB
+    }
+    
+    rst = lorun.run(runcfg)
+    fin.close()
+    ftemp.close()
+    
+    if rst['result'] == 0:
+        ftemp = open('temp.out')
+        fout = open(out_path)
+        crst = lorun.check(fout.fileno(), ftemp.fileno())
+        fout.close()
+        ftemp.close()
+        os.remove('temp.out')
+        if crst != 0:
+            return {'result':crst}
+    
+    return rst
+
+def judge(src_path, td_path, td_total):
+    if not compileSrc(src_path):
+        return
+    for i in range(td_total):
+        in_path = os.path.join(td_path, '%d.in'%i)
+        out_path = os.path.join(td_path, '%d.out'%i)
+        if os.path.isfile(in_path) and os.path.isfile(out_path):
+            rst = runone('./m', in_path, out_path)
+            rst['result'] = RESULT_STR[rst['result']]
+            print(rst)
+        else:
+            print('testdata:%d incompleted' % i)
+            os.remove('./m')
+            exit(-1)
+    os.remove('./m')
+
+if __name__ == '__main__':
+    import sys
+    if len(sys.argv) != 4:
+        print('Usage:%s srcfile testdata_pth testdata_total')
+        exit(-1)
+    judge(sys.argv[1], sys.argv[2], int(sys.argv[3]))

+ 1 - 0
demo/testdata/0.in

@@ -0,0 +1 @@
+11134 2245

+ 1 - 0
demo/testdata/0.out

@@ -0,0 +1 @@
+13379

+ 1 - 0
demo/testdata/1.in

@@ -0,0 +1 @@
+1 1

+ 1 - 0
demo/testdata/1.out

@@ -0,0 +1 @@
+2

+ 1 - 0
demo/testdata/2.in

@@ -0,0 +1 @@
+123 456

+ 1 - 0
demo/testdata/2.out

@@ -0,0 +1 @@
+579

+ 16 - 0
judge.py

@@ -0,0 +1,16 @@
+import lorun
+import os
+
+RESULT_STR = [
+    'Accepted',
+    'Presentation Error',
+    'Time Limit Exceeded',
+    'Memory Limit Exceeded',
+    'Wrong Answer',
+    'Runtime Error',
+    'Output Limit Exceeded',
+    'Compile Error',
+    'System Error'
+]
+
+

+ 1 - 0
lorun/__init__.py

@@ -0,0 +1 @@
+from ._lorun_ext import run, check

+ 92 - 0
lorun/cext/access.c

@@ -0,0 +1,92 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "access.h"
+#include <sys/syscall.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <string.h>
+
+int fileAccess(PyObject *files, const char *file, long flags) {
+    PyObject *perm_obj;
+    long perm;
+
+    if ((perm_obj = PyDict_GetItemString(files, file)) == NULL) {
+        return 0;
+    }
+    //printf("%s:%d\n",file,flags);
+    perm = PyInt_AsLong(perm_obj);
+    if (perm == flags)
+        return 1;
+
+    return 0;
+}
+
+static long file_temp[100];
+int checkAccess(struct Runobj *runobj, int pid, struct user_regs_struct *regs) {
+#if __WORDSIZE == 64
+    if (!runobj->inttable[regs->orig_rax])
+#else
+        if (!runobj->inttable[regs->orig_eax])
+#endif
+        return ACCESS_CALL_ERR;
+
+#if __WORDSIZE == 64
+    switch (regs->orig_rax)
+#else
+    switch (regs->orig_eax)
+#endif
+    {
+        case SYS_open: {
+            int i, j;
+
+            for (i = 0; i < 100; i++) {
+#if __WORDSIZE == 64
+                long t = ptrace(PTRACE_PEEKDATA, pid,
+                        regs->rdi + i * sizeof(long), NULL);
+#else
+                long t = ptrace(PTRACE_PEEKDATA, pid, regs->ebx + i*sizeof(long), NULL);
+#endif
+                file_temp[i] = t;
+                const char* test = (const char*) &file_temp[i];
+                for (j = 0; j < sizeof(long); j++) {
+                    if (!test[j]) {
+                        goto l_cont;
+                    }
+                }
+            }
+            l_cont: file_temp[99] = 0;
+
+#if __WORDSIZE == 64
+            if (fileAccess(runobj->files, (const char *) file_temp, regs->rsi))
+#else
+                if(fileAccess(runobj->files, (const char *)file_temp, regs->ecx))
+#endif
+                return ACCESS_OK;
+            return ACCESS_FILE_ERR;
+        }
+    }
+
+    return ACCESS_OK;
+}
+
+const char* lastFileAccess(void) {
+    file_temp[99] = 0;
+    return (const char*) file_temp;
+}

+ 42 - 0
lorun/cext/access.h

@@ -0,0 +1,42 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_ACCESS_HEADER
+#define __LO_ACCESS_HEADER
+
+#include "lorun.h"
+#include <sys/user.h>
+
+#define ACCESS_CALL_ERR 1
+#define ACCESS_FILE_ERR 2
+#define ACCESS_OK 0
+
+#if __WORDSIZE == 64
+    #define REG_SYS_CALL(x) ((x)->orig_rax)
+    #define REG_ARG_1(x) ((x)->rdi)
+    #define REG_ARG_2(x) ((x)->rsi)
+#else
+    #define REG_SYS_CALL(x) ((x)->orig_eax)
+    #define REG_ARG_1(x) ((x)->ebx)
+    #define REG_ARG_2(x) ((x)->ecx)
+#endif
+
+int checkAccess(struct Runobj *runobj, int pid, struct user_regs_struct *regs);
+const char* lastFileAccess(void);
+
+#endif

+ 122 - 0
lorun/cext/convert.c

@@ -0,0 +1,122 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "convert.h"
+
+int initCalls(PyObject *li, u_char calls[]) {
+    PyObject *t;
+    Py_ssize_t len, i;
+
+    memset(calls, 0, sizeof(u_char) * CALLS_MAX);
+
+    len = PyList_Size(li);
+    for (i = 0; i < len; i++) {
+        t = PyList_GetItem(li, i);
+        if (t == NULL) {return -1;}
+
+        #ifdef IS_PY3
+        if (!PyLong_Check(t))
+            RAISE1("calls must be a list of numbers.");
+        calls[PyLong_AsLong(t)] = 1;
+        #else
+        if (!PyInt_Check(t) && !PyLong_Check(t))
+            RAISE1("calls must be a list of numbers.");
+        calls[PyInt_AsLong(t)] = 1;
+        #endif
+    }
+
+    return 0;
+}
+
+PyObject *genResult(struct Result *rst) {
+    PyObject *rst_obj, *j, *t, *m;
+    if ((rst_obj = PyDict_New()) == NULL)
+        RAISE0("new dict failure");
+
+    j = PyLong_FromLong(rst->judge_result);
+    t = PyLong_FromLong(rst->time_used);
+    m = PyLong_FromLong(rst->memory_used);
+
+    if (!j || !t || !m)
+        RAISE0("set item falure(1)");
+
+    if (PyDict_SetItemString(rst_obj, "result", j)) {
+        RAISE0("set item falure");
+    }
+
+    if (PyDict_SetItemString(rst_obj, "timeused", t)
+            || PyDict_SetItemString(rst_obj, "memoryused", m)) {
+        RAISE0("set item failure");
+    }
+
+    if (rst->re_signum) {
+        PyDict_SetItemString(rst_obj, "re_signum",
+                PyLong_FromLong(rst->re_signum));
+    }
+    if (rst->re_call != -1) {
+        PyDict_SetItemString(rst_obj, "re_call", PyLong_FromLong(rst->re_call));
+    }
+    if (rst->re_file) {
+        #ifdef IS_PY3
+        PyObject *re_file = PyUnicode_FromString(rst->re_file);
+        #else
+        PyObject *re_file = PyString_FromString(rst->re_file);
+        #endif
+        PyDict_SetItemString(rst_obj, "re_file", re_file);
+        PyDict_SetItemString(rst_obj, "re_file_flag",
+            PyLong_FromLong(rst->re_file_flag));
+    }
+
+    return rst_obj;
+}
+
+char * const * genRunArgs(PyObject *args_obj) { //generate the argsments for exec*
+    PyObject *arg;
+    const char **args;
+    int len, i;
+
+    if (!PyList_Check(args_obj))
+        RAISE0("args must be a list")
+
+    len = PyList_GET_SIZE(args_obj);
+    args = (const char**) malloc(sizeof(char*) * (len + 1));
+
+    for (i = 0; i < len; i++) {
+        #ifdef IS_PY3
+        PyObject *arg_bytes;
+        #endif
+
+        if ((arg = PyList_GetItem(args_obj, i)) == NULL) {
+            free(args);
+            return NULL;
+        }
+
+        #ifdef IS_PY3
+        arg_bytes = PyUnicode_AsUTF8String(arg);
+        if (arg_bytes == NULL) {return NULL;}
+        args[i] = PyBytes_AsString(arg_bytes);
+        #else
+        args[i] = PyString_AsString(arg);
+        #endif
+
+        if (args[i] == NULL) {return NULL;}
+    }
+    args[i] = NULL;
+
+    return (char * const *) args;
+}

+ 28 - 0
lorun/cext/convert.h

@@ -0,0 +1,28 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_CONVERT_HEADER
+#define __LO_CONVERT_HEADER
+
+#include "lorun.h"
+
+int initCalls(PyObject *li, u_char calls[]);
+PyObject *genResult(struct Result *rst);
+char * const * genRunArgs(PyObject *args_obj);
+
+#endif

+ 112 - 0
lorun/cext/diff.c

@@ -0,0 +1,112 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "diff.h"
+#include <sys/mman.h>
+
+int equalStr(const char *s, const char *s2) {
+    while (*s && *s2) {
+        if (*s++ != *s2++) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+#define RETURN(rst) {*result = rst;return 0;}
+int checkDiff(int rightout_fd, int userout_fd, int *result) {
+    char *userout, *rightout;
+    const char *cuser, *cright, *end_user, *end_right;
+
+    off_t userout_len, rightout_len;
+    userout_len = lseek(userout_fd, 0, SEEK_END);
+    rightout_len = lseek(rightout_fd, 0, SEEK_END);
+
+    if (userout_len == -1 || rightout_len == -1)
+        RAISE1("lseek failure");
+
+    if (userout_len >= MAX_OUTPUT)
+        RETURN(OLE);
+
+    lseek(userout_fd, 0, SEEK_SET);
+    lseek(rightout_fd, 0, SEEK_SET);
+
+    if ((userout_len && rightout_len) == 0) {
+        if (userout_len || rightout_len)
+            RETURN(WA)
+        else
+            RETURN(AC)
+    }
+
+    if ((userout = (char*) mmap(NULL, userout_len, PROT_READ | PROT_WRITE,
+            MAP_PRIVATE, userout_fd, 0)) == MAP_FAILED) {
+        RAISE1("mmap userout filure");
+    }
+
+    if ((rightout = (char*) mmap(NULL, rightout_len, PROT_READ | PROT_WRITE,
+            MAP_PRIVATE, rightout_fd, 0)) == MAP_FAILED) {
+        munmap(userout, userout_len);
+        RAISE1("mmap right filure");
+    }
+
+    if ((userout_len == rightout_len) && equalStr(userout, rightout) == 0) {
+        munmap(userout, userout_len);
+        munmap(rightout, rightout_len);
+        RETURN(AC);
+    }
+
+    cuser = userout;
+    cright = rightout;
+    end_user = userout + userout_len;
+    end_right = rightout + rightout_len;
+    while ((cuser < end_user) && (cright < end_right)) {
+        while ((cuser < end_user)
+                && (*cuser == ' ' || *cuser == '\n' || *cuser == '\r'
+                        || *cuser == '\t'))
+            cuser++;
+        while ((cright < end_right)
+                && (*cright == ' ' || *cright == '\n' || *cright == '\r'
+                        || *cright == '\t'))
+            cright++;
+        if (cuser == end_user || cright == end_right)
+            break;
+        if (*cuser != *cright)
+            break;
+        cuser++;
+        cright++;
+    }
+    while ((cuser < end_user)
+            && (*cuser == ' ' || *cuser == '\n' || *cuser == '\r'
+                    || *cuser == '\t'))
+        cuser++;
+    while ((cright < end_right)
+            && (*cright == ' ' || *cright == '\n' || *cright == '\r'
+                    || *cright == '\t'))
+        cright++;
+    if (cuser == end_user && cright == end_right) {
+        munmap(userout, userout_len);
+        munmap(rightout, rightout_len);
+        RETURN(PE);
+    }
+
+    munmap(userout, userout_len);
+    munmap(rightout, rightout_len);
+    RETURN(WA);
+}
+

+ 26 - 0
lorun/cext/diff.h

@@ -0,0 +1,26 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_DIFF_HEADER
+#define __LO_DIFF_HEADER
+
+#include "lorun.h"
+
+int checkDiff(int rightout_fd, int userout_fd, int *result);
+
+#endif

+ 61 - 0
lorun/cext/limit.c

@@ -0,0 +1,61 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "limit.h"
+#include <sys/resource.h>
+#include <sys/time.h>
+
+const char *last_limit_err;
+
+int setResLimit(struct Runobj *runobj) {
+#define RAISE_EXIT(err) {last_limit_err = err;return -1;}
+    struct rlimit rl;
+    /*struct itimerval p_realt;*/
+
+    rl.rlim_cur = runobj->time_limit / 1000 + 1;
+    if (runobj->time_limit % 1000 > 800) {
+        rl.rlim_cur += 1;
+    }
+    rl.rlim_max = rl.rlim_cur + 1;
+    if (setrlimit(RLIMIT_CPU, &rl))
+        RAISE_EXIT("set RLIMIT_CPU failure");
+
+    rl.rlim_cur = runobj->memory_limit * 1024;
+    rl.rlim_max = rl.rlim_cur + 1024;
+    if (setrlimit(RLIMIT_DATA, &rl))
+        RAISE_EXIT("set RLIMIT_DATA failure");
+
+    rl.rlim_cur = runobj->memory_limit * 1024 * 2;
+    rl.rlim_max = rl.rlim_cur + 1024;
+    if (setrlimit(RLIMIT_AS, &rl))
+        RAISE_EXIT("set RLIMIT_AS failure");
+
+    rl.rlim_cur = 256 * 1024 * 1024;
+    rl.rlim_max = rl.rlim_cur + 1024;
+    if (setrlimit(RLIMIT_STACK, &rl))
+        RAISE_EXIT("set RLIMIT_STACK failure");
+
+    /*
+    p_realt.it_interval.tv_sec = runobj->time_limit / 1000 + 3;
+    p_realt.it_interval.tv_usec = 0;
+    p_realt.it_value = p_realt.it_interval;
+    if (setitimer(ITIMER_REAL, &p_realt, (struct itimerval *) 0) == -1)
+        RAISE_EXIT("set ITIMER_REAL failure");*/
+
+    return 0;
+}

+ 26 - 0
lorun/cext/limit.h

@@ -0,0 +1,26 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_LIMIT_HEADER
+#define __LO_LIMIT_HEADER
+
+#include "lorun.h"
+
+int setResLimit(struct Runobj *runobj);
+extern const char *last_limit_err;
+#endif

+ 193 - 0
lorun/cext/lorun.c

@@ -0,0 +1,193 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "lorun.h"
+#include "convert.h"
+#include "run.h"
+#include "diff.h"
+
+
+int initRun(struct Runobj *runobj, PyObject *args)
+{
+    PyObject *config, *args_obj, *trace_obj, *time_obj, *memory_obj;
+    PyObject *calls_obj, *runner_obj, *fd_obj;
+    
+    if (!PyArg_ParseTuple(args, "O", &config))
+        RAISE1("initRun parseTuple failure");
+    if(!PyDict_Check(config)) RAISE1("argument must be a dict");
+    
+    if((args_obj = PyDict_GetItemString(config, "args")) == NULL)
+        RAISE1("must supply args");
+    
+    if((runobj->args = genRunArgs(args_obj)) == NULL) return -1;
+    
+    if((fd_obj = PyDict_GetItemString(config, "fd_in")) == NULL)
+        runobj->fd_in = -1;
+    else runobj->fd_in = PyLong_AsLong(fd_obj);
+    if((fd_obj = PyDict_GetItemString(config, "fd_out")) == NULL)
+        runobj->fd_out = -1;
+    else runobj->fd_out = PyLong_AsLong(fd_obj);
+    if((fd_obj = PyDict_GetItemString(config, "fd_err")) == NULL)
+        runobj->fd_err = -1;
+    else runobj->fd_err = PyLong_AsLong(fd_obj);
+      
+    if((time_obj = PyDict_GetItemString(config, "timelimit")) == NULL)
+        RAISE1("must supply timelimit");
+    runobj->time_limit = PyLong_AsLong(time_obj);
+    
+    if((memory_obj = PyDict_GetItemString(config, "memorylimit")) == NULL)
+        RAISE1("must supply memorylimit");
+    runobj->memory_limit = PyLong_AsLong(memory_obj);
+    
+    if((runner_obj = PyDict_GetItemString(config, "runner")) == NULL)
+        runobj->runner = -1;
+    else
+        runobj->runner = PyLong_AsLong(runner_obj);
+    
+    if((trace_obj = PyDict_GetItemString(config, "trace")) != NULL){
+        if(trace_obj == Py_True){
+            runobj->trace = 1;
+            //trace mode: supply calls and files to access.
+            if((calls_obj = PyDict_GetItemString(config, "calls")) == NULL)
+                RAISE1("trace == True, so you must specify calls.");
+            if(!PyList_Check(calls_obj))
+                RAISE1("calls must be a list.");
+            if(initCalls(calls_obj, runobj->inttable)) return -1;
+            
+            if((runobj->files = PyDict_GetItemString(config, "files")) == NULL)
+                RAISE1("trace == True, so you must specify files.");
+            if(!PyDict_Check(runobj->files))
+                RAISE1("files must be a dcit.");
+        } else runobj->trace = 0;
+    } else runobj->trace = 0;
+    
+    return 0;
+}
+
+PyObject *run(PyObject *self, PyObject *args)
+{
+    struct Runobj runobj = {0};
+    struct Result rst = {0};
+    rst.re_call = -1;
+    
+    if(initRun(&runobj, args)){
+        if(runobj.args)
+            free((void*)runobj.args);
+        return NULL;
+    }
+    
+    if(runit(&runobj, &rst) == -1)
+        return NULL;
+    if(runobj.args)
+        free((void*)runobj.args);
+    
+    return genResult(&rst);
+}
+
+PyObject* check(PyObject *self, PyObject *args)
+{
+    int user_fd, right_fd, rst;
+    
+    if (!PyArg_ParseTuple(args, "ii", &right_fd, &user_fd))
+        RAISE0("run parseTuple failure");
+    
+    if(checkDiff(right_fd, user_fd, &rst) == -1)
+        return NULL;
+    
+    return Py_BuildValue("i", rst);
+}
+
+#define run_description "run(argv_dict):\n"\
+    "\targv_dict contains:\n"\
+    "\t@args : cmd to run\n"\
+    "\t@fd_in, fd_out, fd_err : stdin,stdout,stderr fd\n"\
+    "\t@timelimit : program time limit\n"\
+    "\t@memorylimit : program memory limit\n"\
+    "\t@runner : run user\n"\
+    "\t@trace : trace?"
+
+#define check_description "check(right_fd, userout_fd)\n"
+
+static PyMethodDef lorun_methods[] = {
+	{"run", run, METH_VARARGS, run_description},
+	{"check", check, METH_VARARGS, check_description},
+	{NULL, NULL, 0, NULL}
+};
+
+
+struct module_state {
+    PyObject *error;
+};
+
+#ifdef IS_PY3
+
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+static int lorun_ext_traverse(PyObject *m, visitproc visit, void *arg) {
+    Py_VISIT(GETSTATE(m)->error);
+    return 0;
+}
+
+static int lorun_ext_clear(PyObject *m) {
+    Py_CLEAR(GETSTATE(m)->error);
+    return 0;
+}
+
+static struct PyModuleDef moduledef = {
+        PyModuleDef_HEAD_INIT,
+        "_lorun_ext",
+        NULL,
+        sizeof(struct module_state),
+        lorun_methods,
+        NULL,
+        lorun_ext_traverse,
+        lorun_ext_clear,
+        NULL
+};
+
+PyObject *PyInit__lorun_ext(void) {
+    struct module_state *st;
+    PyObject *module = PyModule_Create(&moduledef);
+    if (module == NULL)
+        return NULL;
+
+    st = GETSTATE(module);
+    st->error = PyErr_NewException("_lorun_ext.Error", NULL, NULL);
+    if (st->error == NULL) {
+        Py_DECREF(module);
+        return NULL;
+    }
+
+    return module;
+}
+
+#else
+
+static struct module_state _state;
+void init_lorun_ext(void) {
+    PyObject *module = Py_InitModule("_lorun_ext", lorun_methods);
+    if (module == NULL) {
+        return;
+    }
+
+    _state.error = PyErr_NewException("_lorun_ext.Error", NULL, NULL);
+    if (_state.error == NULL) {
+        Py_DECREF(module);
+        return;
+    }
+}
+#endif

+ 72 - 0
lorun/cext/lorun.h

@@ -0,0 +1,72 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_GCC_HEADER
+#define __LO_GCC_HEADER
+
+#include <Python.h>
+#include <sys/types.h>
+#define CALLS_MAX 400
+#define MAX_OUTPUT 100000000
+
+enum JUDGE_RESULT {
+    AC=0,   //0 Accepted
+    PE,	    //1 Presentation Error
+    TLE,	//2 Time Limit Exceeded
+    MLE,	//3 Memory Limit Exceeded
+    WA,	    //4 Wrong Answer
+    RE,	    //5 Runtime Error
+    OLE,	//6 Output Limit Exceeded
+    CE,	    //7 Compile Error
+    SE,     //8 System Error
+};
+
+struct Result {
+    int judge_result; //JUDGE_RESULT
+    int time_used, memory_used;
+    int re_signum;
+    int re_call;
+    const char* re_file;
+    int re_file_flag;
+};
+
+struct Runobj {
+    PyObject *files;
+    u_char inttable[CALLS_MAX];
+    char * const* args;
+
+    int fd_in, fd_out, fd_err;
+    int time_limit, memory_limit;
+    int runner;
+    int trace;
+};
+
+#define RAISE(msg) PyErr_SetString(PyExc_Exception,msg);
+
+#define RAISE0(msg) \
+{PyErr_SetString(PyExc_Exception,msg); return NULL;}
+
+#define RAISE1(msg) \
+{PyErr_SetString(PyExc_Exception,msg); return -1;}
+
+
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3
+#endif
+
+#endif

+ 242 - 0
lorun/cext/run.c

@@ -0,0 +1,242 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#include "run.h"
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "access.h"
+#include "limit.h"
+
+const char *last_run_err;
+#define RAISE_RUN(err) {last_run_err = err;return -1;}
+
+int traceLoop(struct Runobj *runobj, struct Result *rst, pid_t pid) {
+    int status, incall = 0;
+    struct rusage ru;
+    struct user_regs_struct regs;
+
+    while (1) {
+        if (wait4(pid, &status, WSTOPPED, &ru) == -1)
+            RAISE_RUN("wait4 [WSTOPPED] failure");
+
+        if (WIFEXITED(status))
+            break;
+        else if (WSTOPSIG(status) != SIGTRAP) {
+            ptrace(PTRACE_KILL, pid, NULL, NULL);
+            waitpid(pid, NULL, 0);
+
+            rst->time_used = ru.ru_utime.tv_sec * 1000
+                    + ru.ru_utime.tv_usec / 1000
+                    + ru.ru_stime.tv_sec * 1000
+                    + ru.ru_stime.tv_usec / 1000;
+            rst->memory_used = ru.ru_maxrss;
+
+            switch (WSTOPSIG(status)) {
+                case SIGSEGV:
+                    if (rst->memory_used > runobj->memory_limit)
+                        rst->judge_result = MLE;
+                    else
+                        rst->judge_result = RE;
+                    break;
+                case SIGALRM:
+                case SIGXCPU:
+                    rst->judge_result = TLE;
+                    break;
+                default:
+                    rst->judge_result = RE;
+                    break;
+            }
+
+            rst->re_signum = WSTOPSIG(status);
+            rst->time_used = ru.ru_utime.tv_sec * 1000
+                    + ru.ru_utime.tv_usec / 1000
+                    + ru.ru_stime.tv_sec * 1000
+                    + ru.ru_stime.tv_usec / 1000;
+            rst->memory_used = ru.ru_maxrss;
+            return 0;
+        }
+
+        if (ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1)
+            RAISE_RUN("PTRACE_GETREGS failure");
+
+        if (incall) {
+            int ret = checkAccess(runobj, pid, &regs);
+            if (ret != ACCESS_OK) {
+                ptrace(PTRACE_KILL, pid, NULL, NULL);
+                waitpid(pid, NULL, 0);
+
+                rst->time_used = ru.ru_utime.tv_sec * 1000
+                        + ru.ru_utime.tv_usec / 1000
+                        + ru.ru_stime.tv_sec * 1000
+                        + ru.ru_stime.tv_usec / 1000;
+                rst->memory_used = ru.ru_maxrss
+                        * (sysconf(_SC_PAGESIZE) / 1024);
+
+                rst->judge_result = RE;
+                if (ret == ACCESS_CALL_ERR) {
+                    rst->re_call = REG_SYS_CALL(&regs);
+                } else {
+                    rst->re_file = lastFileAccess();
+                    rst->re_file_flag = REG_ARG_2(&regs);
+                }
+                return 0;
+            }
+            incall = 0;
+        } else
+            incall = 1;
+
+        ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
+    }
+    
+    
+    rst->time_used = ru.ru_utime.tv_sec * 1000
+            + ru.ru_utime.tv_usec / 1000
+            + ru.ru_stime.tv_sec * 1000
+            + ru.ru_stime.tv_usec / 1000;
+    rst->memory_used = ru.ru_maxrss;
+
+
+    if (rst->time_used > runobj->time_limit)
+        rst->judge_result = TLE;
+    else if (rst->memory_used > runobj->memory_limit)
+        rst->judge_result = MLE;
+    else
+        rst->judge_result = AC;
+
+    return 0;
+}
+
+int waitExit(struct Runobj *runobj, struct Result *rst, pid_t pid) {
+    int status;
+    struct rusage ru;
+
+    if (wait4(pid, &status, 0, &ru) == -1)
+        RAISE_RUN("wait4 failure");
+
+    rst->time_used = ru.ru_utime.tv_sec * 1000
+            + ru.ru_utime.tv_usec / 1000
+            + ru.ru_stime.tv_sec * 1000
+            + ru.ru_stime.tv_usec / 1000;
+    rst->memory_used = ru.ru_maxrss;
+
+    if (WIFSIGNALED(status)) {
+        switch (WTERMSIG(status)) {
+            case SIGSEGV:
+                if (rst->memory_used > runobj->memory_limit)
+                    rst->judge_result = MLE;
+                else
+                    rst->judge_result = RE;
+                break;
+            case SIGALRM:
+            case SIGXCPU:
+                rst->judge_result = TLE;
+                break;
+            default:
+                rst->judge_result = RE;
+                break;
+        }
+        rst->re_signum = WTERMSIG(status);
+    } else {
+        if (rst->time_used > runobj->time_limit)
+            rst->judge_result = TLE;
+        else if (rst->memory_used > runobj->memory_limit)
+            rst->judge_result = MLE;
+        else
+            rst->judge_result = AC;
+    }
+
+    return 0;
+}
+
+int runit(struct Runobj *runobj, struct Result *rst) {
+    pid_t pid;
+    int fd_err[2];
+
+    if (pipe2(fd_err, O_NONBLOCK))
+        RAISE1("run :pipe2(fd_err) failure");
+
+    pid = vfork();
+    if (pid < 0) {
+        close(fd_err[0]);
+        close(fd_err[1]);
+        RAISE1("run : vfork failure");
+    }
+
+    if (pid == 0) {
+        close(fd_err[0]);
+
+#define RAISE_EXIT(err) {\
+            int r = write(fd_err[1],err,strlen(err));\
+            _exit(r);\
+        }
+
+        if (runobj->fd_in != -1)
+            if (dup2(runobj->fd_in, 0) == -1)
+                RAISE_EXIT("dup2 stdin failure!")
+
+        if (runobj->fd_out != -1)
+            if (dup2(runobj->fd_out, 1) == -1)
+                RAISE_EXIT("dup2 stdout failure")
+
+        if (runobj->fd_err != -1)
+            if (dup2(runobj->fd_err, 2) == -1)
+                RAISE_EXIT("dup2 stderr failure")
+
+        if (setResLimit(runobj) == -1)
+            RAISE_EXIT(last_limit_err)
+
+        if (runobj->runner != -1)
+            if (setuid(runobj->runner))
+                RAISE_EXIT("setuid failure")
+
+        if (runobj->trace)
+            if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1)
+                RAISE_EXIT("TRACEME failure")
+
+        execvp(runobj->args[0], (char * const *) runobj->args);
+
+        RAISE_EXIT("execvp failure")
+    } else {
+        int r;
+        char errbuffer[100] = { 0 };
+
+        close(fd_err[1]);
+        r = read(fd_err[0], errbuffer, 90);
+        close(fd_err[0]);
+        if (r > 0) {
+            waitpid(pid, NULL, WNOHANG);
+            RAISE(errbuffer);
+            return -1;
+        }
+
+        if (runobj->trace)
+            r = traceLoop(runobj, rst, pid);
+        else
+            r = waitExit(runobj, rst, pid);
+
+        if (r)
+            RAISE1(last_run_err);
+        return 0;
+    }
+}
+

+ 26 - 0
lorun/cext/run.h

@@ -0,0 +1,26 @@
+/**
+ * Loco program runner core
+ * Copyright (C) 2011  Lodevil(Du Jiong)
+ *
+ * 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/>.
+ */
+
+#ifndef __LO_RUN_HEADER
+#define __LO_RUN_HEADER
+
+#include "lorun.h"
+
+int runit(struct Runobj *runobj, struct Result *rst);
+
+#endif

+ 1 - 0
requirements.txt

@@ -0,0 +1 @@
+Flask~=1.1.2

+ 16 - 0
setup.py

@@ -0,0 +1,16 @@
+#!/usr/bin/python
+# -*coding:utf-8*-
+
+from distutils.core import setup, Extension
+
+sources = [
+    'lorun/cext/lorun.c', 'lorun/cext/convert.c', 'lorun/cext/access.c',
+    'lorun/cext/limit.c', 'lorun/cext/run.c', 'lorun/cext/diff.c'
+]
+
+setup(name='lorun',
+      version='1.0.1',
+      description='loco program runner core',
+      ext_modules=[Extension('lorun/_lorun_ext', sources=sources)],
+      packages=['lorun']
+      )