/**
* 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 .
*/
#include "run.h"
#include
#include
#include
#include
#include
#include
#include
#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, ®s) == -1)
RAISE_RUN("PTRACE_GETREGS failure");
if (incall) {
int ret = checkAccess(runobj, pid, ®s);
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(®s);
} else {
rst->re_file = lastFileAccess();
rst->re_file_flag = REG_ARG_2(®s);
}
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;
}
}