diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/ExternalProcessExecutor.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/ExternalProcessExecutor.java index c92943eb4dca41c02f3c1d3f0c18eef612792453..d583f1eaad97ca8d8eb9445b9d3a7950e0f6f029 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/ExternalProcessExecutor.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/ExternalProcessExecutor.java @@ -19,66 +19,76 @@ package org.bigbluebutton.presentation.imp; -import java.util.Timer; -import java.util.TimerTask; +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A wrapper class the executes an external command. - * - * See http://kylecartmell.com/?p=9 + * A wrapper class the executes an external command. * * @author Richard Alam - * + * @author Marcel Hellkamp */ public class ExternalProcessExecutor { private static Logger log = LoggerFactory.getLogger(ExternalProcessExecutor.class); - - public boolean exec(String COMMAND, long timeoutMillis) { - Timer timer = new Timer(false); - Process p = null; - try { - InterruptTimerTask interrupter = new InterruptTimerTask(Thread.currentThread()); - timer.schedule(interrupter, timeoutMillis); - p = Runtime.getRuntime().exec(COMMAND); - int result = p.waitFor(); - if (result == 0) { - return true; - } else { - return false; - } + // Replace with ProcessBuilder.Redirect.DISCARD in java 9+ + private static File DISCARD = new File( + System.getProperty("os.name").startsWith("Windows") ? "NUL" : "/dev/null"); - } catch(Exception e) { - log.info("TIMEDOUT excuting : {}", COMMAND); - if (p != null) { - p.destroy(); - } - } finally { - timer.cancel(); // If the process returns within the timeout period, we have to stop the interrupter - // so that it does not unexpectedly interrupt some other code later. - - Thread.interrupted(); // We need to clear the interrupt flag on the current thread just in case - // interrupter executed after waitFor had already returned but before timer.cancel - // took effect. - // - // Oh, and there's also Sun bug 6420270 to worry about here. - } - return false; + /** + * Run COMMAND for at most timeoutMillis while ignoring any output. + * + * @deprecated The COMMAND string is split on whitespace to create an argument + * list. This won't work for arguments that contain whitespace. Use + * {@link #exec(List, Duration)} instead. + * + * @param COMMAND A single command or whitespace separated list of + * arguments. + * @param timeoutMillis Timeout in milliseconds. + * @return true if the command terminated in time with an exit value of 0. + */ + @Deprecated + public boolean exec(String COMMAND, long timeoutMillis) { + return exec(Arrays.asList(COMMAND.split("[ \\t\\n\\r\\f]+")), Duration.ofMillis(timeoutMillis)); } - - class InterruptTimerTask extends TimerTask { - private Thread thread; + /** + * Run a command for a limited amount of time while ignoring any output. + * + * @param cmd List containing the program and its arguments. + * @param timeout Maximum execution time. + * @return true if the command terminated in time with an exit value of 0. + */ + public boolean exec(List<String> cmd, Duration timeout) { - public InterruptTimerTask(Thread t) { - this.thread = t; - } + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectError(DISCARD); + pb.redirectOutput(DISCARD); - public void run() { - thread.interrupt(); - } + Process proc; + try { + proc = pb.start(); + } catch (IOException e) { + log.error("Failed to execute: {}", String.join(" ", cmd), e); + return false; + } + try { + if (!proc.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS)) { + log.warn("TIMEDOUT excuting: {}", String.join(" ", cmd)); + proc.destroy(); + } + return !proc.isAlive() && proc.exitValue() == 0; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + proc.destroy(); + return false; + } } }