From 0d84baaf05329608969ef8ce62b082a54f76a641 Mon Sep 17 00:00:00 2001 From: gongdewei Date: Wed, 27 May 2020 11:40:18 +0800 Subject: [PATCH] double check telnet port and pid before attach agent (#1119) --- bin/as.sh | 82 ++++- .../com/taobao/arthas/boot/Bootstrap.java | 90 +++++- .../com/taobao/arthas/boot/ProcessUtils.java | 68 ++++ .../java/com/taobao/arthas/client/IOUtil.java | 1 - .../taobao/arthas/client/TelnetConsole.java | 298 +++++++++++------- 5 files changed, 419 insertions(+), 120 deletions(-) diff --git a/bin/as.sh b/bin/as.sh index 7974e2701..1dd5cbfad 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -475,6 +475,34 @@ find_listen_port_process() fi } +# Status from com.taobao.arthas.client.TelnetConsole +# Execute commands timeout +STATUS_EXEC_TIMEOUT=100 +# Execute commands error +STATUS_EXEC_ERROR=101 + +# find the process pid of target telnet port +# maybe another user start an arthas server at the same port, but invisible for current user +find_listen_port_process_by_client() +{ + local arthas_lib_dir="${ARTHAS_HOME}" + # http://www.inonit.com/cygwin/faq/ + if [ "${OS_TYPE}" = "Cygwin" ]; then + arthas_lib_dir=`cygpath -wp "$arthas_lib_dir"` + fi + + "${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \ + -jar "${arthas_lib_dir}/arthas-client.jar" \ + ${TARGET_IP} \ + ${TELNET_PORT} \ + -c "session" \ + --execution-timeout 2000 \ + 2>&1 + + # return java process exit status code ! + return $? +} + parse_arguments() { POSITIONAL=() @@ -699,9 +727,7 @@ parse_arguments() # check the process already using telnet port if equals to target pid if [[ ($telnetPortPid) && ($TARGET_PID != $telnetPortPid) ]]; then - echo "[ERROR] Target process $TARGET_PID is not the process using port $TELNET_PORT, you will connect to an unexpected process." - echo "[ERROR] 1. Try to restart as.sh, select process $telnetPortPid, shutdown it first with running the 'stop' command." - echo "[ERROR] 2. Try to use different telnet port, for example: as.sh --telnet-port 9998 --http-port -1" + print_telnet_port_pid_error exit 1 fi if [[ ($httpPortPid) && ($TARGET_PID != $httpPortPid) ]]; then @@ -798,6 +824,52 @@ sanity_check() { fi } +port_pid_check() { + if [[ $TELNET_PORT > 0 ]]; then + local telnet_output + local find_process_status + # declare local var before var=$() + telnet_output=$(find_listen_port_process_by_client) + find_process_status=$? + #echo "find_process_status: $find_process_status" + #echo "telnet_output: $telnet_output" + + #check return code + if [[ $find_process_status -eq $STATUS_EXEC_TIMEOUT ]]; then + print_telnet_port_used_error "detection timeout" + exit 1 + elif [[ $find_process_status -eq $STATUS_EXEC_ERROR ]]; then + print_telnet_port_used_error "detection error" + exit 1 + fi + + if [[ -n $telnet_output ]]; then + # check JAVA_PID + telnetPortPid=$(echo "$telnet_output" | grep JAVA_PID | awk '{ print $2 }') + #echo "telnetPortPid: $telnetPortPid" + # check the process already using telnet port if equals to target pid + if [[ -n $telnetPortPid && ($TARGET_PID != $telnetPortPid) ]]; then + print_telnet_port_pid_error + exit 1 + fi + fi + + fi +} + +print_telnet_port_pid_error() { + echo "[ERROR] The telnet port $TELNET_PORT is used by process $telnetPortPid instead of target process $TARGET_PID, you will connect to an unexpected process." + echo "[ERROR] 1. Try to restart as.sh, select process $telnetPortPid, shutdown it first with running the 'stop' command." + echo "[ERROR] 2. Try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 $TELNET_PORT -c \"stop\"" + echo "[ERROR] 3. Try to use different telnet port, for example: as.sh --telnet-port 9998 --http-port -1" +} + +print_telnet_port_used_error() { + local error_msg=$1 + echo "[ERROR] The telnet port $TELNET_PORT is used, but process $error_msg, you will connect to an unexpected process." + echo "[ERROR] Try to use different telnet port, for example: as.sh --telnet-port 9998 --http-port -1" +} + # active console # $1 : arthas_lib_dir active_console() @@ -899,6 +971,8 @@ main() sanity_check + port_pid_check + echo "Calculating attach execution time..." time (attach_jvm "${ARTHAS_HOME}" || exit 1) @@ -915,4 +989,4 @@ main() -main "${@}" +main "${@}" \ No newline at end of file diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 77c1ed04e..1f6809507 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -1,5 +1,6 @@ package com.taobao.arthas.boot; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -11,6 +12,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Scanner; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -34,6 +36,9 @@ import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; +import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_ERROR; +import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + /** * @author hengyunabc 2018-10-26 * @@ -353,14 +358,7 @@ public class Bootstrap { } } - if (telnetPortPid > 0 && pid != telnetPortPid) { - AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.", - pid, bootstrap.getTelnetPort()); - AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.", - telnetPortPid); - AnsiLog.error("2. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1"); - System.exit(1); - } + checkTelnetPortPid(bootstrap, telnetPortPid, pid); if (httpPortPid > 0 && pid != httpPortPid) { AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.", @@ -474,6 +472,10 @@ public class Bootstrap { if (telnetPortPid > 0 && pid == telnetPortPid) { AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPort()); } else { + //double check telnet port and pid before attach + telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPort()); + checkTelnetPortPid(bootstrap, telnetPortPid, pid); + // start arthas-core.jar List attachArgs = new ArrayList(); attachArgs.add("-jar"); @@ -556,6 +558,78 @@ public class Bootstrap { mainMethod.invoke(null, new Object[] { telnetArgs.toArray(new String[0]) }); } + private static void checkTelnetPortPid(Bootstrap bootstrap, long telnetPortPid, long targetPid) { + if (telnetPortPid > 0 && targetPid != telnetPortPid) { + AnsiLog.error("The telnet port {} is used by process {} instead of target process {}, you will connect to an unexpected process.", + bootstrap.getTelnetPort(), telnetPortPid, targetPid); + AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.", + telnetPortPid); + AnsiLog.error("2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 {} -c \"stop\"", bootstrap.getTelnetPort()); + AnsiLog.error("3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1"); + System.exit(1); + } + } + + private static long findProcessByTelnetClient(String arthasHomeDir, int telnetPort) { + // start java telnet client + List telnetArgs = new ArrayList(); + telnetArgs.add("-c"); + telnetArgs.add("session"); + telnetArgs.add("--execution-timeout"); + telnetArgs.add("2000"); + // telnet port ,ip + telnetArgs.add("127.0.0.1"); + telnetArgs.add("" + telnetPort); + + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + String error = null; + int status = ProcessUtils.startArthasClient(arthasHomeDir, telnetArgs, out); + if (status == STATUS_EXEC_TIMEOUT) { + error = "detection timeout"; + } else if (status == STATUS_EXEC_ERROR) { + error = "detection error"; + AnsiLog.error("process status: {}", status); + AnsiLog.error("process output: {}", out.toString()); + } else { + // ignore connect error + } + if (error != null) { + AnsiLog.error("The telnet port {} is used, but process {}, you will connect to an unexpected process.", telnetPort, error); + AnsiLog.error("Try to use a different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1"); + System.exit(1); + } + + //parse output, find java pid + String output = out.toString("UTF-8"); + String javaPidLine = null; + Scanner scanner = new Scanner(output); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.contains("JAVA_PID")) { + javaPidLine = line; + break; + } + } + if (javaPidLine != null) { + // JAVA_PID 10473 + try { + String[] strs = javaPidLine.split("JAVA_PID"); + if (strs.length > 1) { + return Long.parseLong(strs[strs.length - 1].trim()); + } + } catch (NumberFormatException e) { + // ignore + } + } + } catch (Throwable ex) { + AnsiLog.error("Detection telnet port error"); + AnsiLog.error(ex); + } + + return -1; + } + private static String listVersions(String mavenMetaData) { StringBuilder result = new StringBuilder(1024); List versionList = listNames(ARTHAS_LIB_DIR); diff --git a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java index cd52a9897..514322b50 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java @@ -3,6 +3,11 @@ package com.taobao.arthas.boot; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -28,6 +33,24 @@ import com.taobao.arthas.common.PidUtils; public class ProcessUtils { private static String FOUND_JAVA_HOME = null; + //status code from com.taobao.arthas.client.TelnetConsole + /** + * Process success + */ + public static final int STATUS_OK = 0; + /** + * Generic error + */ + public static final int STATUS_ERROR = 1; + /** + * Execute commands timeout + */ + public static final int STATUS_EXEC_TIMEOUT = 100; + /** + * Execute commands error + */ + public static final int STATUS_EXEC_ERROR = 101; + @SuppressWarnings("resource") public static long select(boolean v, long telnetPortPid, String select) throws InputMismatchException { Map processMap = listProcessByJps(v); @@ -290,7 +313,52 @@ public class ProcessUtils { } catch (Throwable e) { // ignore } + } + public static int startArthasClient(String arthasHomeDir, List telnetArgs, OutputStream out) throws Throwable { + // start java telnet client + // find arthas-client.jar + URLClassLoader classLoader = new URLClassLoader( + new URL[]{new File(arthasHomeDir, "arthas-client.jar").toURI().toURL()}); + Class telnetConsoleClas = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole"); + Method processMethod = telnetConsoleClas.getMethod("process", String[].class); + + //redirect System.out/System.err + PrintStream originSysOut = System.out; + PrintStream originSysErr = System.err; + PrintStream newOut = new PrintStream(out); + PrintStream newErr = new PrintStream(out); + + // call TelnetConsole.process() + // fix https://github.com/alibaba/arthas/issues/833 + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + System.setOut(newOut); + System.setErr(newErr); + Thread.currentThread().setContextClassLoader(classLoader); + return (Integer) processMethod.invoke(null, new Object[]{telnetArgs.toArray(new String[0])}); + } catch (Throwable e) { + //java.lang.reflect.InvocationTargetException : java.net.ConnectException + e = e.getCause(); + if (e instanceof IOException || e instanceof InterruptedException) { + // ignore connection error and interrupted error + return STATUS_ERROR; + } else { + // process error + AnsiLog.error("process error: {}", e.toString()); + AnsiLog.error(e); + return STATUS_EXEC_ERROR; + } + } finally { + Thread.currentThread().setContextClassLoader(tccl); + + //reset System.out/System.err + System.setOut(originSysOut); + System.setErr(originSysErr); + //flush output + newOut.flush(); + newErr.flush(); + } } private static File findJava() { diff --git a/client/src/main/java/com/taobao/arthas/client/IOUtil.java b/client/src/main/java/com/taobao/arthas/client/IOUtil.java index 41c976efa..a1a360b7f 100644 --- a/client/src/main/java/com/taobao/arthas/client/IOUtil.java +++ b/client/src/main/java/com/taobao/arthas/client/IOUtil.java @@ -53,7 +53,6 @@ public final class IOUtil { } } catch (IOException e) { e.printStackTrace(); - System.exit(1); } } }; diff --git a/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java b/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java index e8fda7b7c..be32d11fb 100644 --- a/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java +++ b/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import org.apache.commons.net.telnet.InvalidTelnetOptionException; import org.apache.commons.net.telnet.TelnetClient; @@ -53,6 +54,25 @@ public class TelnetConsole { private static final byte CTRL_C = 0x03; + // ------- Status codes ------- // + /** + * Process success + */ + public static final int STATUS_OK = 0; + /** + * Generic error + */ + public static final int STATUS_ERROR = 1; + /** + * Execute commands timeout + */ + public static final int STATUS_EXEC_TIMEOUT = 100; + /** + * Execute commands error + */ + public static final int STATUS_EXEC_ERROR = 101; + + private boolean help = false; private String targetIp = "127.0.0.1"; @@ -60,6 +80,7 @@ public class TelnetConsole { private String command; private String batchFile; + private int executionTimeout = -1; private Integer width = null; private Integer height = null; @@ -94,6 +115,12 @@ public class TelnetConsole { this.batchFile = batchFile; } + @Option(shortName = "t", longName = "execution-timeout") + @Description("The timeout (ms) of execute commands or batch file ") + public void setExecutionTimeout(int executionTimeout) { + this.executionTimeout = executionTimeout; + } + @Option(shortName = "w", longName = "width") @Description("The terminal width") public void setWidth(int width) { @@ -133,147 +160,190 @@ public class TelnetConsole { return list; } - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws Exception { + + try { + int status = process(args, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.exit(STATUS_OK); + } + }); + System.exit(status); + } catch (Throwable e) { + e.printStackTrace(); + CLI cli = CLIConfigurator.define(TelnetConsole.class); + System.out.println(usage(cli)); + System.exit(STATUS_ERROR); + } + + } + + /** + * 提供给arthas-boot使用的主处理函数 + * + * @param args + * @return status code + * @throws IOException + * @throws InterruptedException + */ + public static int process(String[] args) throws IOException, InterruptedException { + return process(args, null); + } + + /** + * arthas client 主函数 + * 注意: process()函数提供给arthas-boot使用,内部不能调用System.exit()结束进程的方法 + * + * @param telnetConsole + * @param cli + * @param args + * @param eotEventCallback Ctrl+D signals an End of Transmission (EOT) event + * @return status code + * @throws IOException + * @throws InterruptedException + */ + public static int process(String[] args, ActionListener eotEventCallback) throws IOException, InterruptedException { // support mingw/cygw jline color if (OSUtils.isCygwinOrMinGW()) { System.setProperty("jline.terminal", System.getProperty("jline.terminal", "jline.UnixTerminal")); } TelnetConsole telnetConsole = new TelnetConsole(); - CLI cli = CLIConfigurator.define(TelnetConsole.class); - try { - CommandLine commandLine = cli.parse(Arrays.asList(args)); + CommandLine commandLine = cli.parse(Arrays.asList(args)); - CLIConfigurator.inject(commandLine, telnetConsole); + CLIConfigurator.inject(commandLine, telnetConsole); - if (telnetConsole.isHelp()) { - System.out.println(usage(cli)); - System.exit(0); - } + if (telnetConsole.isHelp()) { + System.out.println(usage(cli)); + return STATUS_ERROR; + } - // Try to read cmds - List cmds = new ArrayList(); - if (telnetConsole.getCommand() != null) { - for (String c : telnetConsole.getCommand().split(";")) { - cmds.add(c.trim()); - } - } else if (telnetConsole.getBatchFile() != null) { - File file = new File(telnetConsole.getBatchFile()); - if (!file.exists()) { - throw new IllegalArgumentException("batch file do not exist: " + telnetConsole.getBatchFile()); - } else { - cmds.addAll(readLines(file)); - } + // Try to read cmds + List cmds = new ArrayList(); + if (telnetConsole.getCommand() != null) { + for (String c : telnetConsole.getCommand().split(";")) { + cmds.add(c.trim()); + } + } else if (telnetConsole.getBatchFile() != null) { + File file = new File(telnetConsole.getBatchFile()); + if (!file.exists()) { + throw new IllegalArgumentException("batch file do not exist: " + telnetConsole.getBatchFile()); + } else { + cmds.addAll(readLines(file)); } + } - final ConsoleReader consoleReader = new ConsoleReader(System.in, System.out); - consoleReader.setHandleUserInterrupt(true); - Terminal terminal = consoleReader.getTerminal(); + final ConsoleReader consoleReader = new ConsoleReader(System.in, System.out); + consoleReader.setHandleUserInterrupt(true); + Terminal terminal = consoleReader.getTerminal(); - if (terminal instanceof TerminalSupport) { - ((TerminalSupport) terminal).disableInterruptCharacter(); - } + if (terminal instanceof TerminalSupport) { + ((TerminalSupport) terminal).disableInterruptCharacter(); + } - // support catch ctrl+c event - terminal.disableInterruptCharacter(); - if (terminal instanceof UnixTerminal) { - ((UnixTerminal) terminal).disableLitteralNextCharacter(); - } + // support catch ctrl+c event + terminal.disableInterruptCharacter(); + if (terminal instanceof UnixTerminal) { + ((UnixTerminal) terminal).disableLitteralNextCharacter(); + } - int width = TerminalSupport.DEFAULT_WIDTH; - int height = TerminalSupport.DEFAULT_HEIGHT; + int width = TerminalSupport.DEFAULT_WIDTH; + int height = TerminalSupport.DEFAULT_HEIGHT; - if (!cmds.isEmpty()) { - // batch mode - if (telnetConsole.getWidth() != null) { - width = telnetConsole.getWidth(); - } - if (telnetConsole.getheight() != null) { - height = telnetConsole.getheight(); - } + if (!cmds.isEmpty()) { + // batch mode + if (telnetConsole.getWidth() != null) { + width = telnetConsole.getWidth(); + } + if (telnetConsole.getheight() != null) { + height = telnetConsole.getheight(); + } + } else { + // normal telnet client, get current terminal size + if (telnetConsole.getWidth() != null) { + width = telnetConsole.getWidth(); } else { - // normal telnet client, get current terminal size - if (telnetConsole.getWidth() != null) { - width = telnetConsole.getWidth(); - } else { - width = terminal.getWidth(); - // hack for windows dos - if (OSUtils.isWindows()) { - width--; - } - } - if (telnetConsole.getheight() != null) { - height = telnetConsole.getheight(); - } else { - height = terminal.getHeight(); + width = terminal.getWidth(); + // hack for windows dos + if (OSUtils.isWindows()) { + width--; } } + if (telnetConsole.getheight() != null) { + height = telnetConsole.getheight(); + } else { + height = terminal.getHeight(); + } + } - final TelnetClient telnet = new TelnetClient(); - telnet.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT); + final TelnetClient telnet = new TelnetClient(); + telnet.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT); - // send init terminal size - TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler(width, height, true, true, false, false); - try { - telnet.addOptionHandler(sizeOpt); - } catch (InvalidTelnetOptionException e) { - // ignore - } + // send init terminal size + TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler(width, height, true, true, false, false); + try { + telnet.addOptionHandler(sizeOpt); + } catch (InvalidTelnetOptionException e) { + // ignore + } - // ctrl + c event callback - consoleReader.getKeys().bind(new Character((char) CTRL_C).toString(), new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - consoleReader.getCursorBuffer().clear(); // clear current line - telnet.getOutputStream().write(CTRL_C); - telnet.getOutputStream().flush(); - } catch (Exception e1) { - e1.printStackTrace(); - } + // ctrl + c event callback + consoleReader.getKeys().bind(new Character((char) CTRL_C).toString(), new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + consoleReader.getCursorBuffer().clear(); // clear current line + telnet.getOutputStream().write(CTRL_C); + telnet.getOutputStream().flush(); + } catch (Exception e1) { + e1.printStackTrace(); } + } - }); + }); - // ctrl + d event call back - consoleReader.getKeys().bind(new Character(KeyMap.CTRL_D).toString(), new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - System.exit(0); - } - }); + // ctrl + d event call back + consoleReader.getKeys().bind(new Character(KeyMap.CTRL_D).toString(), eotEventCallback); - try { - telnet.connect(telnetConsole.getTargetIp(), telnetConsole.getPort()); - } catch (IOException e) { - System.out.println("Connect to telnet server error: " + telnetConsole.getTargetIp() + " " - + telnetConsole.getPort()); - throw e; - } + try { + telnet.connect(telnetConsole.getTargetIp(), telnetConsole.getPort()); + } catch (IOException e) { + System.out.println("Connect to telnet server error: " + telnetConsole.getTargetIp() + " " + + telnetConsole.getPort()); + throw e; + } - if (cmds.isEmpty()) { - IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, - consoleReader.getOutput()); - } else { - batchModeRun(telnet, cmds); - telnet.disconnect(); + if (cmds.isEmpty()) { + IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, + consoleReader.getOutput()); + } else { + try { + return batchModeRun(telnet, cmds, telnetConsole.getExecutionTimeout()); + } catch (Throwable e) { + System.out.println("Execute commands error: " + e.getMessage()); + e.printStackTrace(); + return STATUS_EXEC_ERROR; + } finally { + try { + telnet.disconnect(); + } catch (IOException e) { + //ignore ex + } } - } catch (Throwable e) { - e.printStackTrace(); - System.out.println(usage(cli)); - System.exit(1); } - + return STATUS_OK; } - private static void batchModeRun(TelnetClient telnet, List commands) + private static int batchModeRun(TelnetClient telnet, List commands, final int executionTimeout) throws IOException, InterruptedException { if (commands.size() == 0) { - return; + return STATUS_OK; } + long startTime = System.currentTimeMillis(); final InputStream inputStream = telnet.getInputStream(); final OutputStream outputStream = telnet.getOutputStream(); @@ -305,14 +375,22 @@ public class TelnetConsole { } } }); - printResultThread.start(); + // send commands to arthas server for (String command : commands) { if (command.trim().isEmpty()) { continue; } - receviedPromptQueue.take(); + // try poll prompt and check timeout + while (receviedPromptQueue.poll(100, TimeUnit.MILLISECONDS) == null) { + if (executionTimeout > 0) { + long now = System.currentTimeMillis(); + if (now - startTime > executionTimeout) { + return STATUS_EXEC_TIMEOUT; + } + } + } // send command to server outputStream.write((command + " | plaintext\n").getBytes()); outputStream.flush(); @@ -323,6 +401,8 @@ public class TelnetConsole { outputStream.write("quit\n".getBytes()); outputStream.flush(); System.out.println(); + + return STATUS_OK; } private static String usage(CLI cli) { @@ -349,6 +429,10 @@ public class TelnetConsole { return batchFile; } + public int getExecutionTimeout() { + return executionTimeout; + } + public Integer getWidth() { return width; }