blob: d9c71a415e8d2c9b815dc5111f1e2bcae498e006 [file] [log] [blame]
#include "Program.h"
#include "program_init.h"
#include "Logger.h"
bool Program::para_inited=false;
extern int GENERAL_COMPILE_TIME,GENERATOR_RUN_TIME,GENERATOR_RUN_MEMORY,VMLANG_MULTIPLIER,MAX_OUTPUT_LIMIT,EXTRA_RUNTIME;
extern int lowprivid;
extern string tmpnam();
Program::Program()
{
//ctor
compiled=false;
result="";
base_filename="";
time_used=memory_used=0;
compile_time_limit=GENERAL_COMPILE_TIME;
check_exit_status=false;
if (!para_inited) {
init_error();
init_others();
para_inited=true;
}
}
Program::~Program()
{
if (base_filename!="") Deletefile(base_filename+"*");
if (Checkfile(src_filename)) Deletefile(src_filename);
if (Checkfile(exc_filename)) Deletefile(exc_filename);
if (Checkfile(out_filename)) Deletefile(out_filename);
if (Checkfile(err_filename)) Deletefile(err_filename);
if (Checkfile(res_filename)) Deletefile(res_filename);
if (Checkfile(in_filename)) Deletefile(in_filename);
if (Checkfile(cinfo_filename)) Deletefile(cinfo_filename);
}
void Program::Savetofile(string filename,string content) {
FILE * fp=fopen(filename.c_str(),"w");
while (fp==NULL) fp=fopen(filename.c_str(),"w");
if (fp!=NULL) {
fputs(content.c_str(),fp);
//fprintf(fp,"%s",content.c_str());
fclose(fp);
}
}
string Program::Loadallfromfile(string filename,int limit) {
int lines=0,tried=0;
string res="",tmps;
fstream fin(filename.c_str(),fstream::in);
while (fin.fail()&&tried<10) {
tried++;
fin.open(filename.c_str(),fstream::in);
return res;
}
if (fin.fail()) return res;
while (getline(fin,tmps)) {
if (res!="") res+="\n";
res+=tmps;
lines++;
if (fin.eof()) break;
if (limit!=-1&&lines>limit) break;
//getline(fin,tmps);
}
fin.close();
return res;
}
void Program::Deletefile(string filename) {
if (filename == "*") return;
system(((string)"rm " + filename).c_str());
}
bool Program::Checkfile(string filename) {
int tried=0;
if (filename=="") return false;
FILE * fp=fopen(filename.c_str(),"r");
while (fp==NULL&&tried<5) {
tried++;
fp=fopen(filename.c_str(),"r");
}
if (fp!=NULL) {
fclose(fp);
return true;
}
return false;
}
string Program::Inttostring(int x) {
char tt[100];
sprintf(tt,"%d",x);
string t=tt;
return t;
}
int Program::Compile(string source, int language) {
cinfo_filename = CONFIG->GetTmpfile_path() + tmpnam()+".txt";
if (language!=JAVALANG) base_filename = CONFIG->GetTmpfile_path() + tmpnam();
else base_filename = CONFIG->GetTmpfile_path() + "Main";
exc_filename=base_filename+exc_extension[language];
src_filename=base_filename+src_extension[language];
Savetofile(src_filename,source);
LOG("Compiling "+src_filename);
struct rlimit compile_limit;
compile_limit.rlim_max=compile_limit.rlim_cur=compile_time_limit;
int cpid;
if ((cpid=fork())==0) {
LOGGER->addIdentifier(getpid(), "Compiler");
usleep(50000);
freopen(cinfo_filename.c_str(),"w",stderr);
setrlimit(RLIMIT_CPU,&compile_limit);
setuid(lowprivid);
switch (language) {
case CPPLANG:
execl("/usr/bin/g++","g++",src_filename.c_str(),"-o",exc_filename.c_str(),"-O","-fno-asm","-Wall","-lm",NULL);
break;
case CLANG:
execl("/usr/bin/gcc","gcc",src_filename.c_str(),"-o",exc_filename.c_str(),"-O","-fno-asm","-Wall","-lm",NULL);
break;
case CLANGPPLANG:
execl("/usr/bin/clang++","clang++",src_filename.c_str(),"-o",exc_filename.c_str(),"-O","-fno-asm","-Wall","-lm",NULL);
break;
case CLANGLANG:
execl("/usr/bin/clang","clang",src_filename.c_str(),"-o",exc_filename.c_str(),"-O","-fno-asm","-Wall","-lm",NULL);
break;
case FORTLANG:
execl("/usr/bin/gfortran","gfortran",src_filename.c_str(),"-o",exc_filename.c_str(),"-Wall",NULL);
break;
case JAVALANG:
execl("/usr/bin/javac","javac","-g:none","-Xlint",src_filename.c_str(),NULL);
break;
case FPASLANG:
execl("/usr/bin/fpc","fpc",src_filename.c_str(),"-o",exc_filename.c_str(),"-Co","-Cr","-Ct","-Ci",NULL);
break;
case PYLANG:
execl("/usr/bin/python","python","-c",("\"import py_compile; py_compile.compile(\'"+src_filename+"\')\"").c_str(),NULL);
break;
case CSLANG:
execl("/usr/bin/mcs","mcs",src_filename.c_str(),("-out:"+exc_filename).c_str(),NULL);
break;
case ADALANG:
execl("/usr/bin/gnatmake","gnatmake",src_filename.c_str(),NULL);
break;
case SMLLANG:
execl("/usr/bin/mlton","mlton",src_filename.c_str(),"-output",exc_filename.c_str(),NULL);
break;
case PERLLANG:
break;
case RUBYLANG:
break;
default:
return -1;
}
exit(0);
}
else {
LOG("Compile Child Process: "+Inttostring(cpid));
LOG("Compile time limit: "+Inttostring(compile_time_limit));
int cstat,tused;
struct timeval case_startv,case_nowv;
struct timezone case_startz,case_nowz;
gettimeofday(&case_startv,&case_startz);
int cnt=-1;
cstat=-1;
while (1) {
usleep(50000);
cnt++;
gettimeofday(&case_nowv,&case_nowz);
tused=case_nowv.tv_sec-case_startv.tv_sec;
if (cnt%20==0) LOG("Compiling Used: "+Inttostring(tused));
if (waitpid(cpid,&cstat,WNOHANG)==0) {
if (tused>compile_time_limit) {
LOG("Time too much!");
LOG("kill `pstree -p "+Inttostring(cpid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`");
system(("kill `pstree -p "+Inttostring(cpid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`").c_str());
waitpid(cpid,&cstat,0);
return 2;
}
}
else if (WIFSIGNALED(cstat)&&WTERMSIG(cstat)!=0) {
LOG("Something is wrong.");
LOG("kill `pstree -p "+Inttostring(cpid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`");
system(("kill `pstree -p "+Inttostring(cpid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`").c_str());
waitpid(cpid,&cstat,0);
return 2;
}
else if (WIFEXITED(cstat)) {
waitpid(cpid,&cstat,0);
LOG("Compiled");
break;
}
}
system(("kill `pstree -p "+Inttostring(cpid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`").c_str());
}
if (!Checkfile(exc_filename)&&(language==CPPLANG||language==CLANG||language==FPASLANG||language==FORTLANG||language==SMLLANG||language==CSLANG||language==JAVALANG||language==PYLANG||language==CLANGPPLANG||language==CLANGLANG||language==ADALANG)) {
return 1;
}
return 0;
}
int Program::Excution() {
if (has_input&&!Checkfile(in_filename)) return -1;
system(((string)"chmod +x "+exc_filename).c_str());
res_filename=tmpnam();
struct rlimit runtime;
runtime.rlim_max=runtime.rlim_cur=(total_time_limit-time_used+999)/1000+EXTRA_RUNTIME;
pid_t wid;
if ((wid=fork())==0) {
LOGGER->addIdentifier(getpid(), "Sandbox");
string exc_command;
pid_t pid;
int runstat;
bool excuted=false;
struct rusage rinfo;
setrlimit(RLIMIT_CPU,&runtime);
struct user_regs_struct reg;
struct rlimit time_limit,output_limit;
time_limit.rlim_cur=case_time_limit<total_time_limit-time_used?case_time_limit:total_time_limit-time_used;
time_limit.rlim_cur=(time_limit.rlim_cur+999)/1000;
if (time_limit.rlim_cur<=0) time_limit.rlim_cur=1;
time_limit.rlim_max=time_limit.rlim_cur+1;
if ((pid=fork())==0) {
LOGGER->addIdentifier(getpid(), "Runner");
if (has_input) freopen(in_filename.c_str(),"r",stdin);
freopen(out_filename.c_str(),"w",stdout);
freopen(err_filename.c_str(),"w",stderr);
LOG((string)"Time limit for this program is "+Inttostring(time_limit.rlim_cur));
setrlimit(RLIMIT_CPU,&time_limit);
output_limit.rlim_max=output_limit.rlim_cur=MAX_OUTPUT_LIMIT*1024*1024;
setrlimit(RLIMIT_FSIZE,&output_limit);
setuid(lowprivid);
ptrace(PTRACE_TRACEME,0,NULL,NULL);
switch (language) {
case CPPLANG:
case CLANG:
case FORTLANG:
case FPASLANG:
case SMLLANG:
case ADALANG:
exc_command=(string)"./"+exc_filename;
execl(exc_command.c_str(),exc_command.c_str(),NULL);
break;
case JAVALANG:
execl("/usr/bin/java","java","-Djava.security.manager","-Djava.security.policy=java.policy","-client","-cp",CONFIG->GetTmpfile_path().c_str(),"Main",NULL);
break;
case CSLANG:
execl("/usr/bin/mono","mono",exc_filename.c_str(),NULL);
break;
case PERLLANG:
execl("/usr/bin/perl","perl",src_filename.c_str(),"-W",NULL);
case RUBYLANG:
execl("/usr/bin/ruby","ruby",src_filename.c_str(),"-W",NULL);
break;
case PYLANG:
execl("/usr/bin/python","python",exc_filename.c_str(),NULL);
break;
}
exit(0);
}
else {
if (language<MIN_LANG_NUM||language>MAX_LANG_NUM||language==VCLANG||language==VCPPLANG) {
result="Invalid Language";
LOG("Invalid Language Detected");
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
exit(0);
}
LOG("Program Child Process: "+Inttostring(pid));
LOG("Running program");
runstat=0;
struct timeval case_startv;
struct timezone case_startz;
gettimeofday(&case_startv,&case_startz);
while (1) {
wait4(pid,&runstat,0,&rinfo);
time_used=(rinfo.ru_utime.tv_sec+rinfo.ru_stime.tv_sec)*1000+(rinfo.ru_utime.tv_usec+rinfo.ru_stime.tv_usec)/1000;
if (total_time_limit<time_used) {
LOG("Dectect TLE, type:1, LOOP found. Time used: "+Inttostring(time_used)+", Limit: "+Inttostring(total_time_limit));
ptrace(PTRACE_KILL,pid,NULL,NULL);
result="Time Limit Exceed";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
exit(0);
}
if (memory_used<getpagesize()*rinfo.ru_minflt) memory_used=getpagesize()*rinfo.ru_minflt;
if (WIFEXITED(runstat)) {
LOG((string)"Used time: "+Inttostring(time_used));
LOG((string)"Used Memory: "+Inttostring(memory_used));
LOG((string)"Run status: "+Inttostring(WEXITSTATUS(runstat)));
if (check_exit_status&&WEXITSTATUS(runstat)!=0) result="Runtime Error";
else result="Normal";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
exit(0);
}
if (WIFSIGNALED(runstat)&&WTERMSIG(runstat)!=SIGTRAP) {
int signal=WTERMSIG(runstat);
LOG((string)"Used time: "+Inttostring(time_used));
LOG((string)"Used Memory: "+Inttostring(memory_used));
LOG((string)"Run status: "+Inttostring(runstat));
switch (signal)
{
case SIGXCPU:
LOG("Dectect TLE, type:2, signaled");
result="Time Limit Exceed";
time_used=time_limit.rlim_cur*1000+4;
break;
case SIGXFSZ:
result="Output Limit Exceed";
break;
default:
result="Runtime Error";
}
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
else if (WIFSTOPPED(runstat)&&WSTOPSIG(runstat)!=SIGTRAP) {
int signal=WSTOPSIG(runstat);
LOG((string)"Used time: "+Inttostring(time_used));
LOG((string)"Used Memory: "+Inttostring(memory_used));
LOG((string)"Run status: "+Inttostring(runstat));
switch (signal)
{
case SIGXCPU:
result="Time Limit Exceed";
LOG("Dectect TLE, type:2, signaled");
time_used=time_limit.rlim_cur*1000+4;
break;
case SIGXFSZ:
result="Output Limit Exceed";
break;
default:
result="Runtime Error";
}
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
else if ((runstat>>8)!=5&&(runstat>>8)>0) {
LOG((string)"Used time: "+Inttostring(time_used));
LOG((string)"Used Memory: "+Inttostring(memory_used));
LOG((string)"Run status: "+Inttostring(runstat));
result="Runtime Error";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
ptrace(PTRACE_GETREGS,pid,NULL,&reg);
#ifdef __i386__
//printf("System call:%ld\n",reg.orig_eax);
if (reg.orig_eax==SYS_execve&&!excuted) excuted=true;
else {
if (language==JAVALANG) {
if (syscalls_java[reg.orig_eax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_eax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
else if (language==CSLANG) {
if (syscalls_csharp[reg.orig_eax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_eax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
else if (syscalls_other[reg.orig_eax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_eax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
#else
//printf("System call:%ld\n",reg.orig_rax);
if (reg.orig_rax==SYS_execve&&!excuted) excuted=true;
else {
if (language==JAVALANG) {
if (syscalls_java[reg.orig_rax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_rax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
else if (language==CSLANG) {
if (syscalls_csharp[reg.orig_rax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_rax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
else if (syscalls_other[reg.orig_rax]) {
LOG((string)"Invalid system call: "+ Inttostring(reg.orig_rax));
result="Restricted Function";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
}
#endif
if (memory_used/1024>memory_limit) {
LOG((string)"Used time: "+Inttostring(time_used));
LOG((string)"Used Memory: "+Inttostring(memory_used));
LOG((string)"Run status: "+Inttostring(runstat));
result="Memory Limit Exceed";
result+="\n"+Inttostring(time_used)+"\n"+Inttostring(memory_used);
Savetofile(res_filename,result);
ptrace(PTRACE_KILL,pid,NULL,NULL);
exit(0);
}
ptrace(PTRACE_SYSCALL,pid,NULL,NULL);
}
exit(0);
}
exit(0);
}
else {
LOG("Watch Child Process: "+Inttostring(wid));
int rstat,tused;
struct timeval case_startv,case_nowv;
struct timezone case_startz,case_nowz;
gettimeofday(&case_startv,&case_startz);
int cnt=-1;
rstat=-1;
while (1) {
usleep(50000);
cnt++;
gettimeofday(&case_nowv,&case_nowz);
tused=case_nowv.tv_sec-case_startv.tv_sec;
if (cnt%20==0) LOG("Running Used: "+Inttostring(tused));
if (waitpid(wid,&rstat,WNOHANG)==0) {
if (tused>runtime.rlim_max) {
result="Judge Error";
LOG("Time too much!");
LOG("kill `pstree -p "+Inttostring(wid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`");
system(("kill `pstree -p "+Inttostring(wid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`").c_str());
waitpid(wid,&rstat,0);
return 1;
}
}
else if (WIFSIGNALED(rstat)&&WTERMSIG(rstat)!=0) {
result="Judge Error";
LOG("Something is wrong.");
LOG("kill `pstree -p "+Inttostring(wid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`");
system(("kill `pstree -p "+Inttostring(wid)+" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/' | tr \"\\n\" \" \"`").c_str());
waitpid(wid,&rstat,0);
return 1;
}
else if (WIFEXITED(rstat)) {
waitpid(wid,&rstat,0);
LOG("Runned.");
break;
}
}
string res="";
fstream fin(res_filename.c_str(),fstream::in);
while (fin.fail()) fin.open(res_filename.c_str(),fstream::in);
//system(("kill -9 "+Inttostring(wid)).c_str());
int case_time_used,case_memory_used;
getline(fin,result);
fin>>case_time_used>>case_memory_used;
time_used+=case_time_used;
memory_used=max(memory_used,case_memory_used);
fin.close();
if (result==""||result==" ") {
result="Judge Error";
LOG("Failed to get result.");
}
system(("rm "+res_filename).c_str());
}
return 0;
}
void Program::Trytocompile(string source, int language) {
if (vmlang[language]) compile_time_limit=GENERAL_COMPILE_TIME*3;
if (source == "") {
source = this->source;
}
if (language == -1) {
language = this->language;
}
int res=Compile(source, language);
compiled=true;
if (res==-1) {
//JUDGE ERROR
result="Invalid Language";
return;
}
else if (res==2) {
//COMPILE ERROR
result="Compile Error";
return;
}
int cnt=0;
while (res==1) {
cnt++;
ce_info=Loadallfromfile(cinfo_filename,200);
if (ce_info.length()>0||cnt>2) {
result="Compile Error";
return;
}
else {
usleep(50000);
res=Compile(source, language);
}
}
if (vmlang[language]) {
total_time_limit*=VMLANG_MULTIPLIER;
case_time_limit*=VMLANG_MULTIPLIER;
memory_limit*=VMLANG_MULTIPLIER;
}
}
void Program::Run()
{
if (!compiled) {
result = "";
Trytocompile(source, language);
if (result != "") return;
}
if (exc_filename!=src_filename) {
string tmps=CONFIG->GetTmpfile_path() + tmpnam();
//Deletefile(src_filename);
system(((string)"mv "+src_filename+" "+tmps).c_str());
src_filename=tmps;
}
Excution();
if (total_time_limit<time_used) result="Time Limit Exceed";
}