├── .gitignore ├── README.md ├── demo ├── a+b.c ├── test.py └── testdata │ ├── 0.in │ ├── 0.out │ ├── 1.in │ ├── 1.out │ ├── 2.in │ └── 2.out ├── lorun ├── __init__.py └── cext │ ├── access.c │ ├── access.h │ ├── convert.c │ ├── convert.h │ ├── diff.c │ ├── diff.h │ ├── limit.c │ ├── limit.h │ ├── lorun.c │ ├── lorun.h │ ├── run.c │ └── run.h └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.so 3 | *.swp 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Loco program runner core 2 | ======================== 3 | 4 | We use this python-c library to run program in a sandbox-like environment. 5 | With it, we can accurately known the resource using of the program and 6 | limit its resource using including system-call interrupt. 7 | 8 | Usage 9 | ----- 10 | 11 | For run a program without tracing: 12 | 13 | runcfg = { 14 | 'args':['./m'], 15 | 'fd_in':fin.fileno(), 16 | 'fd_out':ftemp.fileno(), 17 | 'timelimit':1000, #in MS 18 | 'memorylimit':20000, #in KB 19 | } 20 | 21 | rst = lorun.run(runcfg) 22 | 23 | For check one output: 24 | 25 | ftemp = file('temp.out') 26 | fout = file(out_path) 27 | crst = lorun.check(fout.fileno(), ftemp.fileno()) 28 | 29 | 30 | trace 31 | ----- 32 | 33 | You can set runcfg['trace'] to True to ensure runner's security. 34 | 35 | Here is a simple usage: 36 | 37 | ``` 38 | runcfg['trace'] = True 39 | runcfg['calls'] = [1, 2, 3, 4] # system calls that could be used by testing programs 40 | runcfg['files'] = {'/etc/ld.so.cache': 0} # open flag permitted (value is the flags of open) 41 | ``` 42 | -------------------------------------------------------------------------------- /demo/a+b.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | int a, b; 7 | 8 | scanf("%d %d", &a, &b); 9 | printf("%d\n", a + b); 10 | // open("asdf", O_CREAT); 11 | /*scanf("%d", &a);*/ 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /demo/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #! -*- coding: utf8 -*- 3 | 4 | import lorun 5 | import os 6 | 7 | RESULT_STR = [ 8 | 'Accepted', 9 | 'Presentation Error', 10 | 'Time Limit Exceeded', 11 | 'Memory Limit Exceeded', 12 | 'Wrong Answer', 13 | 'Runtime Error', 14 | 'Output Limit Exceeded', 15 | 'Compile Error', 16 | 'System Error' 17 | ] 18 | 19 | def compileSrc(src_path): 20 | if os.system('gcc %s -o m'%src_path) != 0: 21 | print('compile failure!') 22 | return False 23 | return True 24 | 25 | def runone(p_path, in_path, out_path): 26 | fin = open(in_path) 27 | ftemp = open('temp.out', 'w') 28 | 29 | runcfg = { 30 | 'args':['./m'], 31 | 'fd_in':fin.fileno(), 32 | 'fd_out':ftemp.fileno(), 33 | 'timelimit':1000, #in MS 34 | 'memorylimit':20000, #in KB 35 | } 36 | 37 | rst = lorun.run(runcfg) 38 | fin.close() 39 | ftemp.close() 40 | 41 | if rst['result'] == 0: 42 | ftemp = open('temp.out') 43 | fout = open(out_path) 44 | crst = lorun.check(fout.fileno(), ftemp.fileno()) 45 | fout.close() 46 | ftemp.close() 47 | os.remove('temp.out') 48 | if crst != 0: 49 | return {'result':crst} 50 | 51 | return rst 52 | 53 | def judge(src_path, td_path, td_total): 54 | if not compileSrc(src_path): 55 | return 56 | for i in range(td_total): 57 | in_path = os.path.join(td_path, '%d.in'%i) 58 | out_path = os.path.join(td_path, '%d.out'%i) 59 | if os.path.isfile(in_path) and os.path.isfile(out_path): 60 | rst = runone('./m', in_path, out_path) 61 | rst['result'] = RESULT_STR[rst['result']] 62 | print(rst) 63 | else: 64 | print('testdata:%d incompleted' % i) 65 | os.remove('./m') 66 | exit(-1) 67 | os.remove('./m') 68 | 69 | if __name__ == '__main__': 70 | import sys 71 | if len(sys.argv) != 4: 72 | print('Usage:%s srcfile testdata_pth testdata_total') 73 | exit(-1) 74 | judge(sys.argv[1], sys.argv[2], int(sys.argv[3])) 75 | -------------------------------------------------------------------------------- /demo/testdata/0.in: -------------------------------------------------------------------------------- 1 | 11134 2245 2 | -------------------------------------------------------------------------------- /demo/testdata/0.out: -------------------------------------------------------------------------------- 1 | 13379 2 | -------------------------------------------------------------------------------- /demo/testdata/1.in: -------------------------------------------------------------------------------- 1 | 1 1 2 | -------------------------------------------------------------------------------- /demo/testdata/1.out: -------------------------------------------------------------------------------- 1 | 2 2 | -------------------------------------------------------------------------------- /demo/testdata/2.in: -------------------------------------------------------------------------------- 1 | 123 456 2 | -------------------------------------------------------------------------------- /demo/testdata/2.out: -------------------------------------------------------------------------------- 1 | 579 2 | -------------------------------------------------------------------------------- /lorun/__init__.py: -------------------------------------------------------------------------------- 1 | from ._lorun_ext import run, check -------------------------------------------------------------------------------- /lorun/cext/access.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "access.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | int fileAccess(PyObject *files, const char *file, long flags) { 26 | PyObject *perm_obj; 27 | long perm; 28 | 29 | if ((perm_obj = PyDict_GetItemString(files, file)) == NULL) { 30 | return 0; 31 | } 32 | //printf("%s:%d\n",file,flags); 33 | #ifdef IS_PY3 34 | perm = PyLong_AsLong(perm_obj); 35 | #else 36 | perm = PyInt_AsLong(perm_obj); 37 | #endif 38 | if (perm == flags) 39 | return 1; 40 | 41 | return 0; 42 | } 43 | 44 | static long file_temp[100]; 45 | int checkAccess(struct Runobj *runobj, int pid, struct user_regs_struct *regs) { 46 | if (!runobj->inttable[REG_SYS_CALL(regs)]) 47 | return ACCESS_CALL_ERR; 48 | 49 | switch (REG_SYS_CALL(regs)) { 50 | case SYS_open: { 51 | int i, j; 52 | 53 | for (i = 0; i < 100; i++) { 54 | const char* test; 55 | long t = ptrace(PTRACE_PEEKDATA, pid, 56 | REG_ARG_1(regs) + i * sizeof(long), NULL); 57 | file_temp[i] = t; 58 | test = (const char*) &file_temp[i]; 59 | for (j = 0; j < sizeof(long); j++) { 60 | if (!test[j]) { 61 | goto l_cont; 62 | } 63 | } 64 | } 65 | l_cont: file_temp[99] = 0; 66 | 67 | if (fileAccess(runobj->files, (const char*)file_temp, 68 | REG_ARG_2(regs))) { 69 | return ACCESS_OK; 70 | } 71 | 72 | return ACCESS_FILE_ERR; 73 | } 74 | } 75 | 76 | return ACCESS_OK; 77 | } 78 | 79 | const char* lastFileAccess(void) { 80 | file_temp[99] = 0; 81 | return (const char*) file_temp; 82 | } 83 | -------------------------------------------------------------------------------- /lorun/cext/access.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_ACCESS_HEADER 20 | #define __LO_ACCESS_HEADER 21 | 22 | #include "lorun.h" 23 | #include 24 | 25 | #define ACCESS_CALL_ERR 1 26 | #define ACCESS_FILE_ERR 2 27 | #define ACCESS_OK 0 28 | 29 | #if __WORDSIZE == 64 30 | #define REG_SYS_CALL(x) ((x)->orig_rax) 31 | #define REG_ARG_1(x) ((x)->rdi) 32 | #define REG_ARG_2(x) ((x)->rsi) 33 | #else 34 | #define REG_SYS_CALL(x) ((x)->orig_eax) 35 | #define REG_ARG_1(x) ((x)->ebx) 36 | #define REG_ARG_2(x) ((x)->ecx) 37 | #endif 38 | 39 | int checkAccess(struct Runobj *runobj, int pid, struct user_regs_struct *regs); 40 | const char* lastFileAccess(void); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /lorun/cext/convert.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "convert.h" 20 | 21 | int initCalls(PyObject *li, u_char calls[]) { 22 | PyObject *t; 23 | Py_ssize_t len, i; 24 | 25 | memset(calls, 0, sizeof(u_char) * CALLS_MAX); 26 | 27 | len = PyList_Size(li); 28 | for (i = 0; i < len; i++) { 29 | t = PyList_GetItem(li, i); 30 | if (t == NULL) {return -1;} 31 | 32 | #ifdef IS_PY3 33 | if (!PyLong_Check(t)) 34 | RAISE1("calls must be a list of numbers."); 35 | calls[PyLong_AsLong(t)] = 1; 36 | #else 37 | if (!PyInt_Check(t) && !PyLong_Check(t)) 38 | RAISE1("calls must be a list of numbers."); 39 | calls[PyInt_AsLong(t)] = 1; 40 | #endif 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | PyObject *genResult(struct Result *rst) { 47 | PyObject *rst_obj, *j, *t, *m; 48 | if ((rst_obj = PyDict_New()) == NULL) 49 | RAISE0("new dict failure"); 50 | 51 | j = PyLong_FromLong(rst->judge_result); 52 | t = PyLong_FromLong(rst->time_used); 53 | m = PyLong_FromLong(rst->memory_used); 54 | 55 | if (!j || !t || !m) 56 | RAISE0("set item falure(1)"); 57 | 58 | if (PyDict_SetItemString(rst_obj, "result", j)) { 59 | RAISE0("set item falure"); 60 | } 61 | 62 | if (PyDict_SetItemString(rst_obj, "timeused", t) 63 | || PyDict_SetItemString(rst_obj, "memoryused", m)) { 64 | RAISE0("set item failure"); 65 | } 66 | 67 | if (rst->re_signum) { 68 | PyDict_SetItemString(rst_obj, "re_signum", 69 | PyLong_FromLong(rst->re_signum)); 70 | } 71 | if (rst->re_call != -1) { 72 | PyDict_SetItemString(rst_obj, "re_call", PyLong_FromLong(rst->re_call)); 73 | } 74 | if (rst->re_file) { 75 | #ifdef IS_PY3 76 | PyObject *re_file = PyUnicode_FromString(rst->re_file); 77 | #else 78 | PyObject *re_file = PyString_FromString(rst->re_file); 79 | #endif 80 | PyDict_SetItemString(rst_obj, "re_file", re_file); 81 | PyDict_SetItemString(rst_obj, "re_file_flag", 82 | PyLong_FromLong(rst->re_file_flag)); 83 | } 84 | 85 | return rst_obj; 86 | } 87 | 88 | char * const * genRunArgs(PyObject *args_obj) { //generate the argsments for exec* 89 | PyObject *arg; 90 | const char **args; 91 | int len, i; 92 | 93 | if (!PyList_Check(args_obj)) 94 | RAISE0("args must be a list") 95 | 96 | len = PyList_GET_SIZE(args_obj); 97 | args = (const char**) malloc(sizeof(char*) * (len + 1)); 98 | 99 | for (i = 0; i < len; i++) { 100 | #ifdef IS_PY3 101 | PyObject *arg_bytes; 102 | #endif 103 | 104 | if ((arg = PyList_GetItem(args_obj, i)) == NULL) { 105 | free(args); 106 | return NULL; 107 | } 108 | 109 | #ifdef IS_PY3 110 | arg_bytes = PyUnicode_AsUTF8String(arg); 111 | if (arg_bytes == NULL) {return NULL;} 112 | args[i] = PyBytes_AsString(arg_bytes); 113 | #else 114 | args[i] = PyString_AsString(arg); 115 | #endif 116 | 117 | if (args[i] == NULL) {return NULL;} 118 | } 119 | args[i] = NULL; 120 | 121 | return (char * const *) args; 122 | } 123 | -------------------------------------------------------------------------------- /lorun/cext/convert.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_CONVERT_HEADER 20 | #define __LO_CONVERT_HEADER 21 | 22 | #include "lorun.h" 23 | 24 | int initCalls(PyObject *li, u_char calls[]); 25 | PyObject *genResult(struct Result *rst); 26 | char * const * genRunArgs(PyObject *args_obj); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /lorun/cext/diff.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "diff.h" 20 | #include 21 | 22 | int equalStr(const char *s, const char *s2) { 23 | while (*s && *s2) { 24 | if (*s++ != *s2++) { 25 | return 1; 26 | } 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | #define RETURN(rst) {*result = rst;return 0;} 33 | int checkDiff(int rightout_fd, int userout_fd, int *result) { 34 | char *userout, *rightout; 35 | const char *cuser, *cright, *end_user, *end_right; 36 | 37 | off_t userout_len, rightout_len; 38 | userout_len = lseek(userout_fd, 0, SEEK_END); 39 | rightout_len = lseek(rightout_fd, 0, SEEK_END); 40 | 41 | if (userout_len == -1 || rightout_len == -1) 42 | RAISE1("lseek failure"); 43 | 44 | if (userout_len >= MAX_OUTPUT) 45 | RETURN(OLE); 46 | 47 | lseek(userout_fd, 0, SEEK_SET); 48 | lseek(rightout_fd, 0, SEEK_SET); 49 | 50 | if ((userout_len && rightout_len) == 0) { 51 | if (userout_len || rightout_len) 52 | RETURN(WA) 53 | else 54 | RETURN(AC) 55 | } 56 | 57 | if ((userout = (char*) mmap(NULL, userout_len, PROT_READ | PROT_WRITE, 58 | MAP_PRIVATE, userout_fd, 0)) == MAP_FAILED) { 59 | RAISE1("mmap userout filure"); 60 | } 61 | 62 | if ((rightout = (char*) mmap(NULL, rightout_len, PROT_READ | PROT_WRITE, 63 | MAP_PRIVATE, rightout_fd, 0)) == MAP_FAILED) { 64 | munmap(userout, userout_len); 65 | RAISE1("mmap right filure"); 66 | } 67 | 68 | if ((userout_len == rightout_len) && equalStr(userout, rightout) == 0) { 69 | munmap(userout, userout_len); 70 | munmap(rightout, rightout_len); 71 | RETURN(AC); 72 | } 73 | 74 | cuser = userout; 75 | cright = rightout; 76 | end_user = userout + userout_len; 77 | end_right = rightout + rightout_len; 78 | while ((cuser < end_user) && (cright < end_right)) { 79 | while ((cuser < end_user) 80 | && (*cuser == ' ' || *cuser == '\n' || *cuser == '\r' 81 | || *cuser == '\t')) 82 | cuser++; 83 | while ((cright < end_right) 84 | && (*cright == ' ' || *cright == '\n' || *cright == '\r' 85 | || *cright == '\t')) 86 | cright++; 87 | if (cuser == end_user || cright == end_right) 88 | break; 89 | if (*cuser != *cright) 90 | break; 91 | cuser++; 92 | cright++; 93 | } 94 | while ((cuser < end_user) 95 | && (*cuser == ' ' || *cuser == '\n' || *cuser == '\r' 96 | || *cuser == '\t')) 97 | cuser++; 98 | while ((cright < end_right) 99 | && (*cright == ' ' || *cright == '\n' || *cright == '\r' 100 | || *cright == '\t')) 101 | cright++; 102 | if (cuser == end_user && cright == end_right) { 103 | munmap(userout, userout_len); 104 | munmap(rightout, rightout_len); 105 | RETURN(PE); 106 | } 107 | 108 | munmap(userout, userout_len); 109 | munmap(rightout, rightout_len); 110 | RETURN(WA); 111 | } 112 | 113 | -------------------------------------------------------------------------------- /lorun/cext/diff.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_DIFF_HEADER 20 | #define __LO_DIFF_HEADER 21 | 22 | #include "lorun.h" 23 | 24 | int checkDiff(int rightout_fd, int userout_fd, int *result); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /lorun/cext/limit.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "limit.h" 20 | #include 21 | #include 22 | 23 | const char *last_limit_err; 24 | 25 | int setResLimit(struct Runobj *runobj) { 26 | #define RAISE_EXIT(err) {last_limit_err = err;return -1;} 27 | struct rlimit rl; 28 | /*struct itimerval p_realt;*/ 29 | 30 | rl.rlim_cur = runobj->time_limit / 1000 + 1; 31 | if (runobj->time_limit % 1000 > 800) { 32 | rl.rlim_cur += 1; 33 | } 34 | rl.rlim_max = rl.rlim_cur + 1; 35 | if (setrlimit(RLIMIT_CPU, &rl)) 36 | RAISE_EXIT("set RLIMIT_CPU failure"); 37 | 38 | rl.rlim_cur = runobj->memory_limit * 1024; 39 | rl.rlim_max = rl.rlim_cur + 1024; 40 | if (setrlimit(RLIMIT_DATA, &rl)) 41 | RAISE_EXIT("set RLIMIT_DATA failure"); 42 | 43 | rl.rlim_cur = runobj->memory_limit * 1024 * 2; 44 | rl.rlim_max = rl.rlim_cur + 1024; 45 | if (setrlimit(RLIMIT_AS, &rl)) 46 | RAISE_EXIT("set RLIMIT_AS failure"); 47 | 48 | rl.rlim_cur = 256 * 1024 * 1024; 49 | rl.rlim_max = rl.rlim_cur + 1024; 50 | if (setrlimit(RLIMIT_STACK, &rl)) 51 | RAISE_EXIT("set RLIMIT_STACK failure"); 52 | 53 | /* 54 | p_realt.it_interval.tv_sec = runobj->time_limit / 1000 + 3; 55 | p_realt.it_interval.tv_usec = 0; 56 | p_realt.it_value = p_realt.it_interval; 57 | if (setitimer(ITIMER_REAL, &p_realt, (struct itimerval *) 0) == -1) 58 | RAISE_EXIT("set ITIMER_REAL failure");*/ 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /lorun/cext/limit.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_LIMIT_HEADER 20 | #define __LO_LIMIT_HEADER 21 | 22 | #include "lorun.h" 23 | 24 | int setResLimit(struct Runobj *runobj); 25 | extern const char *last_limit_err; 26 | #endif 27 | -------------------------------------------------------------------------------- /lorun/cext/lorun.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "lorun.h" 20 | #include "convert.h" 21 | #include "run.h" 22 | #include "diff.h" 23 | 24 | 25 | int initRun(struct Runobj *runobj, PyObject *args) 26 | { 27 | PyObject *config, *args_obj, *trace_obj, *time_obj, *memory_obj; 28 | PyObject *calls_obj, *runner_obj, *fd_obj; 29 | 30 | if (!PyArg_ParseTuple(args, "O", &config)) 31 | RAISE1("initRun parseTuple failure"); 32 | if(!PyDict_Check(config)) RAISE1("argument must be a dict"); 33 | 34 | if((args_obj = PyDict_GetItemString(config, "args")) == NULL) 35 | RAISE1("must supply args"); 36 | 37 | if((runobj->args = genRunArgs(args_obj)) == NULL) return -1; 38 | 39 | if((fd_obj = PyDict_GetItemString(config, "fd_in")) == NULL) 40 | runobj->fd_in = -1; 41 | else runobj->fd_in = PyLong_AsLong(fd_obj); 42 | if((fd_obj = PyDict_GetItemString(config, "fd_out")) == NULL) 43 | runobj->fd_out = -1; 44 | else runobj->fd_out = PyLong_AsLong(fd_obj); 45 | if((fd_obj = PyDict_GetItemString(config, "fd_err")) == NULL) 46 | runobj->fd_err = -1; 47 | else runobj->fd_err = PyLong_AsLong(fd_obj); 48 | 49 | if((time_obj = PyDict_GetItemString(config, "timelimit")) == NULL) 50 | RAISE1("must supply timelimit"); 51 | runobj->time_limit = PyLong_AsLong(time_obj); 52 | 53 | if((memory_obj = PyDict_GetItemString(config, "memorylimit")) == NULL) 54 | RAISE1("must supply memorylimit"); 55 | runobj->memory_limit = PyLong_AsLong(memory_obj); 56 | 57 | if((runner_obj = PyDict_GetItemString(config, "runner")) == NULL) 58 | runobj->runner = -1; 59 | else 60 | runobj->runner = PyLong_AsLong(runner_obj); 61 | 62 | if((trace_obj = PyDict_GetItemString(config, "trace")) != NULL){ 63 | if(trace_obj == Py_True){ 64 | runobj->trace = 1; 65 | //trace mode: supply calls and files to access. 66 | if((calls_obj = PyDict_GetItemString(config, "calls")) == NULL) 67 | RAISE1("trace == True, so you must specify calls."); 68 | if(!PyList_Check(calls_obj)) 69 | RAISE1("calls must be a list."); 70 | if(initCalls(calls_obj, runobj->inttable)) return -1; 71 | 72 | if((runobj->files = PyDict_GetItemString(config, "files")) == NULL) 73 | RAISE1("trace == True, so you must specify files."); 74 | if(!PyDict_Check(runobj->files)) 75 | RAISE1("files must be a dcit."); 76 | } else runobj->trace = 0; 77 | } else runobj->trace = 0; 78 | 79 | return 0; 80 | } 81 | 82 | PyObject *run(PyObject *self, PyObject *args) 83 | { 84 | struct Runobj runobj = {0}; 85 | struct Result rst = {0}; 86 | rst.re_call = -1; 87 | 88 | if(initRun(&runobj, args)){ 89 | if(runobj.args) 90 | free((void*)runobj.args); 91 | return NULL; 92 | } 93 | 94 | if(runit(&runobj, &rst) == -1) 95 | return NULL; 96 | if(runobj.args) 97 | free((void*)runobj.args); 98 | 99 | return genResult(&rst); 100 | } 101 | 102 | PyObject* check(PyObject *self, PyObject *args) 103 | { 104 | int user_fd, right_fd, rst; 105 | 106 | if (!PyArg_ParseTuple(args, "ii", &right_fd, &user_fd)) 107 | RAISE0("run parseTuple failure"); 108 | 109 | if(checkDiff(right_fd, user_fd, &rst) == -1) 110 | return NULL; 111 | 112 | return Py_BuildValue("i", rst); 113 | } 114 | 115 | #define run_description "run(argv_dict):\n"\ 116 | "\targv_dict contains:\n"\ 117 | "\t@args : cmd to run\n"\ 118 | "\t@fd_in, fd_out, fd_err : stdin,stdout,stderr fd\n"\ 119 | "\t@timelimit : program time limit\n"\ 120 | "\t@memorylimit : program memory limit\n"\ 121 | "\t@runner : run user\n"\ 122 | "\t@trace : trace?" 123 | 124 | #define check_description "check(right_fd, userout_fd)\n" 125 | 126 | static PyMethodDef lorun_methods[] = { 127 | {"run", run, METH_VARARGS, run_description}, 128 | {"check", check, METH_VARARGS, check_description}, 129 | {NULL, NULL, 0, NULL} 130 | }; 131 | 132 | 133 | struct module_state { 134 | PyObject *error; 135 | }; 136 | 137 | #ifdef IS_PY3 138 | 139 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 140 | static int lorun_ext_traverse(PyObject *m, visitproc visit, void *arg) { 141 | Py_VISIT(GETSTATE(m)->error); 142 | return 0; 143 | } 144 | 145 | static int lorun_ext_clear(PyObject *m) { 146 | Py_CLEAR(GETSTATE(m)->error); 147 | return 0; 148 | } 149 | 150 | static struct PyModuleDef moduledef = { 151 | PyModuleDef_HEAD_INIT, 152 | "_lorun_ext", 153 | NULL, 154 | sizeof(struct module_state), 155 | lorun_methods, 156 | NULL, 157 | lorun_ext_traverse, 158 | lorun_ext_clear, 159 | NULL 160 | }; 161 | 162 | PyObject *PyInit__lorun_ext(void) { 163 | struct module_state *st; 164 | PyObject *module = PyModule_Create(&moduledef); 165 | if (module == NULL) 166 | return NULL; 167 | 168 | st = GETSTATE(module); 169 | st->error = PyErr_NewException("_lorun_ext.Error", NULL, NULL); 170 | if (st->error == NULL) { 171 | Py_DECREF(module); 172 | return NULL; 173 | } 174 | 175 | return module; 176 | } 177 | 178 | #else 179 | 180 | static struct module_state _state; 181 | void init_lorun_ext(void) { 182 | PyObject *module = Py_InitModule("_lorun_ext", lorun_methods); 183 | if (module == NULL) { 184 | return; 185 | } 186 | 187 | _state.error = PyErr_NewException("_lorun_ext.Error", NULL, NULL); 188 | if (_state.error == NULL) { 189 | Py_DECREF(module); 190 | return; 191 | } 192 | } 193 | #endif -------------------------------------------------------------------------------- /lorun/cext/lorun.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_GCC_HEADER 20 | #define __LO_GCC_HEADER 21 | 22 | #include 23 | #include 24 | #define CALLS_MAX 400 25 | #define MAX_OUTPUT 100000000 26 | 27 | enum JUDGE_RESULT { 28 | AC=0, //0 Accepted 29 | PE, //1 Presentation Error 30 | TLE, //2 Time Limit Exceeded 31 | MLE, //3 Memory Limit Exceeded 32 | WA, //4 Wrong Answer 33 | RE, //5 Runtime Error 34 | OLE, //6 Output Limit Exceeded 35 | CE, //7 Compile Error 36 | SE, //8 System Error 37 | }; 38 | 39 | struct Result { 40 | int judge_result; //JUDGE_RESULT 41 | int time_used, memory_used; 42 | int re_signum; 43 | int re_call; 44 | const char* re_file; 45 | int re_file_flag; 46 | }; 47 | 48 | struct Runobj { 49 | PyObject *files; 50 | u_char inttable[CALLS_MAX]; 51 | char * const* args; 52 | 53 | int fd_in, fd_out, fd_err; 54 | int time_limit, memory_limit; 55 | int runner; 56 | int trace; 57 | }; 58 | 59 | #define RAISE(msg) PyErr_SetString(PyExc_Exception,msg); 60 | 61 | #define RAISE0(msg) \ 62 | {PyErr_SetString(PyExc_Exception,msg); return NULL;} 63 | 64 | #define RAISE1(msg) \ 65 | {PyErr_SetString(PyExc_Exception,msg); return -1;} 66 | 67 | 68 | #if PY_MAJOR_VERSION >= 3 69 | #define IS_PY3 70 | #endif 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /lorun/cext/run.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "run.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "access.h" 28 | #include "limit.h" 29 | 30 | const char *last_run_err; 31 | #define RAISE_RUN(err) {last_run_err = err;return -1;} 32 | 33 | int traceLoop(struct Runobj *runobj, struct Result *rst, pid_t pid) { 34 | int status, incall = 0; 35 | struct rusage ru; 36 | struct user_regs_struct regs; 37 | 38 | while (1) { 39 | if (wait4(pid, &status, WSTOPPED, &ru) == -1) 40 | RAISE_RUN("wait4 [WSTOPPED] failure"); 41 | 42 | if (WIFEXITED(status)) 43 | break; 44 | else if (WSTOPSIG(status) != SIGTRAP) { 45 | ptrace(PTRACE_KILL, pid, NULL, NULL); 46 | waitpid(pid, NULL, 0); 47 | 48 | rst->time_used = ru.ru_utime.tv_sec * 1000 49 | + ru.ru_utime.tv_usec / 1000 50 | + ru.ru_stime.tv_sec * 1000 51 | + ru.ru_stime.tv_usec / 1000; 52 | rst->memory_used = ru.ru_maxrss; 53 | 54 | switch (WSTOPSIG(status)) { 55 | case SIGSEGV: 56 | if (rst->memory_used > runobj->memory_limit) 57 | rst->judge_result = MLE; 58 | else 59 | rst->judge_result = RE; 60 | break; 61 | case SIGALRM: 62 | case SIGXCPU: 63 | rst->judge_result = TLE; 64 | break; 65 | default: 66 | rst->judge_result = RE; 67 | break; 68 | } 69 | 70 | rst->re_signum = WSTOPSIG(status); 71 | rst->time_used = ru.ru_utime.tv_sec * 1000 72 | + ru.ru_utime.tv_usec / 1000 73 | + ru.ru_stime.tv_sec * 1000 74 | + ru.ru_stime.tv_usec / 1000; 75 | rst->memory_used = ru.ru_maxrss; 76 | return 0; 77 | } 78 | 79 | if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) 80 | RAISE_RUN("PTRACE_GETREGS failure"); 81 | 82 | if (incall) { 83 | int ret = checkAccess(runobj, pid, ®s); 84 | if (ret != ACCESS_OK) { 85 | ptrace(PTRACE_KILL, pid, NULL, NULL); 86 | waitpid(pid, NULL, 0); 87 | 88 | rst->time_used = ru.ru_utime.tv_sec * 1000 89 | + ru.ru_utime.tv_usec / 1000 90 | + ru.ru_stime.tv_sec * 1000 91 | + ru.ru_stime.tv_usec / 1000; 92 | rst->memory_used = ru.ru_maxrss 93 | * (sysconf(_SC_PAGESIZE) / 1024); 94 | 95 | rst->judge_result = RE; 96 | if (ret == ACCESS_CALL_ERR) { 97 | rst->re_call = REG_SYS_CALL(®s); 98 | } else { 99 | rst->re_file = lastFileAccess(); 100 | rst->re_file_flag = REG_ARG_2(®s); 101 | } 102 | return 0; 103 | } 104 | incall = 0; 105 | } else 106 | incall = 1; 107 | 108 | ptrace(PTRACE_SYSCALL, pid, NULL, NULL); 109 | } 110 | 111 | 112 | rst->time_used = ru.ru_utime.tv_sec * 1000 113 | + ru.ru_utime.tv_usec / 1000 114 | + ru.ru_stime.tv_sec * 1000 115 | + ru.ru_stime.tv_usec / 1000; 116 | rst->memory_used = ru.ru_maxrss; 117 | 118 | 119 | if (rst->time_used > runobj->time_limit) 120 | rst->judge_result = TLE; 121 | else if (rst->memory_used > runobj->memory_limit) 122 | rst->judge_result = MLE; 123 | else 124 | rst->judge_result = AC; 125 | 126 | return 0; 127 | } 128 | 129 | int waitExit(struct Runobj *runobj, struct Result *rst, pid_t pid) { 130 | int status; 131 | struct rusage ru; 132 | 133 | if (wait4(pid, &status, 0, &ru) == -1) 134 | RAISE_RUN("wait4 failure"); 135 | 136 | rst->time_used = ru.ru_utime.tv_sec * 1000 137 | + ru.ru_utime.tv_usec / 1000 138 | + ru.ru_stime.tv_sec * 1000 139 | + ru.ru_stime.tv_usec / 1000; 140 | rst->memory_used = ru.ru_maxrss; 141 | 142 | if (WIFSIGNALED(status)) { 143 | switch (WTERMSIG(status)) { 144 | case SIGSEGV: 145 | if (rst->memory_used > runobj->memory_limit) 146 | rst->judge_result = MLE; 147 | else 148 | rst->judge_result = RE; 149 | break; 150 | case SIGALRM: 151 | case SIGXCPU: 152 | rst->judge_result = TLE; 153 | break; 154 | default: 155 | rst->judge_result = RE; 156 | break; 157 | } 158 | rst->re_signum = WTERMSIG(status); 159 | } else { 160 | if (rst->time_used > runobj->time_limit) 161 | rst->judge_result = TLE; 162 | else if (rst->memory_used > runobj->memory_limit) 163 | rst->judge_result = MLE; 164 | else 165 | rst->judge_result = AC; 166 | } 167 | 168 | return 0; 169 | } 170 | 171 | int runit(struct Runobj *runobj, struct Result *rst) { 172 | pid_t pid; 173 | int fd_err[2]; 174 | 175 | if (pipe2(fd_err, O_NONBLOCK)) 176 | RAISE1("run :pipe2(fd_err) failure"); 177 | 178 | pid = vfork(); 179 | if (pid < 0) { 180 | close(fd_err[0]); 181 | close(fd_err[1]); 182 | RAISE1("run : vfork failure"); 183 | } 184 | 185 | if (pid == 0) { 186 | close(fd_err[0]); 187 | 188 | #define RAISE_EXIT(err) {\ 189 | int r = write(fd_err[1],err,strlen(err));\ 190 | _exit(r);\ 191 | } 192 | 193 | if (runobj->fd_in != -1) 194 | if (dup2(runobj->fd_in, 0) == -1) 195 | RAISE_EXIT("dup2 stdin failure!") 196 | 197 | if (runobj->fd_out != -1) 198 | if (dup2(runobj->fd_out, 1) == -1) 199 | RAISE_EXIT("dup2 stdout failure") 200 | 201 | if (runobj->fd_err != -1) 202 | if (dup2(runobj->fd_err, 2) == -1) 203 | RAISE_EXIT("dup2 stderr failure") 204 | 205 | if (setResLimit(runobj) == -1) 206 | RAISE_EXIT(last_limit_err) 207 | 208 | if (runobj->runner != -1) 209 | if (setuid(runobj->runner)) 210 | RAISE_EXIT("setuid failure") 211 | 212 | if (runobj->trace) 213 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) 214 | RAISE_EXIT("TRACEME failure") 215 | 216 | execvp(runobj->args[0], (char * const *) runobj->args); 217 | 218 | RAISE_EXIT("execvp failure") 219 | } else { 220 | int r; 221 | char errbuffer[100] = { 0 }; 222 | 223 | close(fd_err[1]); 224 | r = read(fd_err[0], errbuffer, 90); 225 | close(fd_err[0]); 226 | if (r > 0) { 227 | waitpid(pid, NULL, WNOHANG); 228 | RAISE(errbuffer); 229 | return -1; 230 | } 231 | 232 | if (runobj->trace) 233 | r = traceLoop(runobj, rst, pid); 234 | else 235 | r = waitExit(runobj, rst, pid); 236 | 237 | if (r) 238 | RAISE1(last_run_err); 239 | return 0; 240 | } 241 | } 242 | 243 | -------------------------------------------------------------------------------- /lorun/cext/run.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Loco program runner core 3 | * Copyright (C) 2011 Lodevil(Du Jiong) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LO_RUN_HEADER 20 | #define __LO_RUN_HEADER 21 | 22 | #include "lorun.h" 23 | 24 | int runit(struct Runobj *runobj, struct Result *rst); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #-*coding:utf-8*- 3 | 4 | from distutils.core import setup, Extension 5 | 6 | 7 | sources = [ 8 | 'lorun/cext/lorun.c', 'lorun/cext/convert.c', 'lorun/cext/access.c', 9 | 'lorun/cext/limit.c', 'lorun/cext/run.c', 'lorun/cext/diff.c' 10 | ] 11 | 12 | setup(name='lorun', 13 | version='1.0.1', 14 | description='loco program runner core', 15 | ext_modules=[Extension('lorun/_lorun_ext', sources=sources)], 16 | packages=['lorun'] 17 | ) 18 | --------------------------------------------------------------------------------