rbx.cp 0.5.46__py3-none-any.whl → 0.5.47__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rbx/box/checkers.py +81 -28
- rbx/box/cli.py +12 -10
- rbx/box/packaging/boca/packager.py +44 -7
- rbx/box/packaging/main.py +7 -0
- rbx/box/packaging/moj/packager.py +88 -8
- rbx/box/packaging/packager.py +7 -2
- rbx/box/packaging/polygon/packager.py +5 -4
- rbx/box/solutions.py +2 -2
- rbx/box/stresses.py +0 -1
- rbx/box/tasks.py +6 -4
- rbx/grading/judge/sandbox.py +29 -1
- rbx/grading/judge/sandboxes/isolate.py +10 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +16 -4
- rbx/grading/judge/sandboxes/timeit.py +12 -3
- rbx/grading/processing_context.py +48 -0
- rbx/grading/steps.py +24 -13
- rbx/resources/packagers/boca/checker.sh +8 -6
- rbx/resources/packagers/boca/compare.sh +48 -0
- rbx/resources/packagers/boca/interactive/c +207 -0
- rbx/resources/packagers/boca/interactive/cc +207 -0
- rbx/resources/packagers/boca/interactive/cpp +207 -0
- rbx/resources/packagers/boca/interactive/java +240 -0
- rbx/resources/packagers/boca/interactive/kt +231 -0
- rbx/resources/packagers/boca/interactive/py2 +209 -0
- rbx/resources/packagers/boca/interactive/py3 +209 -0
- rbx/resources/packagers/boca/interactor_compile.sh +45 -0
- rbx/resources/packagers/boca/run/bkp +163 -0
- rbx/resources/packagers/boca/run/c +19 -19
- rbx/resources/packagers/boca/run/cc +19 -19
- rbx/resources/packagers/boca/run/cpp +19 -19
- rbx/resources/packagers/boca/run/java +51 -51
- rbx/resources/packagers/boca/run/kt +30 -30
- rbx/resources/packagers/boca/run/py2 +42 -42
- rbx/resources/packagers/boca/run/py3 +42 -42
- rbx/resources/packagers/moj/scripts/c/compile.sh +19 -0
- rbx/resources/packagers/moj/scripts/c/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/c/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/compare.sh +32 -6
- rbx/resources/packagers/moj/scripts/cpp/compile.sh +19 -0
- rbx/resources/packagers/moj/scripts/cpp/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/cpp/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/interactor_prep.sh +14 -0
- rbx/resources/packagers/moj/scripts/interactor_run.sh +38 -0
- rbx/resources/packagers/moj/scripts/java/compile.sh +12 -0
- rbx/resources/packagers/moj/scripts/java/prep.sh +8 -0
- rbx/resources/packagers/moj/scripts/java/run.sh +17 -0
- rbx/resources/packagers/moj/scripts/py2/compile.sh +7 -0
- rbx/resources/packagers/moj/scripts/py2/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/py2/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/py3/compile.sh +7 -0
- rbx/resources/packagers/moj/scripts/py3/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/py3/run.sh +16 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/METADATA +1 -1
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/RECORD +57 -30
- rbx/resources/packagers/boca/compare +0 -53
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/entry_points.txt +0 -0
rbx/grading/judge/sandbox.py
CHANGED
@@ -6,6 +6,7 @@ import logging
|
|
6
6
|
import os
|
7
7
|
import pathlib
|
8
8
|
import select
|
9
|
+
import signal
|
9
10
|
import stat
|
10
11
|
import subprocess
|
11
12
|
import sys
|
@@ -14,6 +15,7 @@ from typing import IO, Any, Dict, List, Optional
|
|
14
15
|
|
15
16
|
import pydantic
|
16
17
|
|
18
|
+
from rbx.grading import processing_context
|
17
19
|
from rbx.grading.judge import cacher, storage
|
18
20
|
|
19
21
|
logger = logging.getLogger(__name__)
|
@@ -187,6 +189,7 @@ class SandboxBase(abc.ABC):
|
|
187
189
|
|
188
190
|
EXIT_SANDBOX_ERROR = 'sandbox error'
|
189
191
|
EXIT_OK = 'ok'
|
192
|
+
EXIT_TERMINATED = 'terminated'
|
190
193
|
EXIT_SIGNAL = 'signal'
|
191
194
|
EXIT_TIMEOUT = 'timeout'
|
192
195
|
EXIT_TIMEOUT_WALL = 'wall timeout'
|
@@ -225,6 +228,7 @@ class SandboxBase(abc.ABC):
|
|
225
228
|
self.cmd_file = pathlib.PosixPath('commands.log')
|
226
229
|
|
227
230
|
self.params = params or SandboxParams()
|
231
|
+
self.pid = None
|
228
232
|
|
229
233
|
# Set common environment variables.
|
230
234
|
# Specifically needed by Python, that searches the home for
|
@@ -324,6 +328,28 @@ class SandboxBase(abc.ABC):
|
|
324
328
|
"""
|
325
329
|
pass
|
326
330
|
|
331
|
+
def set_pid(self, pid: int):
|
332
|
+
processing_context.add_to_processing_context(pid)
|
333
|
+
self.pid = pid
|
334
|
+
|
335
|
+
def get_pid(self) -> Optional[int]:
|
336
|
+
"""Return the PID of the sandboxed process.
|
337
|
+
|
338
|
+
return (int|None): the PID of the sandboxed process, or None if
|
339
|
+
the sandboxed process is not running.
|
340
|
+
|
341
|
+
"""
|
342
|
+
return self.pid
|
343
|
+
|
344
|
+
@abc.abstractmethod
|
345
|
+
def get_detailed_logs(self) -> str:
|
346
|
+
"""Return the detailed logs of the sandbox.
|
347
|
+
|
348
|
+
return (string): the detailed logs of the sandbox.
|
349
|
+
|
350
|
+
"""
|
351
|
+
pass
|
352
|
+
|
327
353
|
@abc.abstractmethod
|
328
354
|
def get_human_exit_description(self) -> str:
|
329
355
|
"""Get the status of the sandbox and return a human-readable
|
@@ -667,7 +693,9 @@ class SandboxBase(abc.ABC):
|
|
667
693
|
did).
|
668
694
|
|
669
695
|
"""
|
670
|
-
|
696
|
+
# SIGTERM can be safely ignored, just in case it leaks away from
|
697
|
+
# the sandbox.
|
698
|
+
return exitcode == 0 or exitcode == -signal.SIGTERM
|
671
699
|
|
672
700
|
@abc.abstractmethod
|
673
701
|
def initialize(self):
|
@@ -420,6 +420,7 @@ class IsolateSandbox(SandboxBase):
|
|
420
420
|
return (string): the main reason why the sandbox terminated.
|
421
421
|
|
422
422
|
"""
|
423
|
+
# TODO: figure out EXIT_TERMINATED
|
423
424
|
assert self.log is not None
|
424
425
|
status_list = self.get_status_list()
|
425
426
|
if 'XX' in status_list:
|
@@ -462,6 +463,14 @@ class IsolateSandbox(SandboxBase):
|
|
462
463
|
return 'Execution failed because the return code was nonzero'
|
463
464
|
return ''
|
464
465
|
|
466
|
+
def get_detailed_logs(self) -> str:
|
467
|
+
"""Return the detailed logs of the sandbox.
|
468
|
+
|
469
|
+
return (string): the detailed logs of the sandbox.
|
470
|
+
|
471
|
+
"""
|
472
|
+
return str(self.log)
|
473
|
+
|
465
474
|
def inner_absolute_path(self, path: pathlib.Path) -> pathlib.Path:
|
466
475
|
"""Translate from a relative path inside the sandbox to an
|
467
476
|
absolute path inside the sandbox.
|
@@ -602,6 +611,7 @@ class IsolateSandbox(SandboxBase):
|
|
602
611
|
# std*** to interfere with command. Otherwise we let the
|
603
612
|
# caller handle these issues.
|
604
613
|
with popen as p:
|
614
|
+
self.set_pid(p.pid)
|
605
615
|
exitcode = self.translate_box_exitcode(
|
606
616
|
wait_without_std([p], actually_pipe_to_stdout=self.debug)[0]
|
607
617
|
)
|
@@ -57,7 +57,6 @@ class StupidSandbox(SandboxBase):
|
|
57
57
|
self.initialize()
|
58
58
|
|
59
59
|
self.exec_num = -1
|
60
|
-
self.popen = None
|
61
60
|
self.log = None
|
62
61
|
self.returncode = None
|
63
62
|
|
@@ -192,9 +191,13 @@ class StupidSandbox(SandboxBase):
|
|
192
191
|
return (string): the main reason why the sandbox terminated.
|
193
192
|
|
194
193
|
"""
|
195
|
-
if self.returncode
|
194
|
+
if self.returncode is not None and not self.translate_box_exitcode(
|
195
|
+
self.returncode
|
196
|
+
):
|
196
197
|
return self.EXIT_SANDBOX_ERROR
|
197
198
|
status_list = self.get_status_list()
|
199
|
+
if 'TE' in status_list:
|
200
|
+
return self.EXIT_TERMINATED
|
198
201
|
if 'WT' in status_list:
|
199
202
|
return self.EXIT_TIMEOUT_WALL
|
200
203
|
if 'TO' in status_list:
|
@@ -218,6 +221,9 @@ class StupidSandbox(SandboxBase):
|
|
218
221
|
assert self.log is not None
|
219
222
|
return int(self.log['exit-code'])
|
220
223
|
|
224
|
+
def get_detailed_logs(self) -> str:
|
225
|
+
return str(self.log)
|
226
|
+
|
221
227
|
def get_human_exit_description(self) -> str:
|
222
228
|
"""Get the status of the sandbox and return a human-readable
|
223
229
|
string describing it.
|
@@ -299,13 +305,19 @@ class StupidSandbox(SandboxBase):
|
|
299
305
|
+ self.get_timeit_args()
|
300
306
|
+ command
|
301
307
|
)
|
302
|
-
|
308
|
+
with subprocess.Popen(
|
303
309
|
real_command,
|
304
310
|
stdin=subprocess.PIPE,
|
305
311
|
stdout=subprocess.PIPE,
|
306
312
|
stderr=subprocess.STDOUT,
|
307
313
|
env={**os.environ, **self.params.set_env},
|
308
|
-
)
|
314
|
+
) as p:
|
315
|
+
self.set_pid(p.pid)
|
316
|
+
try:
|
317
|
+
self.returncode = p.wait()
|
318
|
+
except Exception:
|
319
|
+
p.kill()
|
320
|
+
raise
|
309
321
|
self.hydrate_logs()
|
310
322
|
return self.translate_box_exitcode(self.returncode)
|
311
323
|
|
@@ -223,6 +223,7 @@ def wait_and_finish(
|
|
223
223
|
pid: int,
|
224
224
|
options: Options,
|
225
225
|
start_time: float,
|
226
|
+
status_holder: Set[str],
|
226
227
|
alarm_msg: Optional[List[Optional[str]]] = None,
|
227
228
|
):
|
228
229
|
_, exitstatus, ru = os.wait4(pid, 0)
|
@@ -237,7 +238,7 @@ def wait_and_finish(
|
|
237
238
|
if exitcode < 0:
|
238
239
|
entries.append(f'exit-sig: {-exitcode}')
|
239
240
|
|
240
|
-
status =
|
241
|
+
status = status_holder
|
241
242
|
if exitcode > 0:
|
242
243
|
status.add('RE')
|
243
244
|
if exitcode < 0:
|
@@ -284,9 +285,11 @@ def main():
|
|
284
285
|
os.chdir(options.chdir)
|
285
286
|
set_rlimits(options)
|
286
287
|
redirect_fds(options)
|
288
|
+
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
287
289
|
os.execvp(options.argv[0], options.argv)
|
288
290
|
|
289
291
|
alarm_msg: List[Optional[str]] = [None]
|
292
|
+
status_holder: Set[str] = set()
|
290
293
|
|
291
294
|
def handle_alarm(*args, **kwargs):
|
292
295
|
nonlocal alarm_msg
|
@@ -311,15 +314,21 @@ def main():
|
|
311
314
|
|
312
315
|
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
313
316
|
|
317
|
+
def handle_sub_term(*args, **kwargs):
|
318
|
+
nonlocal status_holder
|
319
|
+
status_holder.add('TE')
|
320
|
+
os.kill(sub_pid, 9)
|
321
|
+
|
314
322
|
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
315
323
|
signal.signal(signal.SIGALRM, handle_alarm)
|
316
|
-
|
324
|
+
signal.signal(signal.SIGTERM, handle_sub_term)
|
317
325
|
|
326
|
+
wait_and_finish(sub_pid, options, start_time, status_holder, alarm_msg=alarm_msg)
|
318
327
|
# Cancel alarm before exiting to avoid surprises.
|
319
328
|
signal.setitimer(signal.ITIMER_REAL, 0)
|
320
329
|
|
321
330
|
# Exit gracefully.
|
322
|
-
sys.exit()
|
331
|
+
sys.exit(0)
|
323
332
|
|
324
333
|
|
325
334
|
if __name__ == '__main__':
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import contextlib
|
2
|
+
import os
|
3
|
+
import signal
|
4
|
+
import threading
|
5
|
+
from typing import Optional, Set
|
6
|
+
|
7
|
+
_processing_context_pids: Optional[Set[int]] = None
|
8
|
+
_lock = threading.Lock()
|
9
|
+
|
10
|
+
# Creating a processing context is not thread-safe, but adding to it is.
|
11
|
+
|
12
|
+
|
13
|
+
@contextlib.contextmanager
|
14
|
+
def new_processing_context():
|
15
|
+
global _processing_context_pids
|
16
|
+
with _lock:
|
17
|
+
old_processing_context_pids = _processing_context_pids
|
18
|
+
_processing_context_pids = set()
|
19
|
+
try:
|
20
|
+
yield
|
21
|
+
finally:
|
22
|
+
with _lock:
|
23
|
+
_processing_context_pids = old_processing_context_pids
|
24
|
+
|
25
|
+
|
26
|
+
def get_processing_context() -> Set[int]:
|
27
|
+
with _lock:
|
28
|
+
return _processing_context_pids or set()
|
29
|
+
|
30
|
+
|
31
|
+
def add_to_processing_context(pid: int):
|
32
|
+
global _processing_context_pids
|
33
|
+
with _lock:
|
34
|
+
if _processing_context_pids is None:
|
35
|
+
return
|
36
|
+
_processing_context_pids.add(pid)
|
37
|
+
|
38
|
+
|
39
|
+
def terminate_all_processes_in_context():
|
40
|
+
with _lock:
|
41
|
+
if _processing_context_pids is None:
|
42
|
+
return
|
43
|
+
for pid in _processing_context_pids:
|
44
|
+
try:
|
45
|
+
os.kill(pid, signal.SIGTERM)
|
46
|
+
except OSError:
|
47
|
+
pass
|
48
|
+
_processing_context_pids.clear()
|
rbx/grading/steps.py
CHANGED
@@ -21,6 +21,7 @@ from rich.text import Text
|
|
21
21
|
from rbx import utils
|
22
22
|
from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
|
23
23
|
from rbx.console import console
|
24
|
+
from rbx.grading import processing_context
|
24
25
|
from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
|
25
26
|
from rbx.grading.judge.storage import Storage, copyfileobj
|
26
27
|
|
@@ -197,6 +198,7 @@ class RunLog(BaseModel):
|
|
197
198
|
exitstatus: str = SandboxBase.EXIT_SANDBOX_ERROR
|
198
199
|
time: Optional[float] = 0.0
|
199
200
|
memory: Optional[int] = 0
|
201
|
+
sandbox: str = ''
|
200
202
|
warnings: bool = False
|
201
203
|
metadata: Optional[RunLogMetadata] = None
|
202
204
|
|
@@ -210,7 +212,7 @@ class RunLog(BaseModel):
|
|
210
212
|
return 'OK'
|
211
213
|
time = self.time or 0.0
|
212
214
|
memory = self.memory or 0
|
213
|
-
return f'FAILED with exit code {self.exitcode} and sandbox status {self.exitstatus} (time: {time}s, memory: {memory//(1024*1024)}MB)'
|
215
|
+
return f'FAILED with exit code {self.exitcode} and sandbox status {self.exitstatus} (time: {time}s, memory: {memory // (1024 * 1024)}MB)'
|
214
216
|
|
215
217
|
|
216
218
|
class PreprocessLog(RunLog):
|
@@ -567,6 +569,7 @@ def compile(
|
|
567
569
|
memory=sandbox.get_memory_used(),
|
568
570
|
warnings=_check_for_compilation_warnings(sandbox, stderr_file),
|
569
571
|
log='\n'.join(std_outputs),
|
572
|
+
sandbox=sandbox.get_detailed_logs(),
|
570
573
|
)
|
571
574
|
logs.append(log)
|
572
575
|
|
@@ -594,6 +597,7 @@ async def run(
|
|
594
597
|
sandbox: SandboxBase,
|
595
598
|
artifacts: GradingArtifacts,
|
596
599
|
metadata: Optional[RunLogMetadata] = None,
|
600
|
+
kill_on_processing_context_exit: bool = False,
|
597
601
|
) -> Optional[RunLog]:
|
598
602
|
_process_input_artifacts(artifacts, sandbox)
|
599
603
|
_process_fifos(artifacts, sandbox)
|
@@ -609,6 +613,9 @@ async def run(
|
|
609
613
|
)
|
610
614
|
return None
|
611
615
|
|
616
|
+
if sandbox.get_exit_code() != 0 and kill_on_processing_context_exit:
|
617
|
+
processing_context.terminate_all_processes_in_context()
|
618
|
+
|
612
619
|
if not _process_output_artifacts(artifacts, sandbox):
|
613
620
|
return None
|
614
621
|
|
@@ -625,6 +632,7 @@ async def run(
|
|
625
632
|
time=sandbox.get_execution_time(),
|
626
633
|
memory=sandbox.get_memory_used(),
|
627
634
|
metadata=metadata,
|
635
|
+
sandbox=sandbox.get_detailed_logs(),
|
628
636
|
)
|
629
637
|
if metadata is not None and metadata.is_sanitized:
|
630
638
|
run_log.warnings = _check_for_sanitizer_warnings(
|
@@ -649,19 +657,22 @@ async def run_coordinated(
|
|
649
657
|
interactor: CoordinatedRunParams,
|
650
658
|
solution: CoordinatedRunParams,
|
651
659
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
660
|
+
with processing_context.new_processing_context():
|
661
|
+
runs = tuple(
|
662
|
+
run(
|
663
|
+
params.command,
|
664
|
+
params.params,
|
665
|
+
params.sandbox,
|
666
|
+
params.artifacts,
|
667
|
+
params.metadata,
|
668
|
+
kill_on_processing_context_exit=True,
|
669
|
+
)
|
670
|
+
for params in [interactor, solution]
|
671
|
+
)
|
672
|
+
return typing.cast(
|
673
|
+
Tuple[Optional[RunLog], Optional[RunLog]],
|
674
|
+
tuple(await asyncio.gather(*runs)),
|
659
675
|
)
|
660
|
-
for params in [interactor, solution]
|
661
|
-
)
|
662
|
-
return typing.cast(
|
663
|
-
Tuple[Optional[RunLog], Optional[RunLog]], tuple(await asyncio.gather(*runs))
|
664
|
-
)
|
665
676
|
|
666
677
|
|
667
678
|
def _normalize_checked_words(s: str) -> Tuple[str, ...]:
|
@@ -1,9 +1,10 @@
|
|
1
1
|
### START OF CHECKER COMPILATION
|
2
|
+
CDIR=$(pwd)
|
2
3
|
CHECKER_PATH="checker.cpp"
|
3
|
-
CHECKER_OUT="
|
4
|
+
CHECKER_OUT="checker.exe"
|
4
5
|
|
5
6
|
# find compiler
|
6
|
-
cc
|
7
|
+
cc=$(which g++)
|
7
8
|
[ -x "$cc" ] || cc=/usr/bin/g++
|
8
9
|
if [ ! -x "$cc" ]; then
|
9
10
|
echo "$cc not found or it's not executable"
|
@@ -12,25 +13,26 @@ fi
|
|
12
13
|
read -r -d '' TestlibContent <<"EOF"
|
13
14
|
{{testlib_content}}
|
14
15
|
EOF
|
15
|
-
|
16
|
+
|
16
17
|
read -r -d '' CheckerContent <<"EOF"
|
17
18
|
{{checker_content}}
|
18
19
|
EOF
|
19
20
|
|
20
|
-
printf "%s" "${TestlibContent}" >
|
21
|
-
printf "%s" "${CheckerContent}"
|
21
|
+
printf "%s" "${TestlibContent}" >testlib.h
|
22
|
+
printf "%s" "${CheckerContent}" >$CHECKER_PATH
|
22
23
|
|
23
24
|
checker_hash=($(md5sum $CHECKER_PATH))
|
24
25
|
checker_cache="/tmp/boca-chk-${checker_hash}"
|
25
26
|
|
26
27
|
echo "Polygon checker hash: $checker_hash"
|
28
|
+
echo "Copying polygon checker to $CDIR/$CHECKER_OUT"
|
27
29
|
if [ -f "$checker_cache" ]; then
|
28
30
|
echo "Recovering polygon checker from cache: $checker_cache"
|
29
31
|
cp "$checker_cache" $CHECKER_OUT -f
|
30
32
|
else
|
31
33
|
echo "Compiling polygon checker: $CHECKER_PATH"
|
32
34
|
$cc {{rbxFlags}} $CHECKER_PATH -o $CHECKER_OUT
|
33
|
-
|
35
|
+
|
34
36
|
if [ $? -ne 0 ]; then
|
35
37
|
echo "Checker could not be compiled"
|
36
38
|
exit 47
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
if [ ! -r "$1" -o ! -r "$2" ]; then
|
3
|
+
echo "Parameter problem"
|
4
|
+
exit 43
|
5
|
+
fi
|
6
|
+
|
7
|
+
INTERACTOREXITCODE=$(grep '^testlib exitcode' "$1" | awk '{print $NF}')
|
8
|
+
if [[ -n $INTERACTOREXITCODE ]]; then
|
9
|
+
echo "interactor exitcode = $INTERACTOREXITCODE"
|
10
|
+
if [[ $INTERACTOREXITCODE -eq 1 ]]; then
|
11
|
+
echo "interactor return wrong answer"
|
12
|
+
exit 6
|
13
|
+
elif [[ $INTERACTOREXITCODE -eq 2 ]]; then
|
14
|
+
echo "interactor invalid input"
|
15
|
+
exit 6
|
16
|
+
elif [[ $INTERACTOREXITCODE -eq 3 ]]; then
|
17
|
+
echo "interactor failed with exit code 3"
|
18
|
+
exit 43
|
19
|
+
else
|
20
|
+
echo "interactor failed with exit code $INTERACTOREXITCODE"
|
21
|
+
exit 47
|
22
|
+
fi
|
23
|
+
fi
|
24
|
+
|
25
|
+
# Next lines of this script just compares team_output and sol_output,
|
26
|
+
# although it is possible to change them to more complex evaluations.
|
27
|
+
output=$(./checker.exe $3 $1 $2 2>&1 >/dev/null)
|
28
|
+
EC=$?
|
29
|
+
|
30
|
+
echo "checker exitcode = $EC"
|
31
|
+
echo "$output"
|
32
|
+
|
33
|
+
if [ $EC -eq 0 ]; then
|
34
|
+
echo "checker found no differences"
|
35
|
+
exit 4
|
36
|
+
elif [ $EC -eq 1 ]; then
|
37
|
+
echo "checker found differences"
|
38
|
+
exit 6
|
39
|
+
elif [ $EC -eq 2 ]; then
|
40
|
+
echo "checker found invalid output"
|
41
|
+
exit 6
|
42
|
+
elif [ $EC -eq 3 ]; then
|
43
|
+
echo "judge failed with $EC"
|
44
|
+
exit 43
|
45
|
+
else
|
46
|
+
echo "judge failed with $EC"
|
47
|
+
exit 47
|
48
|
+
fi
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# ////////////////////////////////////////////////////////////////////////////////
|
3
|
+
# //BOCA Online Contest Administrator
|
4
|
+
# // Copyright (C) 2003-2014 by BOCA System (bocasystem@gmail.com)
|
5
|
+
# //
|
6
|
+
# // This program is free software: you can redistribute it and/or modify
|
7
|
+
# // it under the terms of the GNU General Public License as published by
|
8
|
+
# // the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# // (at your option) any later version.
|
10
|
+
# //
|
11
|
+
# // This program is distributed in the hope that it will be useful,
|
12
|
+
# // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# // GNU General Public License for more details.
|
15
|
+
# // You should have received a copy of the GNU General Public License
|
16
|
+
# // along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
# ////////////////////////////////////////////////////////////////////////////////
|
18
|
+
#Last modified: 21/aug/2014 by cassio@ime.usp.br
|
19
|
+
#
|
20
|
+
# parameters are:
|
21
|
+
# $1 exe_file
|
22
|
+
# $2 input_file
|
23
|
+
# $3 timelimit (limit to run all the repetitions, by default only one repetition)
|
24
|
+
# $4 number_of_repetitions_to_run (optional, can be used for better tuning the timelimit)
|
25
|
+
# $5 maximum allowed memory (in MBytes)
|
26
|
+
# $6 maximum allowed output size (in KBytes)
|
27
|
+
#
|
28
|
+
# the output of the submission should be directed to the standard output
|
29
|
+
#
|
30
|
+
# the return code show what happened (according to safeexec):
|
31
|
+
# 0 ok
|
32
|
+
# 1 compile error
|
33
|
+
# 2 runtime error
|
34
|
+
# 3 timelimit exceeded
|
35
|
+
# 4 internal error
|
36
|
+
# 5 parameter error
|
37
|
+
# 6 internal error
|
38
|
+
# 7 memory limit exceeded
|
39
|
+
# 8 security threat
|
40
|
+
# 9 runtime error
|
41
|
+
# other_codes are unknown to boca: in this case BOCA will present the
|
42
|
+
# last line of standard output to the judge
|
43
|
+
|
44
|
+
umask 0022
|
45
|
+
id -u bocajail >/dev/null 2>/dev/null
|
46
|
+
if [ $? == 0 ]; then
|
47
|
+
bocau=$(id -u bocajail)
|
48
|
+
bocag=$(id -g bocajail)
|
49
|
+
chown bocajail.nogroup .
|
50
|
+
else
|
51
|
+
bocau=$(id -u nobody)
|
52
|
+
bocag=$(id -g nobody)
|
53
|
+
chown nobody.nogroup .
|
54
|
+
fi
|
55
|
+
if [ "$bocau" == "" -o "$bocag" == "" ]; then
|
56
|
+
echo "error finding user to run script"
|
57
|
+
exit 43
|
58
|
+
fi
|
59
|
+
|
60
|
+
# this script makes use of safeexec to execute the code with less privilegies
|
61
|
+
# make sure that directories below are correct.
|
62
|
+
sf=$(which safeexec)
|
63
|
+
[ -x "$sf" ] || sf=/usr/bin/safeexec
|
64
|
+
|
65
|
+
if [ "$1" == "" -o "$2" == "" -o "$3" == "" ]; then
|
66
|
+
echo "parameter problem"
|
67
|
+
exit 43
|
68
|
+
fi
|
69
|
+
if [ ! -x "$1" ]; then
|
70
|
+
echo "$1 not found (or is not in the current dir) or it's not executable"
|
71
|
+
exit 44
|
72
|
+
fi
|
73
|
+
if [ ! -r "$2" ]; then
|
74
|
+
echo "$2 not found (or is not in the current dir) or it's not readable"
|
75
|
+
exit 45
|
76
|
+
fi
|
77
|
+
if [ ! -x "$sf" ]; then
|
78
|
+
echo "$sf not found or it's not executable"
|
79
|
+
exit 46
|
80
|
+
fi
|
81
|
+
|
82
|
+
time=$3
|
83
|
+
if [ "$time" -gt "0" ]; then
|
84
|
+
let "ttime = $time + 30"
|
85
|
+
else
|
86
|
+
time=1
|
87
|
+
ttime=30
|
88
|
+
fi
|
89
|
+
|
90
|
+
nruns=1
|
91
|
+
if [ "$4" != "" ]; then
|
92
|
+
if [ "$4" -gt "0" ]; then
|
93
|
+
nruns=$4
|
94
|
+
fi
|
95
|
+
fi
|
96
|
+
maxm=512000
|
97
|
+
if [ "$5" != "" ]; then
|
98
|
+
if [ "$5" -gt "0" ]; then
|
99
|
+
maxm=${5}000
|
100
|
+
fi
|
101
|
+
fi
|
102
|
+
maxf=1024
|
103
|
+
if [ "$6" != "" ]; then
|
104
|
+
if [ "$6" -gt "0" ]; then
|
105
|
+
maxf=${6}
|
106
|
+
fi
|
107
|
+
fi
|
108
|
+
|
109
|
+
cp "$2" stdin0 2>/dev/null
|
110
|
+
cp "$1" run.exe 2>/dev/null
|
111
|
+
cp ../interactor.exe interactor.exe 2>/dev/null
|
112
|
+
|
113
|
+
file run.exe | grep -iq "statically linked"
|
114
|
+
if [ "$?" != "0" ]; then
|
115
|
+
echo "Aborting because $1 is not statically linked"
|
116
|
+
exit 47
|
117
|
+
fi
|
118
|
+
|
119
|
+
cdir=$(pwd)
|
120
|
+
echo "Current directory is $cdir -- chrooting on it" >&2
|
121
|
+
|
122
|
+
### START OF BOCA RUN COMMAND
|
123
|
+
cat <<EOF >runit.sh
|
124
|
+
#!/bin/bash
|
125
|
+
mkfifo fifo.in fifo.out
|
126
|
+
|
127
|
+
echo "Running solution as \$@" >&2
|
128
|
+
|
129
|
+
"\$@" >fifo.out <fifo.in 2>/dev/null &
|
130
|
+
SFPID=\$!
|
131
|
+
|
132
|
+
./interactor.exe "stdin0" "stdout0" <fifo.out >fifo.in 2>stderr0 &
|
133
|
+
INTPID=\$!
|
134
|
+
|
135
|
+
wait \$SFPID
|
136
|
+
ECSF=\$?
|
137
|
+
|
138
|
+
wait \$INTPID
|
139
|
+
ECINT=\$?
|
140
|
+
|
141
|
+
rm -rf fifo.in fifo.out
|
142
|
+
|
143
|
+
echo "interactor \$INTPID -> \$ECINT" >&2
|
144
|
+
echo "solution \$SFPID -> \$ECSF" >&2
|
145
|
+
|
146
|
+
echo "interactor exitcode \$ECINT" >&2
|
147
|
+
echo "solution exitcode \$ECSF" >&2
|
148
|
+
|
149
|
+
ret=0
|
150
|
+
if [[ \$ECINT -ge 1 ]] && [[ \$ECINT -le 4 ]]; then
|
151
|
+
echo "testlib exitcode \$ECINT" >stdout0
|
152
|
+
ret=0
|
153
|
+
elif [[ \$ECSF -ne 0 ]]; then
|
154
|
+
ret=\$ECSF
|
155
|
+
elif [[ \$ECINT -ne 0 ]]; then
|
156
|
+
ret=9
|
157
|
+
fi
|
158
|
+
|
159
|
+
exit \$ret
|
160
|
+
EOF
|
161
|
+
|
162
|
+
chmod 755 runit.sh
|
163
|
+
|
164
|
+
ret=0
|
165
|
+
echo $cdir | grep -q "/bocajail"
|
166
|
+
if [ $? -eq 0 ]; then
|
167
|
+
cdir=$(echo $cdir | sed "s/.*\/bocajail//")
|
168
|
+
cat <<EOF >runch.sh
|
169
|
+
#!/bin/bash
|
170
|
+
cd "$cdir"
|
171
|
+
[ -f /proc/cpuinfo ] || /bin/mount -t proc proc /proc
|
172
|
+
[ -d /sys/kernel ] || /bin/mount -t sysfs sysfs /sys
|
173
|
+
"$sf" -F10 -f$maxf -r$nruns -n0 -C. -U$bocau -G$bocag -d$maxm -m$maxm -t$time -T$ttime -ostdout0 -estderr0 -- ./runit.sh ./run.exe
|
174
|
+
retval=\$?
|
175
|
+
echo \$retval > runch.exitcode
|
176
|
+
if [ ! -d /bocajail ]; then
|
177
|
+
/bin/umount /proc 2>/dev/null
|
178
|
+
/bin/umount /sys 2>/dev/null
|
179
|
+
fi
|
180
|
+
EOF
|
181
|
+
chmod 755 runch.sh
|
182
|
+
chroot /bocajail $cdir/runch.sh
|
183
|
+
if [ -r runch.exitcode ]; then
|
184
|
+
ret=$(cat runch.exitcode)
|
185
|
+
fi
|
186
|
+
if [ "$ret" == "" ]; then
|
187
|
+
echo "Execution error - check autojudging"
|
188
|
+
exit 49
|
189
|
+
fi
|
190
|
+
else
|
191
|
+
echo "CODE NOT BEING CHROOTED. DO NOT RUN THIS ON THE MAIN SERVER" >&2
|
192
|
+
echo "CODE NOT BEING CHROOTED. DO NOT RUN THIS ON THE MAIN SERVER" >&2
|
193
|
+
echo "CODE NOT BEING CHROOTED. DO NOT RUN THIS ON THE MAIN SERVER" >&2
|
194
|
+
|
195
|
+
"$sf" -F10 -f$maxf -r$nruns -n0 -C. -U$bocau -G$bocag -d$maxm -m$maxm -t$time -T$ttime -ostdout0 -estderr0 -- ./runit.sh ./run.exe
|
196
|
+
ret=$?
|
197
|
+
fi
|
198
|
+
### END OF BOCA RUN COMMAND
|
199
|
+
|
200
|
+
if [ $ret -gt 10 ]; then
|
201
|
+
echo "execution error with code $ret" >&2
|
202
|
+
ret=0
|
203
|
+
fi
|
204
|
+
if [ -f stdout0 ]; then
|
205
|
+
cat stdout0
|
206
|
+
fi
|
207
|
+
exit $ret
|