Description:
[grader] box.cc now reports running status when die git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@172 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r46:31f3935d33aa - - 2 files changed: 61 inserted, 47 deleted

@@ -1,197 +1,215
1 /*
1 /*
2 * A Simple Testing Sandbox
2 * A Simple Testing Sandbox
3 *
3 *
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
5 */
5 */
6
6
7 #define _LARGEFILE64_SOURCE
7 #define _LARGEFILE64_SOURCE
8 //#define _GNU_SOURCE
8 //#define _GNU_SOURCE
9
9
10 #include <errno.h>
10 #include <errno.h>
11 #include <stdio.h>
11 #include <stdio.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <stdarg.h>
15 #include <stdarg.h>
16 #include <unistd.h>
16 #include <unistd.h>
17 #include <getopt.h>
17 #include <getopt.h>
18 #include <time.h>
18 #include <time.h>
19 #include <sys/wait.h>
19 #include <sys/wait.h>
20 #include <sys/user.h>
20 #include <sys/user.h>
21 #include <sys/time.h>
21 #include <sys/time.h>
22 #include <sys/ptrace.h>
22 #include <sys/ptrace.h>
23 #include <sys/signal.h>
23 #include <sys/signal.h>
24 #include <sys/sysinfo.h>
24 #include <sys/sysinfo.h>
25 #include <sys/syscall.h>
25 #include <sys/syscall.h>
26 #include <sys/resource.h>
26 #include <sys/resource.h>
27
27
28 #define NONRET __attribute__((noreturn))
28 #define NONRET __attribute__((noreturn))
29 #define UNUSED __attribute__((unused))
29 #define UNUSED __attribute__((unused))
30
30
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 static int timeout;
32 static int timeout;
33 static int pass_environ;
33 static int pass_environ;
34 static int use_wall_clock;
34 static int use_wall_clock;
35 static int file_access;
35 static int file_access;
36 static int verbose;
36 static int verbose;
37 static int memory_limit;
37 static int memory_limit;
38 static int allow_times;
38 static int allow_times;
39 static char *redir_stdin, *redir_stdout;
39 static char *redir_stdin, *redir_stdout;
40 static char *set_cwd;
40 static char *set_cwd;
41
41
42 static pid_t box_pid;
42 static pid_t box_pid;
43 static int is_ptraced;
43 static int is_ptraced;
44 static volatile int timer_tick;
44 static volatile int timer_tick;
45 static time_t start_time;
45 static time_t start_time;
46 static int ticks_per_sec;
46 static int ticks_per_sec;
47
47
48 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
48 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
49 /* glibc 2.1 or newer -> has lseek64 */
49 /* glibc 2.1 or newer -> has lseek64 */
50 #define long_seek(f,o,w) lseek64(f,o,w)
50 #define long_seek(f,o,w) lseek64(f,o,w)
51 #else
51 #else
52 /* Touching clandestine places in glibc */
52 /* Touching clandestine places in glibc */
53 extern loff_t llseek(int fd, loff_t pos, int whence);
53 extern loff_t llseek(int fd, loff_t pos, int whence);
54 #define long_seek(f,o,w) llseek(f,o,w)
54 #define long_seek(f,o,w) llseek(f,o,w)
55 #endif
55 #endif
56
56
57 + int max_mem_used = 0;
58 +
59 + void print_running_stat(double wall_time,
60 + double user_time,
61 + double system_time,
62 + int mem_usage)
63 + {
64 + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
65 + wall_time, user_time, system_time, mem_usage);
66 + }
67 +
57 static void NONRET
68 static void NONRET
58 box_exit(void)
69 box_exit(void)
59 {
70 {
60 - if (box_pid > 0)
71 + if (box_pid > 0) {
61 - {
72 + if (is_ptraced)
62 - if (is_ptraced)
73 + ptrace(PTRACE_KILL, box_pid);
63 - ptrace(PTRACE_KILL, box_pid);
74 + kill(-box_pid, SIGKILL);
64 - kill(-box_pid, SIGKILL);
75 + kill(box_pid, SIGKILL);
65 - kill(box_pid, SIGKILL);
76 + }
66 - }
67 - exit(1);
68 - }
69
77
70 - static void
78 + struct timeval total;
71 - box_kill(void)
79 + int wall;
72 - {
80 + struct rusage rus;
73 - if (box_pid > 0)
81 + int stat;
74 - {
82 + pid_t p;
75 - if (is_ptraced)
83 +
76 - ptrace(PTRACE_KILL, box_pid);
84 + // wait so that we can get information
77 - kill(-box_pid, SIGKILL);
85 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
78 - kill(box_pid, SIGKILL);
86 + if (p < 0) {
79 - }
87 + fprintf(stderr,"wait4: error\n");
88 + print_running_stat(0,0,0,max_mem_used);
89 + } else if (p != box_pid) {
90 + fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
91 + print_running_stat(0,0,0,max_mem_used);
92 + } else {
93 + if (!WIFEXITED(stat))
94 + fprintf(stderr,"wait4: unknown status\n");
95 + struct timeval total;
96 + int wall;
97 + wall = time(NULL) - start_time;
98 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
99 +
100 + print_running_stat((double)wall,
101 + (double) rus.ru_utime.tv_sec +
102 + ((double) rus.ru_utime.tv_usec/1000000.0),
103 + (double) rus.ru_stime.tv_sec +
104 + ((double) rus.ru_stime.tv_usec/1000000.0),
105 + max_mem_used);
106 + }
107 + exit(1);
80 }
108 }
81
109
82 static void NONRET __attribute__((format(printf,1,2)))
110 static void NONRET __attribute__((format(printf,1,2)))
83 die(char *msg, ...)
111 die(char *msg, ...)
84 {
112 {
85 va_list args;
113 va_list args;
86 va_start(args, msg);
114 va_start(args, msg);
87 vfprintf(stderr, msg, args);
115 vfprintf(stderr, msg, args);
88 fputc('\n', stderr);
116 fputc('\n', stderr);
89 box_exit();
117 box_exit();
90 }
118 }
91
119
92 - static void __attribute__((format(printf,1,2)))
93 - die_report(char *msg, ...)
94 - {
95 - va_list args;
96 - va_start(args, msg);
97 - vfprintf(stderr, msg, args);
98 - fputc('\n', stderr);
99 - box_kill();
100 - }
101 -
102 static void __attribute__((format(printf,1,2)))
120 static void __attribute__((format(printf,1,2)))
103 log(char *msg, ...)
121 log(char *msg, ...)
104 {
122 {
105 va_list args;
123 va_list args;
106 va_start(args, msg);
124 va_start(args, msg);
107 if (verbose)
125 if (verbose)
108 {
126 {
109 vfprintf(stderr, msg, args);
127 vfprintf(stderr, msg, args);
110 fflush(stderr);
128 fflush(stderr);
111 }
129 }
112 va_end(args);
130 va_end(args);
113 }
131 }
114
132
115 static void
133 static void
116 valid_filename(unsigned long addr)
134 valid_filename(unsigned long addr)
117 {
135 {
118 char namebuf[4096], *p, *end;
136 char namebuf[4096], *p, *end;
119 static int mem_fd;
137 static int mem_fd;
120
138
121 if (!file_access)
139 if (!file_access)
122 die("File access forbidden.");
140 die("File access forbidden.");
123 if (file_access >= 9)
141 if (file_access >= 9)
124 return;
142 return;
125
143
126 if (!mem_fd)
144 if (!mem_fd)
127 {
145 {
128 sprintf(namebuf, "/proc/%d/mem", (int) box_pid);
146 sprintf(namebuf, "/proc/%d/mem", (int) box_pid);
129 mem_fd = open(namebuf, O_RDONLY);
147 mem_fd = open(namebuf, O_RDONLY);
130 if (mem_fd < 0)
148 if (mem_fd < 0)
131 die("open(%s): %m", namebuf);
149 die("open(%s): %m", namebuf);
132 }
150 }
133 p = end = namebuf;
151 p = end = namebuf;
134 do
152 do
135 {
153 {
136 if (p >= end)
154 if (p >= end)
137 {
155 {
138 int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
156 int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
139 int l = namebuf + sizeof(namebuf) - end;
157 int l = namebuf + sizeof(namebuf) - end;
140 if (l > remains)
158 if (l > remains)
141 l = remains;
159 l = remains;
142 if (!l)
160 if (!l)
143 die("Access to file with name too long.");
161 die("Access to file with name too long.");
144 if (long_seek(mem_fd, addr, SEEK_SET) < 0)
162 if (long_seek(mem_fd, addr, SEEK_SET) < 0)
145 die("long_seek(mem): %m");
163 die("long_seek(mem): %m");
146 remains = read(mem_fd, end, l);
164 remains = read(mem_fd, end, l);
147 if (remains < 0)
165 if (remains < 0)
148 die("read(mem): %m");
166 die("read(mem): %m");
149 if (!remains)
167 if (!remains)
150 die("Access to file with name out of memory.");
168 die("Access to file with name out of memory.");
151 end += l;
169 end += l;
152 addr += l;
170 addr += l;
153 }
171 }
154 }
172 }
155 while (*p++);
173 while (*p++);
156
174
157 log("[%s] ", namebuf);
175 log("[%s] ", namebuf);
158 if (file_access >= 3)
176 if (file_access >= 3)
159 return;
177 return;
160 if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
178 if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
161 return;
179 return;
162 if (file_access >= 2)
180 if (file_access >= 2)
163 {
181 {
164 if ((!strncmp(namebuf, "/etc/", 5) ||
182 if ((!strncmp(namebuf, "/etc/", 5) ||
165 !strncmp(namebuf, "/lib/", 5) ||
183 !strncmp(namebuf, "/lib/", 5) ||
166 !strncmp(namebuf, "/usr/lib/", 9))
184 !strncmp(namebuf, "/usr/lib/", 9))
167 && !strstr(namebuf, ".."))
185 && !strstr(namebuf, ".."))
168 return;
186 return;
169 if (!strcmp(namebuf, "/dev/null") ||
187 if (!strcmp(namebuf, "/dev/null") ||
170 !strcmp(namebuf, "/dev/zero") ||
188 !strcmp(namebuf, "/dev/zero") ||
171 !strcmp(namebuf, "/proc/meminfo") ||
189 !strcmp(namebuf, "/proc/meminfo") ||
172 !strcmp(namebuf, "/proc/self/stat") ||
190 !strcmp(namebuf, "/proc/self/stat") ||
173 !strncmp(namebuf, "/usr/share/zoneinfo/", 20))
191 !strncmp(namebuf, "/usr/share/zoneinfo/", 20))
174 return;
192 return;
175 }
193 }
176 die("Forbidden access to file `%s'.", namebuf);
194 die("Forbidden access to file `%s'.", namebuf);
177 }
195 }
178
196
179 static int
197 static int
180 valid_syscall(struct user *u)
198 valid_syscall(struct user *u)
181 {
199 {
182 switch (u->regs.orig_eax)
200 switch (u->regs.orig_eax)
183 {
201 {
184 case __NR_execve:
202 case __NR_execve:
185 {
203 {
186 static int exec_counter;
204 static int exec_counter;
187 return !exec_counter++;
205 return !exec_counter++;
188 }
206 }
189 case __NR_open:
207 case __NR_open:
190 case __NR_creat:
208 case __NR_creat:
191 case __NR_unlink:
209 case __NR_unlink:
192 case __NR_oldstat:
210 case __NR_oldstat:
193 case __NR_access:
211 case __NR_access:
194 case __NR_oldlstat:
212 case __NR_oldlstat:
195 case __NR_truncate:
213 case __NR_truncate:
196 case __NR_stat:
214 case __NR_stat:
197 case __NR_lstat:
215 case __NR_lstat:
@@ -256,328 +274,324
256 case __NR_readdir:
274 case __NR_readdir:
257 case __NR_setitimer:
275 case __NR_setitimer:
258 case __NR_getitimer:
276 case __NR_getitimer:
259 case __NR_sigreturn:
277 case __NR_sigreturn:
260 case __NR_mprotect:
278 case __NR_mprotect:
261 case __NR_sigprocmask:
279 case __NR_sigprocmask:
262 case __NR_getdents:
280 case __NR_getdents:
263 case __NR_getdents64:
281 case __NR_getdents64:
264 case __NR__newselect:
282 case __NR__newselect:
265 case __NR_fdatasync:
283 case __NR_fdatasync:
266 case __NR_mremap:
284 case __NR_mremap:
267 case __NR_poll:
285 case __NR_poll:
268 case __NR_getcwd:
286 case __NR_getcwd:
269 case __NR_nanosleep:
287 case __NR_nanosleep:
270 case __NR_rt_sigreturn:
288 case __NR_rt_sigreturn:
271 case __NR_rt_sigaction:
289 case __NR_rt_sigaction:
272 case __NR_rt_sigprocmask:
290 case __NR_rt_sigprocmask:
273 case __NR_rt_sigpending:
291 case __NR_rt_sigpending:
274 case __NR_rt_sigtimedwait:
292 case __NR_rt_sigtimedwait:
275 case __NR_rt_sigqueueinfo:
293 case __NR_rt_sigqueueinfo:
276 case __NR_rt_sigsuspend:
294 case __NR_rt_sigsuspend:
277 case __NR_mmap2:
295 case __NR_mmap2:
278 case __NR__sysctl:
296 case __NR__sysctl:
279 return (filter_syscalls == 1);
297 return (filter_syscalls == 1);
280 case __NR_times:
298 case __NR_times:
281 return allow_times;
299 return allow_times;
282 case __NR_kill:
300 case __NR_kill:
283 if (u->regs.ebx == box_pid)
301 if (u->regs.ebx == box_pid)
284 die("Commited suicide by signal %d.", (int)u->regs.ecx);
302 die("Commited suicide by signal %d.", (int)u->regs.ecx);
285 return 0;
303 return 0;
286 default:
304 default:
287 return 0;
305 return 0;
288 }
306 }
289 }
307 }
290
308
291 static void
309 static void
292 signal_alarm(int unused UNUSED)
310 signal_alarm(int unused UNUSED)
293 {
311 {
294 /* Time limit checks are synchronous, so we only schedule them there. */
312 /* Time limit checks are synchronous, so we only schedule them there. */
295 timer_tick = 1;
313 timer_tick = 1;
296
314
297 //NOTE: do not use alarm, changed to setitimer for precision
315 //NOTE: do not use alarm, changed to setitimer for precision
298 // alarm(1);
316 // alarm(1);
299 }
317 }
300
318
301 static void
319 static void
302 signal_int(int unused UNUSED)
320 signal_int(int unused UNUSED)
303 {
321 {
304 /* Interrupts are fatal, so no synchronization requirements. */
322 /* Interrupts are fatal, so no synchronization requirements. */
305 die("Interrupted.");
323 die("Interrupted.");
306 }
324 }
307
325
308 static void
326 static void
309 check_timeout(void)
327 check_timeout(void)
310 {
328 {
311 int sec;
329 int sec;
312
330
313 if (use_wall_clock)
331 if (use_wall_clock)
314 sec = time(NULL) - start_time;
332 sec = time(NULL) - start_time;
315 else
333 else
316 {
334 {
317 char buf[4096], *x;
335 char buf[4096], *x;
318 int c, utime, stime;
336 int c, utime, stime;
319 static int proc_status_fd;
337 static int proc_status_fd;
320 if (!proc_status_fd)
338 if (!proc_status_fd)
321 {
339 {
322 sprintf(buf, "/proc/%d/stat", (int) box_pid);
340 sprintf(buf, "/proc/%d/stat", (int) box_pid);
323 proc_status_fd = open(buf, O_RDONLY);
341 proc_status_fd = open(buf, O_RDONLY);
324 if (proc_status_fd < 0)
342 if (proc_status_fd < 0)
325 die("open(%s): %m", buf);
343 die("open(%s): %m", buf);
326 }
344 }
327 lseek(proc_status_fd, 0, SEEK_SET);
345 lseek(proc_status_fd, 0, SEEK_SET);
328 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
346 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
329 die("read on /proc/$pid/stat: %m");
347 die("read on /proc/$pid/stat: %m");
330 if (c >= (int) sizeof(buf) - 1)
348 if (c >= (int) sizeof(buf) - 1)
331 die("/proc/$pid/stat too long");
349 die("/proc/$pid/stat too long");
332 buf[c] = 0;
350 buf[c] = 0;
333 x = buf;
351 x = buf;
334 while (*x && *x != ' ')
352 while (*x && *x != ' ')
335 x++;
353 x++;
336 while (*x == ' ')
354 while (*x == ' ')
337 x++;
355 x++;
338 if (*x++ != '(')
356 if (*x++ != '(')
339 die("proc syntax error 1");
357 die("proc syntax error 1");
340 while (*x && (*x != ')' || x[1] != ' '))
358 while (*x && (*x != ')' || x[1] != ' '))
341 x++;
359 x++;
342 while (*x == ')' || *x == ' ')
360 while (*x == ')' || *x == ' ')
343 x++;
361 x++;
344 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
362 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
345 die("proc syntax error 2");
363 die("proc syntax error 2");
346 //printf("%s - %d\n",x,ticks_per_sec);
364 //printf("%s - %d\n",x,ticks_per_sec);
347 sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
365 sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
348 }
366 }
349 if (verbose > 1)
367 if (verbose > 1)
350 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
368 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
351 if (sec > timeout) {
369 if (sec > timeout) {
352 - die_report("Time limit exceeded.");
370 + die("Time limit exceeded.");
353 }
371 }
354 }
372 }
355
373
356 - int max_mem_used = 0;
357 -
358 static void
374 static void
359 check_memory_usage()
375 check_memory_usage()
360 {
376 {
361 char proc_fname[100];
377 char proc_fname[100];
362 sprintf(proc_fname,"/proc/%d/statm",box_pid);
378 sprintf(proc_fname,"/proc/%d/statm",box_pid);
363 //printf("proc fname: %s\n",proc_fname);
379 //printf("proc fname: %s\n",proc_fname);
364 FILE *fp = fopen(proc_fname,"r");
380 FILE *fp = fopen(proc_fname,"r");
365 if(fp!=NULL) {
381 if(fp!=NULL) {
366 char line[1000];
382 char line[1000];
367 fgets(line,999,fp);
383 fgets(line,999,fp);
368 //printf("%s\n",line);
384 //printf("%s\n",line);
369 int m;
385 int m;
370
386
371 if(sscanf(line,"%d",&m)==1)
387 if(sscanf(line,"%d",&m)==1)
372 if(m>max_mem_used)
388 if(m>max_mem_used)
373 max_mem_used = m;
389 max_mem_used = m;
374
390
375 fclose(fp);
391 fclose(fp);
376 }
392 }
377 }
393 }
378
394
379 static void
395 static void
380 boxkeeper(void)
396 boxkeeper(void)
381 {
397 {
382 int syscall_count = 0;
398 int syscall_count = 0;
383 struct sigaction sa;
399 struct sigaction sa;
384
400
385 is_ptraced = 1;
401 is_ptraced = 1;
386 bzero(&sa, sizeof(sa));
402 bzero(&sa, sizeof(sa));
387 sa.sa_handler = signal_int;
403 sa.sa_handler = signal_int;
388 sigaction(SIGINT, &sa, NULL);
404 sigaction(SIGINT, &sa, NULL);
389 start_time = time(NULL);
405 start_time = time(NULL);
390 ticks_per_sec = sysconf(_SC_CLK_TCK);
406 ticks_per_sec = sysconf(_SC_CLK_TCK);
391 if (ticks_per_sec <= 0)
407 if (ticks_per_sec <= 0)
392 die("Invalid ticks_per_sec!");
408 die("Invalid ticks_per_sec!");
393
409
394 check_memory_usage();
410 check_memory_usage();
395
411
396 sa.sa_handler = signal_alarm;
412 sa.sa_handler = signal_alarm;
397 sigaction(SIGALRM, &sa, NULL);
413 sigaction(SIGALRM, &sa, NULL);
398 //alarm(1);
414 //alarm(1);
399
415
400 struct itimerval val;
416 struct itimerval val;
401 val.it_interval.tv_sec = 0;
417 val.it_interval.tv_sec = 0;
402 val.it_interval.tv_usec = 50000;
418 val.it_interval.tv_usec = 50000;
403 val.it_value.tv_sec = 0;
419 val.it_value.tv_sec = 0;
404 val.it_value.tv_usec = 50000;
420 val.it_value.tv_usec = 50000;
405 setitimer(ITIMER_REAL,&val,NULL);
421 setitimer(ITIMER_REAL,&val,NULL);
406
422
407 /*
423 /*
408 --- add alarm handler no matter what..
424 --- add alarm handler no matter what..
409 if (timeout)
425 if (timeout)
410 {
426 {
411 sa.sa_handler = signal_alarm;
427 sa.sa_handler = signal_alarm;
412 sigaction(SIGALRM, &sa, NULL);
428 sigaction(SIGALRM, &sa, NULL);
413 alarm(1);
429 alarm(1);
414 }
430 }
415 */
431 */
416
432
417 for(;;)
433 for(;;)
418 {
434 {
419 struct rusage rus;
435 struct rusage rus;
420 int stat;
436 int stat;
421 pid_t p;
437 pid_t p;
422
438
423 if (timer_tick)
439 if (timer_tick)
424 {
440 {
425 check_timeout();
441 check_timeout();
426 check_memory_usage();
442 check_memory_usage();
427 timer_tick = 0;
443 timer_tick = 0;
428 }
444 }
429 p = wait4(box_pid, &stat, WUNTRACED, &rus);
445 p = wait4(box_pid, &stat, WUNTRACED, &rus);
430
446
431 if (p < 0)
447 if (p < 0)
432 {
448 {
433 if (errno == EINTR)
449 if (errno == EINTR)
434 continue;
450 continue;
435 die("wait4: %m");
451 die("wait4: %m");
436 }
452 }
437 if (p != box_pid)
453 if (p != box_pid)
438 die("wait4: unknown pid %d exited!", p);
454 die("wait4: unknown pid %d exited!", p);
439 if (WIFEXITED(stat))
455 if (WIFEXITED(stat))
440 {
456 {
441 struct timeval total;
457 struct timeval total;
442 int wall;
458 int wall;
443 wall = time(NULL) - start_time;
459 wall = time(NULL) - start_time;
444 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
460 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
445
461
446 box_pid = 0;
462 box_pid = 0;
447 if (WEXITSTATUS(stat))
463 if (WEXITSTATUS(stat))
448 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
464 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
449 else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
465 else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
450 fprintf(stderr,"Time limit exceeded.\n");
466 fprintf(stderr,"Time limit exceeded.\n");
451 else
467 else
452 // report OK and statistics
468 // report OK and statistics
453 fprintf(stderr,"OK\n");
469 fprintf(stderr,"OK\n");
454
470
455 - fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
471 + print_running_stat((double) wall,
456 - wall,
472 + (double) rus.ru_utime.tv_sec +
457 - (double) rus.ru_utime.tv_sec +
473 + ((double) rus.ru_utime.tv_usec/1000000.0),
458 - ((double) rus.ru_utime.tv_usec/1000000.0),
474 + (double) rus.ru_stime.tv_sec +
459 - (double) rus.ru_stime.tv_sec +
475 + ((double) rus.ru_stime.tv_usec/1000000.0),
460 - ((double) rus.ru_stime.tv_usec/1000000.0),
476 + max_mem_used);
461 - max_mem_used);
462 /*
477 /*
463 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
478 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
464 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
479 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
465 (int) total.tv_usec,
480 (int) total.tv_usec,
466 wall,
481 wall,
467 syscall_count,
482 syscall_count,
468 max_mem_used);
483 max_mem_used);
469 */
484 */
470 exit(0);
485 exit(0);
471 }
486 }
472 if (WIFSIGNALED(stat))
487 if (WIFSIGNALED(stat))
473 {
488 {
474 box_pid = 0;
489 box_pid = 0;
475 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
490 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
476
491
477 struct timeval total;
492 struct timeval total;
478 int wall;
493 int wall;
479 wall = time(NULL) - start_time;
494 wall = time(NULL) - start_time;
480 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
495 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
481 - fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
496 + print_running_stat((double) wall,
482 - wall,
497 + (double) rus.ru_utime.tv_sec +
483 - (double) rus.ru_utime.tv_sec +
498 + ((double) rus.ru_utime.tv_usec/1000000.0),
484 - ((double) rus.ru_utime.tv_usec/1000000.0),
499 + (double) rus.ru_stime.tv_sec +
485 - (double) rus.ru_stime.tv_sec +
500 + ((double) rus.ru_stime.tv_usec/1000000.0),
486 - ((double) rus.ru_stime.tv_usec/1000000.0),
501 + max_mem_used);
487 - max_mem_used);
488 exit(0);
502 exit(0);
489 }
503 }
490 if (WIFSTOPPED(stat))
504 if (WIFSTOPPED(stat))
491 {
505 {
492 int sig = WSTOPSIG(stat);
506 int sig = WSTOPSIG(stat);
493 if (sig == SIGTRAP)
507 if (sig == SIGTRAP)
494 {
508 {
495 struct user u;
509 struct user u;
496 static int stop_count = -1;
510 static int stop_count = -1;
497 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
511 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
498 die("ptrace(PTRACE_GETREGS): %m");
512 die("ptrace(PTRACE_GETREGS): %m");
499 stop_count++;
513 stop_count++;
500 if (!stop_count) /* Traceme request */
514 if (!stop_count) /* Traceme request */
501 log(">> Traceme request caught\n");
515 log(">> Traceme request caught\n");
502 else if (stop_count & 1) /* Syscall entry */
516 else if (stop_count & 1) /* Syscall entry */
503 {
517 {
504 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
518 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
505 syscall_count++;
519 syscall_count++;
506 if (!valid_syscall(&u))
520 if (!valid_syscall(&u))
507 {
521 {
508 /*
522 /*
509 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
523 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
510 * so we have to change it to something harmless (e.g., an undefined
524 * so we have to change it to something harmless (e.g., an undefined
511 * syscall) and make the program continue.
525 * syscall) and make the program continue.
512 */
526 */
513 unsigned int sys = u.regs.orig_eax;
527 unsigned int sys = u.regs.orig_eax;
514 u.regs.orig_eax = 0xffffffff;
528 u.regs.orig_eax = 0xffffffff;
515 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
529 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
516 die("ptrace(PTRACE_SETREGS): %m");
530 die("ptrace(PTRACE_SETREGS): %m");
517 die("Forbidden syscall %d.", sys);
531 die("Forbidden syscall %d.", sys);
518 }
532 }
519 }
533 }
520 else /* Syscall return */
534 else /* Syscall return */
521 log("= %ld\n", u.regs.eax);
535 log("= %ld\n", u.regs.eax);
522 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
536 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
523 }
537 }
524 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
538 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
525 {
539 {
526 log(">> Signal %d\n", sig);
540 log(">> Signal %d\n", sig);
527 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
541 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
528 }
542 }
529 else
543 else
530 die("Received signal %d.", sig);
544 die("Received signal %d.", sig);
531 }
545 }
532 else
546 else
533 die("wait4: unknown status %x, giving up!", stat);
547 die("wait4: unknown status %x, giving up!", stat);
534 }
548 }
535 }
549 }
536
550
537 static void
551 static void
538 box_inside(int argc, char **argv)
552 box_inside(int argc, char **argv)
539 {
553 {
540 struct rlimit rl;
554 struct rlimit rl;
541 char *args[argc+1];
555 char *args[argc+1];
542 char *env[1] = { NULL };
556 char *env[1] = { NULL };
543
557
544 memcpy(args, argv, argc * sizeof(char *));
558 memcpy(args, argv, argc * sizeof(char *));
545 args[argc] = NULL;
559 args[argc] = NULL;
546 if (set_cwd && chdir(set_cwd))
560 if (set_cwd && chdir(set_cwd))
547 die("chdir: %m");
561 die("chdir: %m");
548 if (redir_stdin)
562 if (redir_stdin)
549 {
563 {
550 close(0);
564 close(0);
551 if (open(redir_stdin, O_RDONLY) != 0)
565 if (open(redir_stdin, O_RDONLY) != 0)
552 die("open(\"%s\"): %m", redir_stdin);
566 die("open(\"%s\"): %m", redir_stdin);
553 }
567 }
554 if (redir_stdout)
568 if (redir_stdout)
555 {
569 {
556 close(1);
570 close(1);
557 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
571 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
558 die("open(\"%s\"): %m", redir_stdout);
572 die("open(\"%s\"): %m", redir_stdout);
559 }
573 }
560 dup2(1, 2);
574 dup2(1, 2);
561 setpgrp();
575 setpgrp();
562 if (memory_limit)
576 if (memory_limit)
563 {
577 {
564 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
578 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
565 if (setrlimit(RLIMIT_AS, &rl) < 0)
579 if (setrlimit(RLIMIT_AS, &rl) < 0)
566 die("setrlimit: %m");
580 die("setrlimit: %m");
567 }
581 }
568 rl.rlim_cur = rl.rlim_max = 64;
582 rl.rlim_cur = rl.rlim_max = 64;
569 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
583 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
570 die("setrlimit: %m");
584 die("setrlimit: %m");
571 if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
585 if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
572 die("ptrace(PTRACE_TRACEME): %m");
586 die("ptrace(PTRACE_TRACEME): %m");
573 execve(args[0], args, (pass_environ ? environ : env));
587 execve(args[0], args, (pass_environ ? environ : env));
574 die("execve(\"%s\"): %m", args[0]);
588 die("execve(\"%s\"): %m", args[0]);
575 }
589 }
576
590
577 static void
591 static void
578 usage(void)
592 usage(void)
579 {
593 {
580 fprintf(stderr, "Invalid arguments!\n");
594 fprintf(stderr, "Invalid arguments!\n");
581 printf("\
595 printf("\
582 Usage: box [<options>] -- <command> <arguments>\n\
596 Usage: box [<options>] -- <command> <arguments>\n\
583 \n\
597 \n\
@@ -148,169 +148,169
148 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
148 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
149 end
149 end
150
150
151 end
151 end
152
152
153 describe "A grader engine, when grading test requests" do
153 describe "A grader engine, when grading test requests" do
154
154
155 include GraderEngineHelperMethods
155 include GraderEngineHelperMethods
156
156
157 before(:each) do
157 before(:each) do
158 @config = Grader::Configuration.get_instance
158 @config = Grader::Configuration.get_instance
159 @engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
159 @engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
160 Grader::TestRequestReporter.new)
160 Grader::TestRequestReporter.new)
161 init_sandbox
161 init_sandbox
162 end
162 end
163
163
164 it "should report error if there is no problem template" do
164 it "should report error if there is no problem template" do
165 problem = stub(Problem,
165 problem = stub(Problem,
166 :id => 1, :name => 'nothing')
166 :id => 1, :name => 'nothing')
167 grader_should(:grade => 'test1_correct.c',
167 grader_should(:grade => 'test1_correct.c',
168 :on => problem,
168 :on => problem,
169 :with => 'in1.txt',
169 :with => 'in1.txt',
170 :and_report => {
170 :and_report => {
171 :graded_at= => nil,
171 :graded_at= => nil,
172 :compiler_message= => '',
172 :compiler_message= => '',
173 :grader_comment= => '',
173 :grader_comment= => '',
174 :running_stat= => /template not found/,
174 :running_stat= => /template not found/,
175 :running_time= => nil,
175 :running_time= => nil,
176 :exit_status= => nil,
176 :exit_status= => nil,
177 :memory_usage= => nil,
177 :memory_usage= => nil,
178 :save => nil})
178 :save => nil})
179 end
179 end
180
180
181 it "should run test request and produce output file" do
181 it "should run test request and produce output file" do
182 problem = stub(Problem,
182 problem = stub(Problem,
183 :id => 1, :name => 'test_normal')
183 :id => 1, :name => 'test_normal')
184 grader_should(:grade => 'test1_correct.c',
184 grader_should(:grade => 'test1_correct.c',
185 :on => problem,
185 :on => problem,
186 :with => 'in1.txt',
186 :with => 'in1.txt',
187 :and_report => {
187 :and_report => {
188 :graded_at= => nil,
188 :graded_at= => nil,
189 :compiler_message= => '',
189 :compiler_message= => '',
190 :grader_comment= => '',
190 :grader_comment= => '',
191 :running_stat= => /0.0\d* sec./,
191 :running_stat= => /0.0\d* sec./,
192 :output_file_name= => lambda { |fname|
192 :output_file_name= => lambda { |fname|
193 File.exists?(fname).should be_true
193 File.exists?(fname).should be_true
194 },
194 },
195 :running_time= => nil,
195 :running_time= => nil,
196 :exit_status= => nil,
196 :exit_status= => nil,
197 :memory_usage= => nil,
197 :memory_usage= => nil,
198 :save => nil})
198 :save => nil})
199 end
199 end
200
200
201 it "should clean up problem directory after running test request" do
201 it "should clean up problem directory after running test request" do
202 problem = stub(Problem,
202 problem = stub(Problem,
203 :id => 1, :name => 'test_normal')
203 :id => 1, :name => 'test_normal')
204 grader_should(:grade => 'test1_correct.c',
204 grader_should(:grade => 'test1_correct.c',
205 :on => problem,
205 :on => problem,
206 :with => 'in1.txt',
206 :with => 'in1.txt',
207 :and_report => {
207 :and_report => {
208 :graded_at= => nil,
208 :graded_at= => nil,
209 :compiler_message= => '',
209 :compiler_message= => '',
210 :grader_comment= => '',
210 :grader_comment= => '',
211 :running_stat= => nil,
211 :running_stat= => nil,
212 :output_file_name= => nil,
212 :output_file_name= => nil,
213 :running_time= => nil,
213 :running_time= => nil,
214 :exit_status= => nil,
214 :exit_status= => nil,
215 :memory_usage= => nil,
215 :memory_usage= => nil,
216 :save => nil})
216 :save => nil})
217 File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
217 File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
218 end
218 end
219
219
220 it "should compile test request with error and report compilation error" do
220 it "should compile test request with error and report compilation error" do
221 problem = stub(Problem,
221 problem = stub(Problem,
222 :id => 1, :name => 'test_normal')
222 :id => 1, :name => 'test_normal')
223 grader_should(:grade => 'test1_compile_error.c',
223 grader_should(:grade => 'test1_compile_error.c',
224 :on => problem,
224 :on => problem,
225 :with => 'in1.txt',
225 :with => 'in1.txt',
226 :and_report => {
226 :and_report => {
227 :graded_at= => nil,
227 :graded_at= => nil,
228 :compiler_message= => /.+/,
228 :compiler_message= => /.+/,
229 :grader_comment= => /[Cc]ompil.*error/,
229 :grader_comment= => /[Cc]ompil.*error/,
230 :running_stat= => '',
230 :running_stat= => '',
231 :save => nil})
231 :save => nil})
232 end
232 end
233
233
234 it "should report exit status" do
234 it "should report exit status" do
235 problem = stub(Problem,
235 problem = stub(Problem,
236 :id => 1, :name => 'test_normal')
236 :id => 1, :name => 'test_normal')
237 grader_should(:grade => 'add_nonzero_exit_status.c',
237 grader_should(:grade => 'add_nonzero_exit_status.c',
238 :on => problem,
238 :on => problem,
239 :with => 'in1.txt',
239 :with => 'in1.txt',
240 :and_report => {
240 :and_report => {
241 :graded_at= => nil,
241 :graded_at= => nil,
242 :compiler_message= => '',
242 :compiler_message= => '',
243 :grader_comment= => '',
243 :grader_comment= => '',
244 - :running_stat= => /[Ee]xit.*status.*10.*0\.0 sec/m,
244 + :running_stat= => /[Ee]xit.*status.*10.*0\.0\d* sec/m,
245 :output_file_name= => lambda { |fname|
245 :output_file_name= => lambda { |fname|
246 File.exists?(fname).should be_true
246 File.exists?(fname).should be_true
247 },
247 },
248 :running_time= => nil,
248 :running_time= => nil,
249 :exit_status= => /10/,
249 :exit_status= => /10/,
250 :memory_usage= => nil,
250 :memory_usage= => nil,
251 :save => nil})
251 :save => nil})
252 end
252 end
253
253
254 it "should produce running statistics for normal submission" do
254 it "should produce running statistics for normal submission" do
255 problem = stub(Problem,
255 problem = stub(Problem,
256 :id => 1, :name => 'test_normal')
256 :id => 1, :name => 'test_normal')
257 grader_should(:grade => 'test_run_stat.c',
257 grader_should(:grade => 'test_run_stat.c',
258 :on => problem,
258 :on => problem,
259 :with => 'in1.txt',
259 :with => 'in1.txt',
260 :and_report => {
260 :and_report => {
261 :graded_at= => nil,
261 :graded_at= => nil,
262 :compiler_message= => '',
262 :compiler_message= => '',
263 :grader_comment= => '',
263 :grader_comment= => '',
264 :running_stat= => nil,
264 :running_stat= => nil,
265 :output_file_name= => lambda { |fname|
265 :output_file_name= => lambda { |fname|
266 File.exists?(fname).should be_true
266 File.exists?(fname).should be_true
267 },
267 },
268 :running_time= => lambda { |t|
268 :running_time= => lambda { |t|
269 (t>=0.14) and (t<=0.16)
269 (t>=0.14) and (t<=0.16)
270 },
270 },
271 :exit_status= => nil,
271 :exit_status= => nil,
272 :memory_usage= => lambda { |s|
272 :memory_usage= => lambda { |s|
273 (s>=500) and (s<=1000)
273 (s>=500) and (s<=1000)
274 },
274 },
275 :save => nil})
275 :save => nil})
276 end
276 end
277
277
278 protected
278 protected
279 def grader_should(args)
279 def grader_should(args)
280 @user1 = stub(User,
280 @user1 = stub(User,
281 :id => 1, :login => 'user1')
281 :id => 1, :login => 'user1')
282
282
283 problem = args[:on]
283 problem = args[:on]
284 input_file = @config.test_request_input_base_dir + "/" + args[:with]
284 input_file = @config.test_request_input_base_dir + "/" + args[:with]
285
285
286 submission =
286 submission =
287 create_submission_from_file(1, @user1, args[:on], args[:grade])
287 create_submission_from_file(1, @user1, args[:on], args[:grade])
288
288
289 test_request = stub(TestRequest,
289 test_request = stub(TestRequest,
290 :id => 1,
290 :id => 1,
291 :user => @user1,
291 :user => @user1,
292 :problem => problem,
292 :problem => problem,
293 :submission => submission,
293 :submission => submission,
294 :input_file_name => input_file,
294 :input_file_name => input_file,
295 :language => submission.language,
295 :language => submission.language,
296 :problem_name => problem.name)
296 :problem_name => problem.name)
297
297
298 expectations = args[:and_report]
298 expectations = args[:and_report]
299
299
300 expectations.each do |key,val|
300 expectations.each do |key,val|
301 if val==nil
301 if val==nil
302 test_request.should_receive(key)
302 test_request.should_receive(key)
303 elsif val.class == Proc
303 elsif val.class == Proc
304 test_request.should_receive(key) { |fname|
304 test_request.should_receive(key) { |fname|
305 val.call(fname)
305 val.call(fname)
306 }
306 }
307 else
307 else
308 test_request.should_receive(key).with(val)
308 test_request.should_receive(key).with(val)
309 end
309 end
310 end
310 end
311
311
312 @engine.grade(test_request)
312 @engine.grade(test_request)
313 end
313 end
314
314
315 end
315 end
316
316
You need to be logged in to leave comments. Login now