Java tutorial
package de.alexkamp.sandbox; /** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. * * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import com.fasterxml.jackson.core.JsonGenerator; import de.alexkamp.sandbox.channels.NullChannel; import java.io.IOException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A process which is running inside a sandbox. * * Created by akampmann on 2/7/15. */ public class ChrootSandboxProcess implements SandboxProcess { private static final Pattern exit = Pattern.compile("exit status ([0-9]*)"); private static final Pattern timePattern = Pattern.compile("Message: (.*), Runtime: ([0-9]*)"); private final ChrootSandbox sandbox; private final String workdir; private final long timeout; private final String executable; private final String[] args; /** This lock (and condition) indicate whether the process is running. */ private final Lock running = new ReentrantLock(); private boolean isRunning = false; private final Condition condition = running.newCondition(); /** Those fields hold data about an already terminated process. */ private String exitMessage; private long runtime; private boolean hadTimeout; private SandboxChannel stdout = new NullChannel(); private SandboxChannel stderr = new NullChannel(); public ChrootSandboxProcess(ChrootSandbox sandbox, String workdir, String executable, String[] args, long timeout) { this.sandbox = sandbox; this.workdir = workdir; this.executable = executable; this.args = args; this.timeout = timeout; } /** * start the process inside the sandbox. * * @return */ @Override public int start() throws SandboxTimeoutException { try { running.lock(); sandbox.start(this); isRunning = true; // condition will be signalled if the process terminates while (isRunning) { condition.await(); sandbox.checkState(); } if (hadTimeout) { throw new SandboxTimeoutException(exitMessage); } // try to parse the output for the exit code Matcher matcher = exit.matcher(exitMessage); if (matcher.find()) { return Integer.parseInt(matcher.group(1)); } throw new SandboxException(exitMessage); } catch (InterruptedException e) { throw new SandboxException(e); } finally { running.unlock(); } } public SandboxChannel getStdout() { return stdout; } @Override public void setStdout(SandboxChannel stdout) { this.stdout = stdout; } public SandboxChannel getStderr() { return stderr; } @Override public void setStderr(SandboxChannel stderr) { this.stderr = stderr; } @Override public String getExitMessage() { return exitMessage; } /** * Internal method to handle incoming communication. * * @param channel - identifier of the channel, either stdout, stderr or exit * @param message - the message on the channel * @return - true if more input is expected */ protected boolean handle(String channel, String message) { switch (channel) { case "stdout": stdout.receive(message); return true; case "stderr": stderr.receive(message); return true; case "exit": handleExit(message, false); return false; case "timeout": handleExit(message, true); return false; case "error": handleExit(message, false); throw new SandboxException("A process error: \"" + message + "\" in " + executable); default: throw new SandboxException( "A process received a message it should not have: " + channel + " \"" + message + "\""); } } /** * called if the process exits * @param message */ private void handleExit(String message, boolean timeout) { try { running.lock(); this.isRunning = false; this.hadTimeout = timeout; if (!timeout) { Matcher matcher = timePattern.matcher(message); if (matcher.find()) { this.exitMessage = matcher.group(1); this.runtime = Long.parseLong(matcher.group(2)); } else { this.exitMessage = message; this.runtime = -1; } } else { this.exitMessage = message; } } finally { running.unlock(); } } public String getWorkdir() { return workdir; } @Override public String getExecutable() { return executable; } @Override public String[] getArgs() { return args; } public void toJson(JsonGenerator sender) throws IOException { sender.writeStartObject(); sender.writeObjectField("WorkDir", getWorkdir()); sender.writeObjectField("Executable", getExecutable()); sender.writeObjectField("Timeout", getTimeout()); sender.writeArrayFieldStart("Arguments"); for (String arg : getArgs()) { sender.writeString(arg); } sender.writeEndArray(); sender.writeEndObject(); } public long getTimeout() { return timeout; } @Override public boolean hadTimeout() { return hadTimeout; } @Override public long getRuntime() { return runtime; } public void wakeUp() { try { running.lock(); condition.signalAll(); } finally { running.unlock(); } } }