@ -14,6 +14,7 @@ import java.util.Arrays;
import java.util.List ;
import java.util.List ;
import java.util.concurrent.BlockingQueue ;
import java.util.concurrent.BlockingQueue ;
import java.util.concurrent.LinkedBlockingQueue ;
import java.util.concurrent.LinkedBlockingQueue ;
import java.util.concurrent.TimeUnit ;
import org.apache.commons.net.telnet.InvalidTelnetOptionException ;
import org.apache.commons.net.telnet.InvalidTelnetOptionException ;
import org.apache.commons.net.telnet.TelnetClient ;
import org.apache.commons.net.telnet.TelnetClient ;
@ -53,6 +54,25 @@ public class TelnetConsole {
private static final byte CTRL_C = 0x03 ;
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 boolean help = false ;
private String targetIp = "127.0.0.1" ;
private String targetIp = "127.0.0.1" ;
@ -60,6 +80,7 @@ public class TelnetConsole {
private String command ;
private String command ;
private String batchFile ;
private String batchFile ;
private int executionTimeout = - 1 ;
private Integer width = null ;
private Integer width = null ;
private Integer height = null ;
private Integer height = null ;
@ -94,6 +115,12 @@ public class TelnetConsole {
this . batchFile = batchFile ;
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" )
@Option ( shortName = "w" , longName = "width" )
@Description ( "The terminal width" )
@Description ( "The terminal width" )
public void setWidth ( int width ) {
public void setWidth ( int width ) {
@ -133,147 +160,190 @@ public class TelnetConsole {
return list ;
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
// support mingw/cygw jline color
if ( OSUtils . isCygwinOrMinGW ( ) ) {
if ( OSUtils . isCygwinOrMinGW ( ) ) {
System . setProperty ( "jline.terminal" , System . getProperty ( "jline.terminal" , "jline.UnixTerminal" ) ) ;
System . setProperty ( "jline.terminal" , System . getProperty ( "jline.terminal" , "jline.UnixTerminal" ) ) ;
}
}
TelnetConsole telnetConsole = new TelnetConsole ( ) ;
TelnetConsole telnetConsole = new TelnetConsole ( ) ;
CLI cli = CLIConfigurator . define ( TelnetConsole . class ) ;
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 ( ) ) {
if ( telnetConsole . isHelp ( ) ) {
System . out . println ( usage ( cli ) ) ;
System . out . println ( usage ( cli ) ) ;
System . exit ( 0 ) ;
return STATUS_ERROR ;
}
}
// Try to read cmds
// Try to read cmds
List < String > cmds = new ArrayList < String > ( ) ;
List < String > cmds = new ArrayList < String > ( ) ;
if ( telnetConsole . getCommand ( ) ! = null ) {
if ( telnetConsole . getCommand ( ) ! = null ) {
for ( String c : telnetConsole . getCommand ( ) . split ( ";" ) ) {
for ( String c : telnetConsole . getCommand ( ) . split ( ";" ) ) {
cmds . add ( c . trim ( ) ) ;
cmds . add ( c . trim ( ) ) ;
}
}
} else if ( telnetConsole . getBatchFile ( ) ! = null ) {
} else if ( telnetConsole . getBatchFile ( ) ! = null ) {
File file = new File ( telnetConsole . getBatchFile ( ) ) ;
File file = new File ( telnetConsole . getBatchFile ( ) ) ;
if ( ! file . exists ( ) ) {
if ( ! file . exists ( ) ) {
throw new IllegalArgumentException ( "batch file do not exist: " + telnetConsole . getBatchFile ( ) ) ;
throw new IllegalArgumentException ( "batch file do not exist: " + telnetConsole . getBatchFile ( ) ) ;
} else {
} else {
cmds . addAll ( readLines ( file ) ) ;
cmds . addAll ( readLines ( file ) ) ;
}
}
}
}
final ConsoleReader consoleReader = new ConsoleReader ( System . in , System . out ) ;
final ConsoleReader consoleReader = new ConsoleReader ( System . in , System . out ) ;
consoleReader . setHandleUserInterrupt ( true ) ;
consoleReader . setHandleUserInterrupt ( true ) ;
Terminal terminal = consoleReader . getTerminal ( ) ;
Terminal terminal = consoleReader . getTerminal ( ) ;
if ( terminal instanceof TerminalSupport ) {
if ( terminal instanceof TerminalSupport ) {
( ( TerminalSupport ) terminal ) . disableInterruptCharacter ( ) ;
( ( TerminalSupport ) terminal ) . disableInterruptCharacter ( ) ;
}
}
// support catch ctrl+c event
// support catch ctrl+c event
terminal . disableInterruptCharacter ( ) ;
terminal . disableInterruptCharacter ( ) ;
if ( terminal instanceof UnixTerminal ) {
if ( terminal instanceof UnixTerminal ) {
( ( UnixTerminal ) terminal ) . disableLitteralNextCharacter ( ) ;
( ( UnixTerminal ) terminal ) . disableLitteralNextCharacter ( ) ;
}
}
int width = TerminalSupport . DEFAULT_WIDTH ;
int width = TerminalSupport . DEFAULT_WIDTH ;
int height = TerminalSupport . DEFAULT_HEIGHT ;
int height = TerminalSupport . DEFAULT_HEIGHT ;
if ( ! cmds . isEmpty ( ) ) {
if ( ! cmds . isEmpty ( ) ) {
// batch mode
// batch mode
if ( telnetConsole . getWidth ( ) ! = null ) {
if ( telnetConsole . getWidth ( ) ! = null ) {
width = telnetConsole . getWidth ( ) ;
width = telnetConsole . getWidth ( ) ;
}
}
if ( telnetConsole . getheight ( ) ! = null ) {
if ( telnetConsole . getheight ( ) ! = null ) {
height = telnetConsole . getheight ( ) ;
height = telnetConsole . getheight ( ) ;
}
}
} else {
// normal telnet client, get current terminal size
if ( telnetConsole . getWidth ( ) ! = null ) {
width = telnetConsole . getWidth ( ) ;
} else {
} else {
// normal telnet client, get current terminal size
width = terminal . getWidth ( ) ;
if ( telnetConsole . getWidth ( ) ! = null ) {
// hack for windows dos
width = telnetConsole . getWidth ( ) ;
if ( OSUtils . isWindows ( ) ) {
} else {
width - - ;
width = terminal . getWidth ( ) ;
// hack for windows dos
if ( OSUtils . isWindows ( ) ) {
width - - ;
}
}
if ( telnetConsole . getheight ( ) ! = null ) {
height = telnetConsole . getheight ( ) ;
} else {
height = terminal . getHeight ( ) ;
}
}
}
}
if ( telnetConsole . getheight ( ) ! = null ) {
height = telnetConsole . getheight ( ) ;
} else {
height = terminal . getHeight ( ) ;
}
}
final TelnetClient telnet = new TelnetClient ( ) ;
final TelnetClient telnet = new TelnetClient ( ) ;
telnet . setConnectTimeout ( DEFAULT_CONNECTION_TIMEOUT ) ;
telnet . setConnectTimeout ( DEFAULT_CONNECTION_TIMEOUT ) ;
// send init terminal size
// send init terminal size
TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler ( width , height , true , true , false , false ) ;
TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler ( width , height , true , true , false , false ) ;
try {
try {
telnet . addOptionHandler ( sizeOpt ) ;
telnet . addOptionHandler ( sizeOpt ) ;
} catch ( InvalidTelnetOptionException e ) {
} catch ( InvalidTelnetOptionException e ) {
// ignore
// ignore
}
}
// ctrl + c event callback
// ctrl + c event callback
consoleReader . getKeys ( ) . bind ( new Character ( ( char ) CTRL_C ) . toString ( ) , new ActionListener ( ) {
consoleReader . getKeys ( ) . bind ( new Character ( ( char ) CTRL_C ) . toString ( ) , new ActionListener ( ) {
@Override
@Override
public void actionPerformed ( ActionEvent e ) {
public void actionPerformed ( ActionEvent e ) {
try {
try {
consoleReader . getCursorBuffer ( ) . clear ( ) ; // clear current line
consoleReader . getCursorBuffer ( ) . clear ( ) ; // clear current line
telnet . getOutputStream ( ) . write ( CTRL_C ) ;
telnet . getOutputStream ( ) . write ( CTRL_C ) ;
telnet . getOutputStream ( ) . flush ( ) ;
telnet . getOutputStream ( ) . flush ( ) ;
} catch ( Exception e1 ) {
} catch ( Exception e1 ) {
e1 . printStackTrace ( ) ;
e1 . printStackTrace ( ) ;
}
}
}
}
} ) ;
} ) ;
// ctrl + d event call back
// ctrl + d event call back
consoleReader . getKeys ( ) . bind ( new Character ( KeyMap . CTRL_D ) . toString ( ) , new ActionListener ( ) {
consoleReader . getKeys ( ) . bind ( new Character ( KeyMap . CTRL_D ) . toString ( ) , eotEventCallback ) ;
@Override
public void actionPerformed ( ActionEvent e ) {
System . exit ( 0 ) ;
}
} ) ;
try {
try {
telnet . connect ( telnetConsole . getTargetIp ( ) , telnetConsole . getPort ( ) ) ;
telnet . connect ( telnetConsole . getTargetIp ( ) , telnetConsole . getPort ( ) ) ;
} catch ( IOException e ) {
} catch ( IOException e ) {
System . out . println ( "Connect to telnet server error: " + telnetConsole . getTargetIp ( ) + " "
System . out . println ( "Connect to telnet server error: " + telnetConsole . getTargetIp ( ) + " "
+ telnetConsole . getPort ( ) ) ;
+ telnetConsole . getPort ( ) ) ;
throw e ;
throw e ;
}
}
if ( cmds . isEmpty ( ) ) {
if ( cmds . isEmpty ( ) ) {
IOUtil . readWrite ( telnet . getInputStream ( ) , telnet . getOutputStream ( ) , System . in ,
IOUtil . readWrite ( telnet . getInputStream ( ) , telnet . getOutputStream ( ) , System . in ,
consoleReader . getOutput ( ) ) ;
consoleReader . getOutput ( ) ) ;
} else {
} else {
batchModeRun ( telnet , cmds ) ;
try {
telnet . disconnect ( ) ;
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 < String > commands )
private static int batchModeRun ( TelnetClient telnet , List < String > commands , final int executionTimeout )
throws IOException , InterruptedException {
throws IOException , InterruptedException {
if ( commands . size ( ) = = 0 ) {
if ( commands . size ( ) = = 0 ) {
return ;
return STATUS_OK ;
}
}
long startTime = System . currentTimeMillis ( ) ;
final InputStream inputStream = telnet . getInputStream ( ) ;
final InputStream inputStream = telnet . getInputStream ( ) ;
final OutputStream outputStream = telnet . getOutputStream ( ) ;
final OutputStream outputStream = telnet . getOutputStream ( ) ;
@ -305,14 +375,22 @@ public class TelnetConsole {
}
}
}
}
} ) ;
} ) ;
printResultThread . start ( ) ;
printResultThread . start ( ) ;
// send commands to arthas server
for ( String command : commands ) {
for ( String command : commands ) {
if ( command . trim ( ) . isEmpty ( ) ) {
if ( command . trim ( ) . isEmpty ( ) ) {
continue ;
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
// send command to server
outputStream . write ( ( command + " | plaintext\n" ) . getBytes ( ) ) ;
outputStream . write ( ( command + " | plaintext\n" ) . getBytes ( ) ) ;
outputStream . flush ( ) ;
outputStream . flush ( ) ;
@ -323,6 +401,8 @@ public class TelnetConsole {
outputStream . write ( "quit\n" . getBytes ( ) ) ;
outputStream . write ( "quit\n" . getBytes ( ) ) ;
outputStream . flush ( ) ;
outputStream . flush ( ) ;
System . out . println ( ) ;
System . out . println ( ) ;
return STATUS_OK ;
}
}
private static String usage ( CLI cli ) {
private static String usage ( CLI cli ) {
@ -349,6 +429,10 @@ public class TelnetConsole {
return batchFile ;
return batchFile ;
}
}
public int getExecutionTimeout ( ) {
return executionTimeout ;
}
public Integer getWidth ( ) {
public Integer getWidth ( ) {
return width ;
return width ;
}
}