org.ng200.openolympus.cerberus.executors.SandboxedExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.ng200.openolympus.cerberus.executors.SandboxedExecutor.java

Source

/**
 * The MIT License
 * Copyright (c) 2014-2015 Nick Guletskii
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.ng200.openolympus.cerberus.executors;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
import org.ng200.openolympus.FileAccess;
import org.ng200.openolympus.cerberus.ExecutionResult;
import org.ng200.openolympus.cerberus.SolutionJudge;
import org.ng200.openolympus.cerberus.util.TemporaryStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SandboxedExecutor extends OpenOlympusWatchdogExecutor implements Executor {
    public static final Path CHROOT_TEMPLATE_PATH = FileSystems.getDefault().getPath("/usr/chroot");
    private transient TemporaryStorage storage;
    private long memoryLimit = 0;
    private long cpuLimit = 0;
    private long timeLimit = 0;
    private long diskLimit = 0;
    private InputStream inputStream = null;
    private OutputStream errorStream = null;
    private OutputStream outputStream = null;

    private static final Logger logger = LoggerFactory.getLogger(SandboxedExecutor.class);

    public SandboxedExecutor() {
        // Serialization constructor
    }

    public TemporaryStorage getStorage() {
        return storage;
    }

    public void setStorage(TemporaryStorage storage) {
        this.storage = storage;
    }

    public SandboxedExecutor(final SolutionJudge holder) throws IOException {
        this.storage = new TemporaryStorage(holder);
        SandboxedExecutor.logger.info("Chroot template path: {}",
                SandboxedExecutor.CHROOT_TEMPLATE_PATH.toAbsolutePath().toString());
        FileAccess.rsync(SandboxedExecutor.CHROOT_TEMPLATE_PATH, this.storage.getPath());
    }

    @Override
    public void close() throws IOException {
        this.storage.close();
    }

    @Override
    public ExecutionResult execute(final Path program) throws IOException {
        SandboxedExecutor.logger.debug("Copying program into jail");
        final Path chrootedProgram = this.storage.getPath().resolve("chroot")
                .resolve(program.getFileName().toString());
        chrootedProgram.getParent().toFile().mkdirs();
        FileAccess.copy(program, chrootedProgram, StandardCopyOption.COPY_ATTRIBUTES);

        final CommandLine commandLine = new CommandLine("sudo");
        commandLine.addArgument("olympus_watchdog");

        this.setUpOlrunnerLimits(commandLine);

        commandLine.addArgument(MessageFormat.format("--jail={0}",
                this.storage.getPath().resolve("chroot").toAbsolutePath().toString()));

        commandLine.addArgument("--");
        commandLine
                .addArgument("/" + this.storage.getPath().resolve("chroot").relativize(chrootedProgram).toString());

        final DefaultExecutor executor = new DefaultExecutor();

        executor.setExitValue(0);

        executor.setWatchdog(new ExecuteWatchdog(60000)); // 60 seconds for the
        // sandbox to
        // complete

        executor.setWorkingDirectory(this.storage.getPath().toFile());

        executor.setStreamHandler(new PumpStreamHandler(this.outputStream, this.errorStream, this.inputStream));

        SandboxedExecutor.logger.debug("Executing in sandbox: {}", commandLine.toString());
        try {
            executor.execute(commandLine);
        } catch (final ExecuteException e) {
            SandboxedExecutor.logger.info("Execution failed: {}", e);
            throw e;
        } catch (final IOException e) {
            if (!e.getMessage().toLowerCase().equals("stream closed")) {
                throw e;
            }
        }

        return this.readOlrunnerVerdict(this.storage.getPath().resolve("verdict.txt"));
    }

    @Override
    public long getCpuLimit() {
        return this.cpuLimit;
    }

    @Override
    public long getDiskLimit() {
        return this.diskLimit;
    }

    @Override
    public OutputStream getErrorStream() {
        return this.errorStream;
    }

    @Override
    public void getFile(final String name, final Path destination) throws IOException {
        FileAccess.copy(this.storage.getPath().resolve("chroot").resolve(name), destination,
                StandardCopyOption.REPLACE_EXISTING);
    }

    @Override
    public InputStream getInputStream() {
        return this.inputStream;
    }

    @Override
    public long getMemoryLimit() {
        return this.memoryLimit;
    }

    @Override
    public OutputStream getOutputStream() {
        return this.outputStream;
    }

    @Override
    public long getTimeLimit() {
        return this.timeLimit;
    }

    @Override
    public void provideFile(final Path file) throws IOException {
        SandboxedExecutor.logger.info("Providing file {}", file);
        FileAccess.copy(file, this.storage.getPath().resolve("chroot").resolve(file.getFileName()));
    }

    @Override
    public SandboxedExecutor setCpuLimit(final long cpuLimit) {
        this.cpuLimit = cpuLimit;
        return this;
    }

    @Override
    public SandboxedExecutor setDiskLimit(final long diskLimit) {
        this.diskLimit = diskLimit;
        return this;
    }

    @Override
    public SandboxedExecutor setErrorStream(final OutputStream errorStream) {
        this.errorStream = errorStream;
        return this;
    }

    @Override
    public SandboxedExecutor setInputStream(final InputStream inputStream) {
        this.inputStream = inputStream;
        return this;
    }

    @Override
    public SandboxedExecutor setMemoryLimit(final long memoryLimit) {
        this.memoryLimit = memoryLimit;
        return this;
    }

    @Override
    public SandboxedExecutor setOutputStream(final OutputStream outputStream) {
        this.outputStream = outputStream;
        return this;
    }

    @Override
    public SandboxedExecutor setTimeLimit(final long timeLimit) {
        this.timeLimit = timeLimit;
        return this;
    }
}